@vellumai/assistant 0.8.8 → 0.8.9-dev.202606091926.ebb2d62

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 (490) hide show
  1. package/ARCHITECTURE.md +6 -6
  2. package/bun.lock +2 -2
  3. package/docs/activation-funnel-telemetry.md +310 -0
  4. package/examples/plugins/echo/README.md +61 -60
  5. package/examples/plugins/echo/hooks/post-tool-use.ts +18 -0
  6. package/examples/plugins/echo/hooks/stop.ts +16 -0
  7. package/examples/plugins/echo/hooks/user-prompt-submit.ts +18 -0
  8. package/examples/plugins/echo/package.json +1 -2
  9. package/examples/plugins/echo/src/emit.ts +19 -0
  10. package/node_modules/@vellumai/skill-host-contracts/src/skill-host.ts +7 -6
  11. package/openapi.yaml +273 -121
  12. package/package.json +2 -2
  13. package/scripts/sync-llm-catalog.ts +2 -0
  14. package/src/__tests__/activation-early-marking.test.ts +120 -0
  15. package/src/__tests__/agent-loop-callsite-precedence.test.ts +69 -14
  16. package/src/__tests__/agent-loop-exit-reason.test.ts +204 -144
  17. package/src/__tests__/agent-loop-mutable-latest-user-message.test.ts +50 -35
  18. package/src/__tests__/agent-loop-output-hooks.test.ts +357 -0
  19. package/src/__tests__/agent-loop-override-profile.test.ts +25 -6
  20. package/src/__tests__/agent-loop-provider-error-recording.test.ts +41 -21
  21. package/src/__tests__/agent-loop-thinking.test.ts +36 -20
  22. package/src/__tests__/agent-loop.test.ts +441 -96
  23. package/src/__tests__/agent-wake-disk-pressure-callsite.test.ts +14 -14
  24. package/src/__tests__/agent-wake-override-profile.test.ts +17 -21
  25. package/src/__tests__/anthropic-provider.test.ts +23 -8
  26. package/src/__tests__/app-builder-tool-scripts.test.ts +21 -0
  27. package/src/__tests__/app-control-flow.test.ts +1 -1
  28. package/src/__tests__/app-dir-path-guard.test.ts +1 -0
  29. package/src/__tests__/app-executors.test.ts +132 -0
  30. package/src/__tests__/approval-cascade.test.ts +6 -5
  31. package/src/__tests__/approval-routes-http.test.ts +4 -1
  32. package/src/__tests__/background-workers-disk-pressure.test.ts +1 -1
  33. package/src/__tests__/build-persisted-content.test.ts +75 -1
  34. package/src/__tests__/channel-approval-routes.test.ts +1 -1
  35. package/src/__tests__/channel-approvals.test.ts +1 -1
  36. package/src/__tests__/compaction-circuit.test.ts +258 -0
  37. package/src/__tests__/compaction-direct.test.ts +146 -0
  38. package/src/__tests__/compaction-events.test.ts +7 -7
  39. package/src/__tests__/compaction.benchmark.test.ts +1 -1
  40. package/src/__tests__/compactor-web-search-strip.test.ts +213 -0
  41. package/src/__tests__/config-managed-gemini-defaults.test.ts +2 -46
  42. package/src/__tests__/context-overflow-reducer.test.ts +265 -125
  43. package/src/__tests__/context-window-manager-compact-retry.test.ts +121 -15
  44. package/src/__tests__/conversation-abort-tool-results.test.ts +7 -5
  45. package/src/__tests__/conversation-agent-loop-disk-pressure.test.ts +7 -10
  46. package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +78 -119
  47. package/src/__tests__/conversation-agent-loop-overflow.test.ts +158 -234
  48. package/src/__tests__/conversation-agent-loop.test.ts +342 -623
  49. package/src/__tests__/conversation-clean-command.test.ts +5 -2
  50. package/src/__tests__/conversation-confirmation-signals.test.ts +6 -5
  51. package/src/__tests__/conversation-crud-inference-profile.test.ts +7 -9
  52. package/src/__tests__/conversation-error.test.ts +15 -1
  53. package/src/__tests__/conversation-history-web-search.test.ts +6 -1
  54. package/src/__tests__/conversation-media-retry.test.ts +1 -1
  55. package/src/__tests__/conversation-process-app-control-preactivation.test.ts +44 -4
  56. package/src/__tests__/conversation-process-callsite.test.ts +15 -15
  57. package/src/__tests__/conversation-provider-retry-repair.test.ts +66 -65
  58. package/src/__tests__/conversation-queue.test.ts +10 -10
  59. package/src/__tests__/conversation-runtime-assembly.test.ts +1059 -231
  60. package/src/__tests__/conversation-runtime-workspace.test.ts +115 -20
  61. package/src/__tests__/conversation-slash-queue.test.ts +7 -5
  62. package/src/__tests__/conversation-slash-unknown.test.ts +7 -5
  63. package/src/__tests__/conversation-speed-override.test.ts +11 -10
  64. package/src/__tests__/conversation-starter-routes.test.ts +14 -6
  65. package/src/__tests__/conversation-surfaces-activation-emit.test.ts +395 -0
  66. package/src/__tests__/conversation-surfaces-app-control.test.ts +48 -1
  67. package/src/__tests__/conversation-tool-setup-app-refresh.test.ts +73 -4
  68. package/src/__tests__/conversation-undo.test.ts +2 -2
  69. package/src/__tests__/conversation-workspace-cache-state.test.ts +24 -21
  70. package/src/__tests__/conversation-workspace-injection.test.ts +69 -7
  71. package/src/__tests__/conversation-workspace-tool-tracking.test.ts +15 -12
  72. package/src/__tests__/conversations-import-system-filter.test.ts +101 -0
  73. package/src/__tests__/corrected-target.test.ts +93 -0
  74. package/src/__tests__/credential-execution-feature-gates.test.ts +5 -4
  75. package/src/__tests__/credential-security-invariants.test.ts +1 -0
  76. package/src/__tests__/cu-unified-flow.test.ts +36 -0
  77. package/src/__tests__/db-acp-history.test.ts +101 -0
  78. package/src/__tests__/dynamic-page-surface.test.ts +31 -0
  79. package/src/__tests__/embedding-managed-proxy-selection.test.ts +6 -40
  80. package/src/__tests__/empty-response-hook.test.ts +1 -1
  81. package/src/__tests__/file-write-tool.test.ts +63 -0
  82. package/src/__tests__/gateway-only-guard.test.ts +17 -2
  83. package/src/__tests__/guardian-grant-minting.test.ts +1 -1
  84. package/src/__tests__/guardian-routing-invariants.test.ts +93 -4
  85. package/src/__tests__/handlers-user-message-approval-consumption.test.ts +1 -1
  86. package/src/__tests__/heartbeat-disk-pressure.test.ts +1 -0
  87. package/src/__tests__/heartbeat-service.test.ts +1 -0
  88. package/src/__tests__/history-repair-hook.test.ts +5 -1
  89. package/src/__tests__/host-app-control-proxy.test.ts +45 -0
  90. package/src/__tests__/host-app-control-routes.test.ts +1 -1
  91. package/src/__tests__/host-cu-routes-targeted.test.ts +3 -3
  92. package/src/__tests__/inference-profile-reaper.test.ts +62 -0
  93. package/src/__tests__/inference-profile-session-handler.test.ts +86 -0
  94. package/src/__tests__/injector-background-turn.test.ts +13 -23
  95. package/src/__tests__/injector-chain.test.ts +268 -44
  96. package/src/__tests__/injector-disk-pressure.test.ts +210 -52
  97. package/src/__tests__/injector-document-comments.test.ts +97 -114
  98. package/src/__tests__/injector-pkb-v2-silenced.test.ts +2 -2
  99. package/src/__tests__/injector-v3-suppression.test.ts +4 -4
  100. package/src/__tests__/list-messages-client-message-id.test.ts +91 -0
  101. package/src/__tests__/list-messages-hidden-metadata.test.ts +38 -0
  102. package/src/__tests__/llm-catalog-parity.test.ts +4 -0
  103. package/src/__tests__/llm-resolver.test.ts +73 -0
  104. package/src/__tests__/memory-retrieval-hook.test.ts +156 -38
  105. package/src/__tests__/memory-v2-static-injector.test.ts +86 -8
  106. package/src/__tests__/notification-decision-strategy.test.ts +3 -3
  107. package/src/__tests__/parallel-tool.benchmark.test.ts +35 -8
  108. package/src/__tests__/persist-unsendable-image-downscale.test.ts +145 -0
  109. package/src/__tests__/persist-unsendable-image.test.ts +97 -1
  110. package/src/__tests__/persistence-secret-redaction.test.ts +78 -0
  111. package/src/__tests__/plugin-api-shim.test.ts +6 -9
  112. package/src/__tests__/plugin-bootstrap.test.ts +91 -81
  113. package/src/__tests__/plugin-registry.test.ts +3 -49
  114. package/src/__tests__/plugin-skill-contribution.test.ts +15 -14
  115. package/src/__tests__/plugin-tool-contribution.test.ts +7 -4
  116. package/src/__tests__/plugin-types.test.ts +0 -70
  117. package/src/__tests__/post-turn-tool-result-truncation.test.ts +69 -0
  118. package/src/__tests__/pre-model-call-sanitize.test.ts +109 -0
  119. package/src/__tests__/published-app-updater.test.ts +138 -0
  120. package/src/__tests__/reaction-persistence.test.ts +1 -1
  121. package/src/__tests__/retry-thinking-adaptive-only.test.ts +245 -0
  122. package/src/__tests__/send-endpoint-busy.test.ts +4 -1
  123. package/src/__tests__/server-history-render.test.ts +39 -0
  124. package/src/__tests__/skill-feature-flags-integration.test.ts +31 -0
  125. package/src/__tests__/steer-tool-repair.test.ts +1 -1
  126. package/src/__tests__/subagent-call-site-routing.test.ts +1 -1
  127. package/src/__tests__/subagent-detail.test.ts +25 -7
  128. package/src/__tests__/subagent-fork-notifications.test.ts +1 -3
  129. package/src/__tests__/subagent-fork-spawn.test.ts +1 -1
  130. package/src/__tests__/subagent-manager-notify.test.ts +1 -3
  131. package/src/__tests__/subagent-notify-parent.test.ts +1 -3
  132. package/src/__tests__/subagent-spawn-tool-fork.test.ts +1 -1
  133. package/src/__tests__/title-generate-hook.test.ts +5 -1
  134. package/src/__tests__/tool-error-hook.test.ts +1 -1
  135. package/src/__tests__/tool-result-truncate-hook.test.ts +1 -1
  136. package/src/__tests__/tool-start-timestamp.test.ts +218 -0
  137. package/src/__tests__/user-plugin-loader.test.ts +54 -286
  138. package/src/__tests__/web-fetch.test.ts +45 -0
  139. package/src/acp/__tests__/agent-process.test.ts +161 -0
  140. package/src/acp/__tests__/client-handler.test.ts +40 -0
  141. package/src/acp/__tests__/helpers/acp-config-stub.ts +0 -2
  142. package/src/acp/__tests__/helpers/acp-history-db.ts +82 -0
  143. package/src/acp/__tests__/helpers/exec-file-stub.ts +106 -0
  144. package/src/acp/__tests__/prepare-agent-env.test.ts +97 -0
  145. package/src/acp/__tests__/session-manager-persistence.test.ts +95 -28
  146. package/src/acp/__tests__/session-manager-resume.test.ts +888 -0
  147. package/src/acp/agent-process.ts +61 -1
  148. package/src/acp/auto-install.test.ts +280 -0
  149. package/src/acp/auto-install.ts +232 -0
  150. package/src/acp/client-handler.ts +31 -0
  151. package/src/acp/prepare-agent-env.ts +80 -27
  152. package/src/acp/resolve-agent.test.ts +188 -28
  153. package/src/acp/resolve-agent.ts +119 -42
  154. package/src/acp/resume-hint.ts +23 -0
  155. package/src/acp/session-manager.ts +507 -73
  156. package/src/agent/compaction-circuit.ts +60 -102
  157. package/src/agent/loop.ts +403 -251
  158. package/src/api/events/tool-result.ts +9 -0
  159. package/src/api/events/tool-use-start.ts +7 -0
  160. package/src/api/responses/conversation-message.ts +72 -13
  161. package/src/api/responses/memory-v3-selection-log.ts +19 -10
  162. package/src/approvals/guardian-request-resolvers.ts +27 -1
  163. package/src/background-wake/next-wake.ts +1 -0
  164. package/src/cli/commands/__tests__/memory-v3.test.ts +191 -210
  165. package/src/cli/commands/channel-verification-sessions.ts +6 -6
  166. package/src/cli/commands/db/__tests__/repair.test.ts +3 -1
  167. package/src/cli/commands/memory-v3.ts +57 -199
  168. package/src/cli/commands/plugins.ts +43 -37
  169. package/src/cli/lib/__tests__/install-from-github.test.ts +790 -111
  170. package/src/cli/lib/__tests__/plugin-catalog-cache.test.ts +196 -0
  171. package/src/cli/lib/__tests__/plugin-details.test.ts +381 -0
  172. package/src/cli/lib/__tests__/plugin-marketplace.test.ts +270 -0
  173. package/src/cli/lib/__tests__/search-plugins.test.ts +233 -32
  174. package/src/cli/lib/install-from-github.ts +797 -60
  175. package/src/cli/lib/plugin-catalog-cache.ts +84 -0
  176. package/src/cli/lib/plugin-details.ts +416 -0
  177. package/src/cli/lib/plugin-marketplace.ts +215 -0
  178. package/src/cli/lib/search-plugins.ts +201 -29
  179. package/src/config/__tests__/feature-flag-registry-guard.test.ts +2 -2
  180. package/src/config/acp-defaults.test.ts +10 -0
  181. package/src/config/acp-defaults.ts +9 -3
  182. package/src/config/acp-schema.ts +1 -7
  183. package/src/config/bundled-skills/acp/SKILL.md +80 -41
  184. package/src/config/bundled-skills/acp/TOOLS.json +4 -4
  185. package/src/config/bundled-skills/app-builder/SKILL.md +224 -381
  186. package/src/config/bundled-skills/app-builder/TOOLS.json +72 -2
  187. package/src/config/bundled-skills/app-builder/references/DESIGN_SYSTEM.md +48 -0
  188. package/src/config/bundled-skills/app-builder/references/RESPONSIVE.md +57 -0
  189. package/src/config/bundled-skills/app-builder/references/SLIDES.md +38 -0
  190. package/src/config/bundled-skills/app-builder/tools/app-list.ts +62 -0
  191. package/src/config/bundled-skills/app-builder/tools/app-update.ts +18 -0
  192. package/src/config/bundled-skills/document-editor/SKILL.md +28 -23
  193. package/src/config/bundled-skills/document-editor/TOOLS.json +1 -1
  194. package/src/config/bundled-tool-registry.ts +4 -0
  195. package/src/config/call-site-defaults.ts +0 -3
  196. package/src/config/feature-flag-registry.json +10 -56
  197. package/src/config/llm-resolver.ts +39 -7
  198. package/src/config/loader.ts +5 -22
  199. package/src/config/schemas/__tests__/memory-v3.test.ts +25 -9
  200. package/src/config/schemas/call-site-catalog.ts +0 -21
  201. package/src/config/schemas/heartbeat.ts +9 -0
  202. package/src/config/schemas/llm.ts +17 -3
  203. package/src/config/schemas/memory-v3.ts +58 -8
  204. package/src/config/seed-inference-profiles.ts +18 -0
  205. package/src/context/compactor.ts +22 -4
  206. package/src/context/post-turn-tool-result-truncation.ts +39 -1
  207. package/src/context/strip-injections.ts +8 -2
  208. package/src/daemon/conversation-agent-loop-handlers.ts +120 -42
  209. package/src/daemon/conversation-agent-loop.ts +308 -1297
  210. package/src/daemon/conversation-error.ts +31 -4
  211. package/src/daemon/conversation-history.ts +1 -1
  212. package/src/daemon/conversation-lifecycle.ts +11 -255
  213. package/src/daemon/conversation-media-retry.ts +19 -6
  214. package/src/daemon/conversation-messaging.ts +17 -0
  215. package/src/daemon/conversation-process.ts +22 -141
  216. package/src/daemon/conversation-queue-manager.ts +8 -0
  217. package/src/daemon/conversation-registry.ts +159 -0
  218. package/src/daemon/conversation-runtime-assembly.ts +361 -393
  219. package/src/daemon/conversation-store.ts +9 -90
  220. package/src/daemon/conversation-surfaces.ts +165 -11
  221. package/src/daemon/conversation-workspace.ts +17 -0
  222. package/src/daemon/conversation.ts +444 -62
  223. package/src/daemon/disk-pressure-policy.ts +0 -1
  224. package/src/daemon/external-plugins-bootstrap.ts +36 -46
  225. package/src/daemon/handlers/config-channels.ts +11 -2
  226. package/src/daemon/handlers/conversations.ts +3 -1
  227. package/src/daemon/handlers/shared.ts +9 -1
  228. package/src/daemon/handlers/skills.ts +4 -1
  229. package/src/daemon/host-app-control-proxy.ts +72 -57
  230. package/src/daemon/host-proxy-preactivation.ts +1 -3
  231. package/src/daemon/lifecycle.ts +21 -7
  232. package/src/daemon/persist-unsendable-image.ts +62 -25
  233. package/src/daemon/process-message.ts +1 -1
  234. package/src/daemon/server.ts +2 -0
  235. package/src/daemon/tool-side-effects.ts +15 -0
  236. package/src/daemon/wake-conversation-ops.ts +269 -0
  237. package/src/embedded/plugin-api.ts +2 -2
  238. package/src/export/__tests__/transcript-formatter.test.ts +5 -0
  239. package/src/heartbeat/__tests__/heartbeat-service.test.ts +3 -0
  240. package/src/heartbeat/heartbeat-run-store.ts +23 -1
  241. package/src/heartbeat/heartbeat-service.ts +26 -0
  242. package/src/ipc/__tests__/browser-ipc.test.ts +1 -1
  243. package/src/ipc/__tests__/ui-request-route.test.ts +3 -3
  244. package/src/ipc/skill-routes/__tests__/memory.test.ts +15 -0
  245. package/src/ipc/skill-routes/memory.ts +4 -2
  246. package/src/memory/__tests__/activation-session-store.test.ts +41 -0
  247. package/src/memory/__tests__/jobs-worker-v2-schedule.test.ts +87 -0
  248. package/src/memory/__tests__/onboarding-events-store.test.ts +80 -0
  249. package/src/memory/activation-session-store.ts +43 -0
  250. package/src/memory/conversation-crud.ts +29 -19
  251. package/src/memory/conversation-starter-checkpoints.ts +1 -0
  252. package/src/memory/db-init.ts +6 -0
  253. package/src/memory/embedding-backend.ts +5 -22
  254. package/src/memory/job-handlers/conversation-starters.ts +13 -2
  255. package/src/memory/jobs/__tests__/embed-concept-page.test.ts +5 -4
  256. package/src/memory/jobs-worker.ts +25 -1
  257. package/src/memory/migrations/272-acp-session-history-cwd.ts +36 -0
  258. package/src/memory/migrations/273-onboarding-events-funnel-columns.ts +46 -0
  259. package/src/memory/migrations/274-create-activation-sessions.ts +15 -0
  260. package/src/memory/migrations/index.ts +3 -0
  261. package/src/memory/onboarding-events-store.ts +66 -18
  262. package/src/memory/schema/acp.ts +4 -0
  263. package/src/memory/schema/infrastructure.ts +13 -0
  264. package/src/memory/v2/__tests__/consolidation-job.test.ts +4 -97
  265. package/src/memory/v2/__tests__/page-store.test.ts +22 -0
  266. package/src/memory/v2/consolidation-job.ts +2 -63
  267. package/src/memory/v2/types.ts +5 -0
  268. package/src/messaging/providers/telegram-bot/api.ts +14 -5
  269. package/src/notifications/__tests__/copy-composer.test.ts +244 -0
  270. package/src/notifications/access-request-copy.ts +298 -0
  271. package/src/notifications/adapters/slack.ts +3 -3
  272. package/src/notifications/adapters/telegram.ts +9 -2
  273. package/src/notifications/copy-composer.ts +49 -267
  274. package/src/notifications/decision-engine.ts +16 -35
  275. package/src/notifications/home-feed-side-effect.ts +1 -6
  276. package/src/plugin-api/constants.ts +4 -0
  277. package/src/plugin-api/index.ts +40 -11
  278. package/src/plugin-api/types.ts +108 -0
  279. package/src/plugins/defaults/compaction/compact.ts +106 -0
  280. package/src/{daemon → plugins/defaults/compaction}/context-overflow-reducer.ts +250 -42
  281. package/src/plugins/defaults/compaction/corrected-target.ts +53 -0
  282. package/src/plugins/defaults/compaction/manager-store.ts +57 -0
  283. package/src/plugins/defaults/compaction/package.json +1 -2
  284. package/src/{context → plugins/defaults/compaction}/window-manager.ts +95 -25
  285. package/src/plugins/defaults/empty-response/package.json +0 -1
  286. package/src/plugins/defaults/history-repair/package.json +0 -1
  287. package/src/plugins/defaults/index.ts +156 -26
  288. package/src/plugins/defaults/memory-retrieval/hooks/post-compact.ts +100 -52
  289. package/src/plugins/defaults/memory-retrieval/hooks/user-prompt-submit.ts +285 -0
  290. package/src/plugins/defaults/memory-retrieval/injector-chain.ts +2 -2
  291. package/src/plugins/defaults/{injectors/register.ts → memory-retrieval/injectors.ts} +148 -73
  292. package/src/plugins/defaults/memory-retrieval/package.json +14 -0
  293. package/src/plugins/defaults/memory-retrieval/unified-turn-context.ts +223 -0
  294. package/src/plugins/defaults/memory-v3-shadow/__tests__/capabilities.test.ts +71 -0
  295. package/src/plugins/defaults/memory-v3-shadow/__tests__/dense.test.ts +181 -0
  296. package/src/plugins/defaults/memory-v3-shadow/__tests__/edge.test.ts +247 -0
  297. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/live-integration.test.ts +147 -134
  298. package/src/plugins/defaults/memory-v3-shadow/__tests__/maintain-job.test.ts +456 -0
  299. package/src/plugins/defaults/memory-v3-shadow/__tests__/orchestrate.test.ts +662 -0
  300. package/src/plugins/defaults/memory-v3-shadow/__tests__/pool-select.test.ts +306 -0
  301. package/src/plugins/defaults/memory-v3-shadow/__tests__/render-injection.test.ts +91 -0
  302. package/src/plugins/defaults/memory-v3-shadow/__tests__/section-dense-store.test.ts +402 -0
  303. package/src/plugins/defaults/memory-v3-shadow/__tests__/section-needle.test.ts +135 -0
  304. package/src/plugins/defaults/memory-v3-shadow/__tests__/sections.test.ts +125 -0
  305. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/selection-log-store.test.ts +74 -19
  306. package/src/plugins/defaults/memory-v3-shadow/__tests__/shadow-integration.test.ts +446 -0
  307. package/src/plugins/defaults/memory-v3-shadow/__tests__/shadow-plugin.test.ts +592 -0
  308. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/types.test.ts +2 -24
  309. package/src/plugins/defaults/memory-v3-shadow/capabilities.ts +91 -0
  310. package/src/plugins/defaults/memory-v3-shadow/dense.ts +97 -0
  311. package/src/plugins/defaults/memory-v3-shadow/edge.ts +252 -0
  312. package/src/plugins/defaults/memory-v3-shadow/hooks/post-compact.ts +14 -0
  313. package/src/plugins/defaults/memory-v3-shadow/hooks/user-prompt-submit.ts +19 -0
  314. package/src/plugins/defaults/memory-v3-shadow/injector.ts +76 -0
  315. package/src/plugins/defaults/memory-v3-shadow/maintain-job.ts +555 -0
  316. package/src/plugins/defaults/memory-v3-shadow/orchestrate.ts +266 -0
  317. package/src/plugins/defaults/memory-v3-shadow/package.json +14 -0
  318. package/src/plugins/defaults/memory-v3-shadow/page-content.ts +71 -0
  319. package/src/plugins/defaults/memory-v3-shadow/pool-select.ts +204 -0
  320. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/render-injection.ts +16 -8
  321. package/src/plugins/defaults/memory-v3-shadow/section-dense-store.ts +236 -0
  322. package/src/plugins/defaults/memory-v3-shadow/section-needle.ts +200 -0
  323. package/src/plugins/defaults/memory-v3-shadow/sections.ts +115 -0
  324. package/src/plugins/defaults/memory-v3-shadow/selection-log-store.ts +156 -0
  325. package/src/plugins/defaults/memory-v3-shadow/shadow-plugin.ts +382 -0
  326. package/src/plugins/defaults/memory-v3-shadow/types.ts +92 -0
  327. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/working-set.ts +4 -1
  328. package/src/plugins/defaults/title-generate/package.json +0 -1
  329. package/src/plugins/defaults/tool-error/package.json +0 -1
  330. package/src/plugins/defaults/tool-result-truncate/package.json +0 -1
  331. package/src/plugins/external-api.ts +12 -2
  332. package/src/plugins/pipeline.ts +6 -293
  333. package/src/plugins/registry.ts +9 -37
  334. package/src/plugins/types.ts +76 -381
  335. package/src/plugins/user-loader.ts +30 -127
  336. package/src/prompts/__tests__/system-prompt.test.ts +6 -0
  337. package/src/prompts/system-prompt.ts +61 -10
  338. package/src/prompts/templates/BOOTSTRAP-ACTIVATION-RAIL.md +71 -4
  339. package/src/providers/anthropic/client.ts +9 -10
  340. package/src/providers/inference/kimi-cjk-token-ids.ts +493 -0
  341. package/src/providers/inference/logit-bias.ts +55 -0
  342. package/src/providers/model-catalog.ts +57 -0
  343. package/src/providers/openai/__tests__/vision-not-supported.test.ts +75 -0
  344. package/src/providers/openai/chat-completions-provider.ts +44 -1
  345. package/src/providers/retry.ts +57 -7
  346. package/src/providers/types.ts +6 -0
  347. package/src/runtime/__tests__/agent-wake.test.ts +555 -691
  348. package/src/runtime/__tests__/interactive-ui.test.ts +1 -1
  349. package/src/runtime/agent-wake.ts +108 -209
  350. package/src/runtime/assistant-event-hub.ts +1 -1
  351. package/src/runtime/channel-approvals.ts +1 -1
  352. package/src/runtime/interactive-ui.ts +1 -1
  353. package/src/runtime/routes/__tests__/acp-routes.test.ts +466 -55
  354. package/src/runtime/routes/__tests__/conversation-list-routes.test.ts +1 -1
  355. package/src/runtime/routes/__tests__/plugins-routes.test.ts +423 -73
  356. package/src/runtime/routes/__tests__/stt-routes.test.ts +112 -0
  357. package/src/runtime/routes/__tests__/surface-action-routes.test.ts +5 -4
  358. package/src/runtime/routes/__tests__/surface-content-routes.test.ts +4 -1
  359. package/src/runtime/routes/acp-routes.test.ts +185 -32
  360. package/src/runtime/routes/acp-routes.ts +328 -30
  361. package/src/runtime/routes/app-management-routes.ts +3 -0
  362. package/src/runtime/routes/approval-routes.ts +1 -1
  363. package/src/runtime/routes/browser-routes.ts +1 -1
  364. package/src/runtime/routes/browser-tabs-routes.ts +6 -10
  365. package/src/runtime/routes/channel-verification-routes.ts +14 -5
  366. package/src/runtime/routes/conversation-cli-routes.ts +1 -1
  367. package/src/runtime/routes/conversation-list-routes.ts +1 -1
  368. package/src/runtime/routes/conversation-query-routes.ts +1 -1
  369. package/src/runtime/routes/conversation-routes.ts +30 -2
  370. package/src/runtime/routes/conversation-starter-routes.ts +13 -7
  371. package/src/runtime/routes/conversations-import-routes.ts +24 -7
  372. package/src/runtime/routes/host-app-control-routes.ts +1 -1
  373. package/src/runtime/routes/host-cu-routes.ts +1 -1
  374. package/src/runtime/routes/identity-routes.ts +18 -3
  375. package/src/runtime/routes/inbound-message-handler.ts +1 -1
  376. package/src/runtime/routes/inbound-stages/acl-enforcement.ts +33 -34
  377. package/src/runtime/routes/inference-profile-session-handler.ts +11 -0
  378. package/src/runtime/routes/inference-profile-session-reaper.ts +6 -0
  379. package/src/runtime/routes/memory-v3-routes.ts +65 -305
  380. package/src/runtime/routes/playground/__tests__/force-compact.test.ts +1 -1
  381. package/src/runtime/routes/playground/helpers.ts +1 -1
  382. package/src/runtime/routes/plugins-routes.ts +337 -35
  383. package/src/runtime/routes/stt-routes.ts +45 -12
  384. package/src/runtime/routes/surface-conversation-resolver.ts +4 -3
  385. package/src/runtime/routes/work-items-routes.ts +2 -4
  386. package/src/runtime/routes/workspace-routes.ts +50 -15
  387. package/src/runtime/services/conversation-serializer.ts +1 -1
  388. package/src/runtime/verification-outbound-actions.ts +147 -2
  389. package/src/runtime/verification-templates.ts +29 -3
  390. package/src/services/published-app-updater.ts +30 -8
  391. package/src/signals/cancel.ts +2 -4
  392. package/src/subagent/manager.ts +21 -5
  393. package/src/telemetry/__tests__/activation-funnel.test.ts +95 -0
  394. package/src/telemetry/activation-funnel.ts +167 -0
  395. package/src/telemetry/types.ts +13 -0
  396. package/src/telemetry/usage-telemetry-reporter.test.ts +154 -0
  397. package/src/telemetry/usage-telemetry-reporter.ts +26 -1
  398. package/src/tools/acp/context.ts +20 -0
  399. package/src/tools/acp/list-agents.test.ts +9 -19
  400. package/src/tools/acp/list-agents.ts +3 -15
  401. package/src/tools/acp/spawn.test.ts +173 -202
  402. package/src/tools/acp/spawn.ts +37 -172
  403. package/src/tools/acp/steer.test.ts +105 -8
  404. package/src/tools/acp/steer.ts +48 -17
  405. package/src/tools/apps/executors.ts +166 -50
  406. package/src/tools/browser/browser-execution.ts +12 -2
  407. package/src/tools/filesystem/write.ts +34 -0
  408. package/src/tools/network/web-fetch.ts +65 -24
  409. package/src/tools/skills/load.ts +1 -1
  410. package/src/tools/subagent/spawn.ts +2 -4
  411. package/src/tools/ui-surface/definitions.ts +32 -5
  412. package/src/workspace/migrations/051-seed-conversation-summarization-callsite.ts +4 -5
  413. package/src/workspace/migrations/097-enable-adaptive-thinking-managed-profiles.ts +69 -45
  414. package/docs/plugins.md +0 -832
  415. package/examples/plugins/echo/register.ts +0 -143
  416. package/src/__tests__/circuit-breaker-pipeline.test.ts +0 -405
  417. package/src/__tests__/compaction-pipeline.test.ts +0 -210
  418. package/src/__tests__/compaction-timeout-recovery.test.ts +0 -251
  419. package/src/__tests__/overflow-reduce-pipeline.test.ts +0 -667
  420. package/src/__tests__/pipeline-runner.test.ts +0 -554
  421. package/src/daemon/wake-target-adapter.ts +0 -253
  422. package/src/memory/v3/__tests__/assign.test.ts +0 -242
  423. package/src/memory/v3/__tests__/capabilities.test.ts +0 -118
  424. package/src/memory/v3/__tests__/core.test.ts +0 -39
  425. package/src/memory/v3/__tests__/health.test.ts +0 -219
  426. package/src/memory/v3/__tests__/maintain-job.test.ts +0 -288
  427. package/src/memory/v3/__tests__/needle.test.ts +0 -107
  428. package/src/memory/v3/__tests__/orchestrate.test.ts +0 -436
  429. package/src/memory/v3/__tests__/provider-blocks.test.ts +0 -13
  430. package/src/memory/v3/__tests__/reconcile.test.ts +0 -274
  431. package/src/memory/v3/__tests__/render-injection.test.ts +0 -61
  432. package/src/memory/v3/__tests__/router.test.ts +0 -332
  433. package/src/memory/v3/__tests__/selector.test.ts +0 -470
  434. package/src/memory/v3/__tests__/shadow-plugin.test.ts +0 -432
  435. package/src/memory/v3/__tests__/snapshot.test.ts +0 -168
  436. package/src/memory/v3/__tests__/tree.test.ts +0 -192
  437. package/src/memory/v3/assign.ts +0 -268
  438. package/src/memory/v3/capabilities.ts +0 -124
  439. package/src/memory/v3/core.ts +0 -26
  440. package/src/memory/v3/health.ts +0 -0
  441. package/src/memory/v3/maintain-job.ts +0 -314
  442. package/src/memory/v3/needle.ts +0 -115
  443. package/src/memory/v3/orchestrate.ts +0 -126
  444. package/src/memory/v3/page-content.ts +0 -34
  445. package/src/memory/v3/provider-blocks.ts +0 -26
  446. package/src/memory/v3/reconcile.ts +0 -523
  447. package/src/memory/v3/router.ts +0 -190
  448. package/src/memory/v3/selection-log-store.ts +0 -84
  449. package/src/memory/v3/selector.ts +0 -226
  450. package/src/memory/v3/shadow-plugin.ts +0 -411
  451. package/src/memory/v3/snapshot.ts +0 -209
  452. package/src/memory/v3/tree.ts +0 -174
  453. package/src/memory/v3/types.ts +0 -59
  454. package/src/notifications/__tests__/emit-signal-home-feed.test.ts +0 -187
  455. package/src/plugins/defaults/circuit-breaker/middlewares/circuitBreaker.ts +0 -93
  456. package/src/plugins/defaults/circuit-breaker/package.json +0 -15
  457. package/src/plugins/defaults/circuit-breaker/register.ts +0 -39
  458. package/src/plugins/defaults/compaction/middlewares/compaction.ts +0 -25
  459. package/src/plugins/defaults/compaction/register.ts +0 -35
  460. package/src/plugins/defaults/compaction/terminal.ts +0 -73
  461. package/src/plugins/defaults/empty-response/register.ts +0 -23
  462. package/src/plugins/defaults/history-repair/register.ts +0 -24
  463. package/src/plugins/defaults/memory-retrieval/hooks/user-prompt-submit-temp.ts +0 -216
  464. package/src/plugins/defaults/overflow-reduce/middlewares/overflowReduce.ts +0 -126
  465. package/src/plugins/defaults/overflow-reduce/package.json +0 -15
  466. package/src/plugins/defaults/overflow-reduce/register.ts +0 -42
  467. package/src/plugins/defaults/title-generate/register.ts +0 -35
  468. package/src/plugins/defaults/tool-error/register.ts +0 -23
  469. package/src/plugins/defaults/tool-result-truncate/register.ts +0 -24
  470. package/src/proactive-artifact/aux-message-injector.ts +0 -97
  471. package/src/proactive-artifact/decision.test.ts +0 -226
  472. package/src/proactive-artifact/decision.ts +0 -165
  473. package/src/proactive-artifact/index.ts +0 -7
  474. package/src/proactive-artifact/job.test.ts +0 -962
  475. package/src/proactive-artifact/job.ts +0 -372
  476. package/src/proactive-artifact/message-copy.ts +0 -58
  477. package/src/proactive-artifact/trigger-state.test.ts +0 -286
  478. package/src/proactive-artifact/trigger-state.ts +0 -123
  479. package/src/util/map-limit.ts +0 -27
  480. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/fixtures/eval-turns.json +0 -0
  481. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/fixtures/live-turns.json +0 -0
  482. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/working-set-eviction.test.ts +0 -0
  483. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/working-set-skeleton.test.ts +0 -0
  484. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/data/README.md +0 -0
  485. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/data/assignments.json +0 -0
  486. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/data/core.json +0 -0
  487. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/data/leaves/domain-a/topic-x.md +0 -0
  488. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/data/leaves/domain-a/topic-y.md +0 -0
  489. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/data/leaves/domain-b/topic-z.md +0 -0
  490. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/llm-retry.ts +0 -0
@@ -1,219 +0,0 @@
1
- import { describe, expect, test } from "bun:test";
2
-
3
- import { computeV3Health, renderV3Health } from "../health.js";
4
- import type { LeafNode, LeafPath, LeafTree, Slug } from "../types.js";
5
-
6
- /** Build an in-memory leaf node with the given members. */
7
- function leaf(path: LeafPath, members: Slug[]): LeafNode {
8
- return {
9
- path,
10
- frontmatter: { path, in_core: false },
11
- description: "",
12
- members: [...members],
13
- domain: path.split("/")[0],
14
- };
15
- }
16
-
17
- /**
18
- * Build a {@link LeafTree} from `{ leafPath: members[] }`. `byPage` is the
19
- * inverted membership map, matching what `loadLeafTree` produces.
20
- */
21
- function tree(spec: Record<LeafPath, Slug[]>): LeafTree {
22
- const leaves = new Map<LeafPath, LeafNode>();
23
- const byPage = new Map<Slug, LeafPath[]>();
24
- for (const [path, members] of Object.entries(spec)) {
25
- leaves.set(path, leaf(path, members));
26
- for (const slug of members) {
27
- const existing = byPage.get(slug);
28
- if (existing) existing.push(path);
29
- else byPage.set(slug, [path]);
30
- }
31
- }
32
- return { leaves, byPage };
33
- }
34
-
35
- describe("computeV3Health", () => {
36
- test("flags unassigned slugs (in neither byPage nor a leaf)", () => {
37
- const t = tree({ "domain-a/topic-x": ["page-a"] });
38
- const report = computeV3Health({
39
- tree: t,
40
- allSlugs: ["page-a", "page-b", "page-c"],
41
- });
42
- expect(report.unassigned).toEqual(["page-b", "page-c"]);
43
- });
44
-
45
- test("excludes synthetic capability slugs from unassigned / novel clusters", () => {
46
- const t = tree({ "domain-a/topic-x": ["page-a"] });
47
- const report = computeV3Health({
48
- tree: t,
49
- allSlugs: ["page-a", "page-b", "cli-commands/example", "skills/example"],
50
- });
51
- // Capability slugs are handled by the always-on capabilities leaf (injected
52
- // into the live lane tree, absent here), not the persisted tree — so they must
53
- // not be reported as unassigned or grouped into novel clusters. page-b is the
54
- // one real unassigned concept page.
55
- expect(report.unassigned).toEqual(["page-b"]);
56
- expect(report.novelClusters).toEqual([
57
- { prefix: "page-b", slugs: ["page-b"], count: 1 },
58
- ]);
59
- });
60
-
61
- test("flags dangling page refs pointing at missing leaves", () => {
62
- const t = tree({ "domain-a/topic-x": ["page-a"] });
63
- const report = computeV3Health({
64
- tree: t,
65
- allSlugs: ["page-a", "page-b"],
66
- pageRefs: new Map([
67
- ["page-a", ["domain-a/topic-x"]],
68
- ["page-b", ["domain-a/topic-missing"]],
69
- ]),
70
- });
71
- expect(report.danglingRefs).toEqual([
72
- { source: "page-b", leaf: "domain-a/topic-missing" },
73
- ]);
74
- });
75
-
76
- test("flags dangling core refs", () => {
77
- const t = tree({ "domain-a/topic-x": ["page-a"] });
78
- const report = computeV3Health({
79
- tree: t,
80
- allSlugs: ["page-a"],
81
- core: new Set(["domain-a/topic-x", "domain-z/gone"]),
82
- });
83
- expect(report.danglingRefs).toEqual([
84
- { source: "core", leaf: "domain-z/gone" },
85
- ]);
86
- });
87
-
88
- test("groups unassigned slugs into novel clusters by 2-level prefix", () => {
89
- const t = tree({ "domain-a/topic-x": ["assigned"] });
90
- const report = computeV3Health({
91
- tree: t,
92
- allSlugs: [
93
- "assigned",
94
- "area-a/topic-x/one",
95
- "area-a/topic-x/two",
96
- "area-b/topic-y",
97
- ],
98
- });
99
- expect(report.novelClusters).toEqual([
100
- {
101
- prefix: "area-a/topic-x",
102
- slugs: ["area-a/topic-x/one", "area-a/topic-x/two"],
103
- count: 2,
104
- },
105
- { prefix: "area-b/topic-y", slugs: ["area-b/topic-y"], count: 1 },
106
- ]);
107
- });
108
-
109
- test("flags oversized leaves above 3x the median member count", () => {
110
- // Medians: counts [1,1,1,12] -> sorted median = (1+1)/2 = 1; threshold 3.
111
- const t = tree({
112
- "domain-a/small-1": ["s1"],
113
- "domain-a/small-2": ["s2"],
114
- "domain-a/small-3": ["s3"],
115
- "domain-a/big": Array.from({ length: 12 }, (_, i) => `b${i}`),
116
- });
117
- const report = computeV3Health({ tree: t, allSlugs: [] });
118
- expect(report.oversizedLeaves).toEqual([
119
- { leaf: "domain-a/big", members: 12 },
120
- ]);
121
- });
122
-
123
- test("flags tiny leaves with 0-1 members", () => {
124
- const t = tree({
125
- "domain-a/empty": [],
126
- "domain-a/single": ["s1"],
127
- "domain-a/full": ["a", "b", "c"],
128
- });
129
- const report = computeV3Health({ tree: t, allSlugs: [] });
130
- expect(report.tinyLeaves).toEqual([
131
- { leaf: "domain-a/empty", members: 0 },
132
- { leaf: "domain-a/single", members: 1 },
133
- ]);
134
- });
135
-
136
- test("staleLabels is deferred (always empty)", () => {
137
- const t = tree({ "domain-a/topic-x": ["page-a"] });
138
- const report = computeV3Health({ tree: t, allSlugs: ["page-a"] });
139
- expect(report.staleLabels).toEqual([]);
140
- });
141
-
142
- test("passes lastMaintain through", () => {
143
- const t = tree({ "domain-a/topic-x": ["page-a"] });
144
- const report = computeV3Health({
145
- tree: t,
146
- allSlugs: ["page-a"],
147
- lastMaintain: { at: 12345, ok: true },
148
- });
149
- expect(report.lastMaintain).toEqual({ at: 12345, ok: true });
150
- });
151
-
152
- test("does not mutate inputs", () => {
153
- const t = tree({ "domain-a/topic-x": ["page-a", "page-b"] });
154
- const allSlugs = ["page-a", "page-c"];
155
- computeV3Health({ tree: t, allSlugs });
156
- expect(allSlugs).toEqual(["page-a", "page-c"]);
157
- expect(t.leaves.get("domain-a/topic-x")?.members).toEqual([
158
- "page-a",
159
- "page-b",
160
- ]);
161
- });
162
- });
163
-
164
- describe("renderV3Health", () => {
165
- test("returns empty string when all-green", () => {
166
- // A perfectly balanced tree: every leaf has the median count, no
167
- // unassigned, no dangling, no clusters, lastMaintain ok.
168
- const t = tree({
169
- "domain-a/topic-x": ["page-a", "page-b"],
170
- "domain-a/topic-y": ["page-c", "page-d"],
171
- });
172
- const report = computeV3Health({
173
- tree: t,
174
- allSlugs: ["page-a", "page-b", "page-c", "page-d"],
175
- lastMaintain: { at: 1, ok: true },
176
- });
177
- expect(report.unassigned).toEqual([]);
178
- expect(report.danglingRefs).toEqual([]);
179
- expect(report.novelClusters).toEqual([]);
180
- expect(report.oversizedLeaves).toEqual([]);
181
- expect(report.tinyLeaves).toEqual([]);
182
- expect(renderV3Health(report)).toBe("");
183
- });
184
-
185
- test("returns empty string when report is entirely empty and no maintain", () => {
186
- const report = computeV3Health({
187
- tree: { leaves: new Map(), byPage: new Map() },
188
- allSlugs: [],
189
- });
190
- expect(renderV3Health(report)).toBe("");
191
- });
192
-
193
- test("renders a non-empty block when any signal fires", () => {
194
- const t = tree({ "domain-a/topic-x": ["page-a"] });
195
- const report = computeV3Health({
196
- tree: t,
197
- allSlugs: ["page-a", "page-b"],
198
- });
199
- const rendered = renderV3Health(report);
200
- expect(rendered).not.toBe("");
201
- expect(rendered).toContain("memory-v3 health:");
202
- expect(rendered).toContain("unassigned");
203
- expect(rendered).toContain("page-b");
204
- });
205
-
206
- test("renders a failed last-maintenance even with no other signals", () => {
207
- const t = tree({
208
- "domain-a/topic-x": ["page-a", "page-b"],
209
- "domain-a/topic-y": ["page-c", "page-d"],
210
- });
211
- const report = computeV3Health({
212
- tree: t,
213
- allSlugs: ["page-a", "page-b", "page-c", "page-d"],
214
- lastMaintain: { at: 99, ok: false },
215
- });
216
- const rendered = renderV3Health(report);
217
- expect(rendered).toContain("last maintenance FAILED");
218
- });
219
- });
@@ -1,288 +0,0 @@
1
- import { mkdtemp, rm } from "node:fs/promises";
2
- import { tmpdir } from "node:os";
3
- import { join } from "node:path";
4
- import { afterEach, beforeEach, describe, expect, test } from "bun:test";
5
-
6
- import { setOverridesForTesting } from "../../../__tests__/feature-flag-test-helpers.js";
7
- import type { AssistantConfig } from "../../../config/types.js";
8
- import type { MemoryJob } from "../../jobs-store.js";
9
- import { readPage, writePage } from "../../v2/page-store.js";
10
- import type { ConceptPage } from "../../v2/types.js";
11
- import type { AssignPageResult, AssignPagesOptions } from "../assign.js";
12
- import {
13
- type ClassifyCandidate,
14
- computeClassifyTargets,
15
- maintainJob,
16
- type MaintainJobDeps,
17
- } from "../maintain-job.js";
18
- import type { LeafNode, LeafPath, LeafTree, Slug } from "../types.js";
19
-
20
- const FLAG_SHADOW = "memory-v3-shadow";
21
- const FLAG_LIVE = "memory-v3-live";
22
-
23
- // The flag resolver ignores the passed config and reads the override cache; the
24
- // config arg only satisfies the signature. Flags are driven via
25
- // `setOverridesForTesting` below.
26
- const CONFIG = {} as AssistantConfig;
27
-
28
- function makeLeaf(path: LeafPath): LeafNode {
29
- return {
30
- path,
31
- frontmatter: { path, in_core: false },
32
- description: `${path} description`,
33
- members: [],
34
- domain: path.split("/")[0],
35
- };
36
- }
37
-
38
- function makeTree(paths: LeafPath[]): LeafTree {
39
- const leaves = new Map<LeafPath, LeafNode>();
40
- for (const path of paths) leaves.set(path, makeLeaf(path));
41
- return { leaves, byPage: new Map<Slug, LeafPath[]>() };
42
- }
43
-
44
- function makePage(slug: string, leaves?: string[]): ConceptPage {
45
- return {
46
- slug,
47
- frontmatter: {
48
- summary: `Summary for ${slug}`,
49
- edges: [],
50
- ref_files: [],
51
- ref_urls: [],
52
- leaves,
53
- },
54
- body: `Body content for ${slug}`,
55
- };
56
- }
57
-
58
- const JOB = { id: "job-1", type: "memory_v3_maintain" } as unknown as MemoryJob;
59
-
60
- describe("maintainJob", () => {
61
- let workspaceDir: string;
62
-
63
- beforeEach(async () => {
64
- workspaceDir = await mkdtemp(join(tmpdir(), "v3-maintain-"));
65
- });
66
-
67
- afterEach(async () => {
68
- setOverridesForTesting({});
69
- await rm(workspaceDir, { recursive: true, force: true });
70
- });
71
-
72
- function deps(overrides: Partial<MaintainJobDeps> = {}): {
73
- deps: MaintainJobDeps;
74
- calls: { assign: number; invalidate: number; commit: number };
75
- } {
76
- const calls = { assign: 0, invalidate: 0, commit: 0 };
77
- const base: MaintainJobDeps = {
78
- workspaceDir,
79
- loadTree: async () => makeTree(["domain/topic-a", "domain/topic-b"]),
80
- assignPages: async (
81
- _opts: AssignPagesOptions,
82
- ): Promise<AssignPageResult[]> => {
83
- calls.assign += 1;
84
- return [];
85
- },
86
- selectClassifyTargets: async () => [],
87
- commitClassifyHighWater: () => {
88
- calls.commit += 1;
89
- },
90
- invalidateLanes: () => {
91
- calls.invalidate += 1;
92
- },
93
- };
94
- return { deps: { ...base, ...overrides }, calls };
95
- }
96
-
97
- test("no-op when both v3 flags are off", async () => {
98
- setOverridesForTesting({ [FLAG_SHADOW]: false, [FLAG_LIVE]: false });
99
- const { deps: d, calls } = deps();
100
- const outcome = await maintainJob(JOB, CONFIG, d);
101
- expect(outcome.disabled).toBe(true);
102
- expect(calls.assign).toBe(0);
103
- expect(calls.invalidate).toBe(0);
104
- });
105
-
106
- test("runs assign + invalidate when shadow flag is on", async () => {
107
- setOverridesForTesting({ [FLAG_SHADOW]: true });
108
- const { deps: d, calls } = deps();
109
- const outcome = await maintainJob(JOB, CONFIG, d);
110
- expect(outcome.disabled).toBe(false);
111
- expect(calls.assign).toBe(1);
112
- expect(calls.invalidate).toBe(1);
113
- expect(outcome.invalidated).toBe(true);
114
- });
115
-
116
- test("runs when only the live flag is on", async () => {
117
- setOverridesForTesting({ [FLAG_LIVE]: true });
118
- const { deps: d, calls } = deps();
119
- await maintainJob(JOB, CONFIG, d);
120
- expect(calls.assign).toBe(1);
121
- expect(calls.invalidate).toBe(1);
122
- });
123
-
124
- test("counts pages newly assigned by the classify-union stage", async () => {
125
- setOverridesForTesting({ [FLAG_SHADOW]: true });
126
- const { deps: d } = deps({
127
- assignPages: async () => [
128
- { slug: "p1", before: [], after: ["domain/topic-a"], failed: false },
129
- { slug: "p2", before: [], after: [], failed: true },
130
- {
131
- slug: "p3",
132
- before: ["domain/topic-a"],
133
- after: ["domain/topic-a"],
134
- failed: false,
135
- },
136
- ],
137
- });
138
- const outcome = await maintainJob(JOB, CONFIG, d);
139
- expect(outcome.assigned).toBe(1);
140
- });
141
-
142
- test("prune drops dangling leaf refs and rewrites the page", async () => {
143
- setOverridesForTesting({ [FLAG_SHADOW]: true });
144
- await writePage(
145
- workspaceDir,
146
- makePage("dangling", ["domain/topic-a", "domain/gone"]),
147
- );
148
- await writePage(workspaceDir, makePage("clean", ["domain/topic-b"]));
149
- await writePage(workspaceDir, makePage("empty", []));
150
-
151
- const { deps: d } = deps();
152
- const outcome = await maintainJob(JOB, CONFIG, d);
153
-
154
- expect(outcome.pruned).toBe(1);
155
- expect(outcome.prunedRefs).toBe(1);
156
-
157
- const dangling = await readPage(workspaceDir, "dangling");
158
- expect(dangling?.frontmatter.leaves).toEqual(["domain/topic-a"]);
159
-
160
- const clean = await readPage(workspaceDir, "clean");
161
- expect(clean?.frontmatter.leaves).toEqual(["domain/topic-b"]);
162
- });
163
-
164
- test("contains an assign failure without aborting prune or invalidate", async () => {
165
- setOverridesForTesting({ [FLAG_SHADOW]: true });
166
- await writePage(
167
- workspaceDir,
168
- makePage("dangling", ["domain/topic-a", "domain/gone"]),
169
- );
170
- const { deps: d, calls } = deps({
171
- assignPages: async () => {
172
- throw new Error("assign boom");
173
- },
174
- });
175
- const outcome = await maintainJob(JOB, CONFIG, d);
176
- expect(outcome.failures).toContain("assign");
177
- expect(outcome.pruned).toBe(1);
178
- expect(calls.invalidate).toBe(1);
179
- expect(outcome.invalidated).toBe(true);
180
- });
181
-
182
- test("contains a tree-load failure but still invalidates lanes", async () => {
183
- setOverridesForTesting({ [FLAG_SHADOW]: true });
184
- const { deps: d, calls } = deps({
185
- loadTree: async () => {
186
- throw new Error("load boom");
187
- },
188
- });
189
- const outcome = await maintainJob(JOB, CONFIG, d);
190
- expect(outcome.failures).toContain("load_tree");
191
- expect(calls.assign).toBe(0);
192
- expect(calls.invalidate).toBe(1);
193
- });
194
-
195
- test("classifies the selected delta targets (passes them to assignPages)", async () => {
196
- setOverridesForTesting({ [FLAG_SHADOW]: true });
197
- let seenSlugs: Slug[] | undefined;
198
- const { deps: d } = deps({
199
- selectClassifyTargets: async () => ["p-new", "p-edited"],
200
- assignPages: async (opts: AssignPagesOptions) => {
201
- seenSlugs = opts.slugs;
202
- return [];
203
- },
204
- });
205
- await maintainJob(JOB, CONFIG, d);
206
- expect(seenSlugs).toEqual(["p-new", "p-edited"]);
207
- });
208
-
209
- test("advances the high-water mark after a successful classify pass", async () => {
210
- setOverridesForTesting({ [FLAG_SHADOW]: true });
211
- const { deps: d, calls } = deps();
212
- await maintainJob(JOB, CONFIG, d);
213
- expect(calls.commit).toBe(1);
214
- });
215
-
216
- test("does not advance the high-water mark when classify throws", async () => {
217
- setOverridesForTesting({ [FLAG_SHADOW]: true });
218
- const { deps: d, calls } = deps({
219
- assignPages: async () => {
220
- throw new Error("assign boom");
221
- },
222
- });
223
- const outcome = await maintainJob(JOB, CONFIG, d);
224
- expect(outcome.failures).toContain("assign");
225
- expect(calls.commit).toBe(0);
226
- });
227
- });
228
-
229
- describe("computeClassifyTargets", () => {
230
- const page = (
231
- slug: string,
232
- modifiedAt: number,
233
- leaves: string[] = [],
234
- ): ClassifyCandidate => ({ slug, modifiedAt, leaves });
235
-
236
- test("first run (null high-water) classifies only unassigned pages", () => {
237
- const targets = computeClassifyTargets(
238
- [page("unassigned", 100, []), page("assigned-recent", 200, ["domain/a"])],
239
- null,
240
- );
241
- expect(targets).toEqual(["unassigned"]);
242
- });
243
-
244
- test("re-classifies an already-assigned page edited since the high-water", () => {
245
- const targets = computeClassifyTargets(
246
- [page("assigned-edited", 300, ["domain/a"])],
247
- 200,
248
- );
249
- expect(targets).toEqual(["assigned-edited"]);
250
- });
251
-
252
- test("skips an assigned page untouched since the high-water (no self-trigger / drift)", () => {
253
- const targets = computeClassifyTargets(
254
- [page("assigned-stale", 150, ["domain/a"])],
255
- 200,
256
- );
257
- expect(targets).toEqual([]);
258
- });
259
-
260
- test("still classifies unassigned pages regardless of mtime", () => {
261
- const targets = computeClassifyTargets(
262
- [page("unassigned-old", 10, [])],
263
- 200,
264
- );
265
- expect(targets).toEqual(["unassigned-old"]);
266
- });
267
-
268
- test("excludes synthetic skill/CLI rows (modifiedAt 0) despite empty leaves", () => {
269
- const targets = computeClassifyTargets(
270
- [page("skills/meet-join", 0, []), page("real", 300, [])],
271
- 200,
272
- );
273
- expect(targets).toEqual(["real"]);
274
- });
275
-
276
- test("targets = unassigned ∪ recently-edited, excluding assigned-and-stale", () => {
277
- const targets = computeClassifyTargets(
278
- [
279
- page("unassigned-old", 10, []),
280
- page("assigned-stale", 150, ["domain/a"]),
281
- page("assigned-fresh", 300, ["domain/a"]),
282
- page("skills/x", 0, []),
283
- ],
284
- 200,
285
- );
286
- expect(targets).toEqual(["unassigned-old", "assigned-fresh"]);
287
- });
288
- });
@@ -1,107 +0,0 @@
1
- import { expect, test } from "bun:test";
2
-
3
- import { buildNeedleIndex } from "../needle.js";
4
- import type { LeafNode, LeafPath, LeafTree, Slug } from "../types.js";
5
-
6
- /**
7
- * Builds a small synthetic LeafTree + summary stub. No real content — every
8
- * label, summary, and slug here is invented for the test.
9
- */
10
- function makeFixture(): {
11
- tree: LeafTree;
12
- summaries: Map<Slug, string>;
13
- } {
14
- const leaves = new Map<LeafPath, LeafNode>();
15
- const byPage = new Map<Slug, LeafPath[]>();
16
-
17
- const leaf = (path: LeafPath, description: string, members: Slug[]): void => {
18
- leaves.set(path, {
19
- path,
20
- frontmatter: { path, in_core: false },
21
- description,
22
- members,
23
- domain: "synthetic",
24
- });
25
- for (const slug of members) {
26
- const list = byPage.get(slug) ?? [];
27
- list.push(path);
28
- byPage.set(slug, list);
29
- }
30
- };
31
-
32
- leaf("animals/aquatic", "creatures that swim in water", ["dolphin-notes"]);
33
- leaf("animals/birds", "winged creatures that fly", ["sparrow-notes"]);
34
- leaf("vehicles/land", "machines with wheels for roads", ["bicycle-notes"]);
35
-
36
- const summaries = new Map<Slug, string>([
37
- ["dolphin-notes", "observations about marine mammals and echolocation"],
38
- ["sparrow-notes", "field journal of garden visitors"],
39
- ["bicycle-notes", "maintenance log for chains and brakes"],
40
- ]);
41
-
42
- return { tree: { leaves, byPage }, summaries };
43
- }
44
-
45
- test("query matches a literal term in a page summary", async () => {
46
- const { tree, summaries } = makeFixture();
47
- const index = await buildNeedleIndex(
48
- tree,
49
- async (slug) => summaries.get(slug) ?? "",
50
- );
51
-
52
- const results = index.query("echolocation", 3);
53
- expect(results[0]).toBe("dolphin-notes");
54
- });
55
-
56
- test("query matches a term that appears only in a leaf label", async () => {
57
- const { tree, summaries } = makeFixture();
58
- const index = await buildNeedleIndex(
59
- tree,
60
- async (slug) => summaries.get(slug) ?? "",
61
- );
62
-
63
- // "wheels" appears only in the vehicles/land leaf description, not in any
64
- // summary or slug — it must still surface bicycle-notes.
65
- const results = index.query("wheels", 3);
66
- expect(results).toContain("bicycle-notes");
67
- });
68
-
69
- test("query matches a term in the slug title segment", async () => {
70
- const { tree, summaries } = makeFixture();
71
- const index = await buildNeedleIndex(
72
- tree,
73
- async (slug) => summaries.get(slug) ?? "",
74
- );
75
-
76
- const results = index.query("sparrow", 3);
77
- expect(results[0]).toBe("sparrow-notes");
78
- });
79
-
80
- test("nonsense query returns no spurious results", async () => {
81
- const { tree, summaries } = makeFixture();
82
- const index = await buildNeedleIndex(
83
- tree,
84
- async (slug) => summaries.get(slug) ?? "",
85
- );
86
-
87
- expect(index.query("xyzzyplugh", 3)).toEqual([]);
88
- });
89
-
90
- test("results are capped at k and ordered by score", async () => {
91
- const { tree, summaries } = makeFixture();
92
- const index = await buildNeedleIndex(
93
- tree,
94
- async (slug) => summaries.get(slug) ?? "",
95
- );
96
-
97
- // "creatures" appears in both animal leaf labels; vehicles should be absent.
98
- const results = index.query("creatures", 1);
99
- expect(results.length).toBe(1);
100
- expect(results[0]).not.toBe("bicycle-notes");
101
- });
102
-
103
- test("empty corpus yields empty results", async () => {
104
- const tree: LeafTree = { leaves: new Map(), byPage: new Map() };
105
- const index = await buildNeedleIndex(tree, async () => "");
106
- expect(index.query("anything", 5)).toEqual([]);
107
- });