@vellumai/assistant 0.8.1 → 0.8.3

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 (630) hide show
  1. package/ARCHITECTURE.md +13 -19
  2. package/Dockerfile +75 -1
  3. package/bun.lock +11 -1
  4. package/docker-entrypoint.sh +17 -0
  5. package/docker-init-apt-root.sh +167 -0
  6. package/docker-kata-apt-env.sh +39 -0
  7. package/docs/plugins.md +88 -47
  8. package/docs/skills.md +9 -7
  9. package/examples/plugins/echo/README.md +27 -27
  10. package/examples/plugins/echo/package.json +3 -0
  11. package/examples/plugins/echo/register.ts +31 -31
  12. package/node_modules/@vellumai/slack-text/src/index.test.ts +114 -14
  13. package/node_modules/@vellumai/slack-text/src/index.ts +82 -18
  14. package/openapi.yaml +642 -5
  15. package/package.json +3 -1
  16. package/scripts/generate-openapi.ts +83 -10
  17. package/scripts/sync-llm-catalog.ts +2 -2
  18. package/scripts/sync-web-search-catalog.ts +47 -25
  19. package/src/__tests__/agent-image-optimize.test.ts +11 -3
  20. package/src/__tests__/agent-loop-exit-reason.test.ts +272 -0
  21. package/src/__tests__/agent-loop-provider-error-recording.test.ts +195 -0
  22. package/src/__tests__/agent-wake-disk-pressure-callsite.test.ts +131 -0
  23. package/src/__tests__/anthropic-provider.test.ts +45 -0
  24. package/src/__tests__/app-builder-tool-scripts.test.ts +9 -3
  25. package/src/__tests__/app-executors.test.ts +220 -4
  26. package/src/__tests__/auto-analysis-end-to-end.test.ts +35 -0
  27. package/src/__tests__/bundled-asset.test.ts +6 -6
  28. package/src/__tests__/channel-availability-routes.test.ts +206 -0
  29. package/src/__tests__/channel-delivery-store.test.ts +289 -1
  30. package/src/__tests__/circuit-breaker-pipeline.test.ts +0 -1
  31. package/src/__tests__/clawhub.test.ts +75 -16
  32. package/src/__tests__/compactor-tail-resolution.test.ts +147 -0
  33. package/src/__tests__/config-get-vision-flag.test.ts +136 -0
  34. package/src/__tests__/config-loader-backfill.test.ts +115 -18
  35. package/src/__tests__/config-schema.test.ts +21 -0
  36. package/src/__tests__/config-set-route.test.ts +80 -0
  37. package/src/__tests__/config-sounds-sync.test.ts +97 -0
  38. package/src/__tests__/config-watcher-skill-reseed.test.ts +453 -0
  39. package/src/__tests__/context-search-conversations-source.test.ts +117 -2
  40. package/src/__tests__/context-search-memory-v2-source.test.ts +0 -1
  41. package/src/__tests__/context-search-workspace-source.test.ts +7 -0
  42. package/src/__tests__/context-token-estimator.test.ts +31 -65
  43. package/src/__tests__/conversation-abort-tool-results.test.ts +4 -1
  44. package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +1 -0
  45. package/src/__tests__/conversation-agent-loop-overflow.test.ts +92 -92
  46. package/src/__tests__/conversation-agent-loop.test.ts +59 -1
  47. package/src/__tests__/conversation-error.test.ts +42 -3
  48. package/src/__tests__/conversation-fork-crud.test.ts +82 -0
  49. package/src/__tests__/conversation-inference-profile-route.test.ts +40 -4
  50. package/src/__tests__/conversation-lifecycle.test.ts +173 -0
  51. package/src/__tests__/conversation-media-retry.test.ts +19 -8
  52. package/src/__tests__/conversation-message-sync-tags.test.ts +97 -0
  53. package/src/__tests__/conversation-pairing.test.ts +54 -0
  54. package/src/__tests__/conversation-process-callsite.test.ts +4 -1
  55. package/src/__tests__/conversation-provider-retry-repair.test.ts +5 -1
  56. package/src/__tests__/conversation-queue.test.ts +4 -1
  57. package/src/__tests__/conversation-runtime-assembly.test.ts +102 -13
  58. package/src/__tests__/conversation-slash-queue.test.ts +59 -1
  59. package/src/__tests__/conversation-slash-unknown.test.ts +4 -1
  60. package/src/__tests__/conversation-surfaces-table-action.test.ts +360 -0
  61. package/src/__tests__/conversation-sync-tags.test.ts +235 -0
  62. package/src/__tests__/conversation-workspace-injection.test.ts +5 -1
  63. package/src/__tests__/conversation-workspace-tool-tracking.test.ts +5 -1
  64. package/src/__tests__/credential-security-invariants.test.ts +3 -2
  65. package/src/__tests__/date-context.test.ts +45 -0
  66. package/src/__tests__/db-slack-external-content-normalization.test.ts +301 -0
  67. package/src/__tests__/delete-managed-skill-tool.test.ts +55 -13
  68. package/src/__tests__/disk-pressure-tools.test.ts +1 -0
  69. package/src/__tests__/dm-backfill.test.ts +121 -10
  70. package/src/__tests__/document-tool-security.test.ts +258 -0
  71. package/src/__tests__/dynamic-skill-workflow-prompt.test.ts +0 -1
  72. package/src/__tests__/edit-propagation.test.ts +33 -0
  73. package/src/__tests__/empty-response-pipeline.test.ts +0 -4
  74. package/src/__tests__/external-plugin-loader.test.ts +151 -55
  75. package/src/__tests__/filing-service.test.ts +140 -0
  76. package/src/__tests__/get-skill-detail-audit.test.ts +0 -4
  77. package/src/__tests__/guardian-action-no-hardcoded-copy.test.ts +0 -1
  78. package/src/__tests__/guardian-dispatch.test.ts +1 -0
  79. package/src/__tests__/handlers-skills-memory-v2-reseed.test.ts +43 -62
  80. package/src/__tests__/heartbeat-service.test.ts +24 -164
  81. package/src/__tests__/helpers/channel-test-adapter.ts +0 -2
  82. package/src/__tests__/helpers/tar-fixtures.ts +39 -0
  83. package/src/__tests__/helpers/wait-for.ts +21 -0
  84. package/src/__tests__/history-repair-pipeline.test.ts +0 -3
  85. package/src/__tests__/history-repair.test.ts +73 -0
  86. package/src/__tests__/host-app-control-proxy.test.ts +507 -10
  87. package/src/__tests__/host-proxy-preactivation.test.ts +200 -13
  88. package/src/__tests__/image-credentials.test.ts +1 -1
  89. package/src/__tests__/inbound-slack-persistence.test.ts +2 -0
  90. package/src/__tests__/inference-no-mode-boot-e2e.test.ts +1 -1
  91. package/src/__tests__/inference-profile-reaper.test.ts +4 -2
  92. package/src/__tests__/inference-profile-session-handler.test.ts +18 -6
  93. package/src/__tests__/inference-profile-session-ipc.test.ts +17 -5
  94. package/src/__tests__/injector-background-turn.test.ts +153 -0
  95. package/src/__tests__/injector-chain.test.ts +15 -8
  96. package/src/__tests__/install-skill-routing.test.ts +155 -37
  97. package/src/__tests__/lifecycle-memory-v2-seed.test.ts +99 -3
  98. package/src/__tests__/list-messages-page-latest.test.ts +55 -0
  99. package/src/__tests__/llm-call-pipeline.test.ts +0 -3
  100. package/src/__tests__/llm-callsite-catalog.test.ts +25 -0
  101. package/src/__tests__/llm-catalog-parity.test.ts +58 -13
  102. package/src/__tests__/llm-request-log-agent-loop-exit-reason.test.ts +116 -0
  103. package/src/__tests__/llm-request-log-error-payload.test.ts +138 -0
  104. package/src/__tests__/llm-request-log-source-clickhouse.test.ts +36 -0
  105. package/src/__tests__/llm-request-log-source-factory.test.ts +29 -53
  106. package/src/__tests__/llm-resolver.test.ts +255 -2
  107. package/src/__tests__/llm-usage-store.test.ts +114 -0
  108. package/src/__tests__/managed-profile-guard.test.ts +41 -29
  109. package/src/__tests__/managed-skill-lifecycle.test.ts +109 -18
  110. package/src/__tests__/managed-store.test.ts +84 -192
  111. package/src/__tests__/media-generate-image.test.ts +1 -1
  112. package/src/__tests__/memory-retrieval-pipeline.test.ts +0 -2
  113. package/src/__tests__/messages-after-tiebreaker.test.ts +122 -0
  114. package/src/__tests__/notification-decision-fallback.test.ts +0 -91
  115. package/src/__tests__/notification-decision-strategy.test.ts +14 -31
  116. package/src/__tests__/notification-deep-link.test.ts +15 -0
  117. package/src/__tests__/notification-guardian-path.test.ts +1 -2
  118. package/src/__tests__/notification-platform-adapter.test.ts +5 -4
  119. package/src/__tests__/notification-telegram-adapter.test.ts +1 -0
  120. package/src/__tests__/notification-vellum-adapter.test.ts +113 -0
  121. package/src/__tests__/oauth-commands-routes.test.ts +168 -16
  122. package/src/__tests__/oauth-provider-profiles.test.ts +9 -0
  123. package/src/__tests__/openai-provider.test.ts +242 -3
  124. package/src/__tests__/openai-responses-cutover-guard.test.ts +17 -9
  125. package/src/__tests__/openrouter-provider-only.test.ts +51 -3
  126. package/src/__tests__/openrouter-token-estimation.test.ts +34 -25
  127. package/src/__tests__/overflow-reduce-pipeline.test.ts +0 -2
  128. package/src/__tests__/persistence-pipeline.test.ts +0 -2
  129. package/src/__tests__/{managed-proxy-context.test.ts → platform-proxy-context.test.ts} +7 -2
  130. package/src/__tests__/platform.test.ts +2 -0
  131. package/src/__tests__/plugin-api-shim.test.ts +125 -0
  132. package/src/__tests__/plugin-bootstrap.test.ts +10 -36
  133. package/src/__tests__/plugin-external-api.test.ts +68 -0
  134. package/src/__tests__/plugin-registry.test.ts +0 -77
  135. package/src/__tests__/plugin-route-contribution.test.ts +0 -1
  136. package/src/__tests__/plugin-skill-contribution.test.ts +0 -2
  137. package/src/__tests__/plugin-tool-contribution.test.ts +16 -15
  138. package/src/__tests__/plugin-types.test.ts +3 -13
  139. package/src/__tests__/process-message-background-slack.test.ts +8 -1
  140. package/src/__tests__/process-message-display-content.test.ts +421 -0
  141. package/src/__tests__/provider-catalog-visibility.test.ts +158 -0
  142. package/src/__tests__/provider-error-scenarios.test.ts +111 -0
  143. package/src/__tests__/{provider-managed-proxy-integration.test.ts → provider-platform-proxy-integration.test.ts} +33 -31
  144. package/src/__tests__/scaffold-managed-skill-tool.test.ts +65 -13
  145. package/src/__tests__/schedule-routes.test.ts +50 -3
  146. package/src/__tests__/schedule-store.test.ts +94 -0
  147. package/src/__tests__/scheduler-reuse-conversation.test.ts +54 -7
  148. package/src/__tests__/schema-transforms.test.ts +20 -0
  149. package/src/__tests__/search-skills-unified.test.ts +0 -5
  150. package/src/__tests__/{secret-routes-managed-proxy.test.ts → secret-routes-platform-proxy.test.ts} +1 -1
  151. package/src/__tests__/server-history-render.test.ts +43 -0
  152. package/src/__tests__/skill-load-feature-flag.test.ts +0 -12
  153. package/src/__tests__/skill-load-tool.test.ts +27 -89
  154. package/src/__tests__/skill-memory.test.ts +23 -3
  155. package/src/__tests__/skills-file-content-endpoint.test.ts +9 -38
  156. package/src/__tests__/skills-files-catalog-fallback.test.ts +0 -3
  157. package/src/__tests__/skills-install-extract.test.ts +49 -38
  158. package/src/__tests__/skills-install-staging.test.ts +159 -0
  159. package/src/__tests__/skills-uninstall.test.ts +9 -41
  160. package/src/__tests__/skills.test.ts +51 -58
  161. package/src/__tests__/slack-channel-config.test.ts +9 -0
  162. package/src/__tests__/subagent-tool-filtering.test.ts +50 -0
  163. package/src/__tests__/system-prompt.test.ts +670 -63
  164. package/src/__tests__/terminal-tools.test.ts +28 -1
  165. package/src/__tests__/thread-backfill.test.ts +557 -27
  166. package/src/__tests__/title-generate-pipeline.test.ts +0 -13
  167. package/src/__tests__/token-estimate-pipeline.test.ts +0 -3
  168. package/src/__tests__/tool-error-pipeline.test.ts +0 -3
  169. package/src/__tests__/tool-execute-pipeline.test.ts +0 -5
  170. package/src/__tests__/tool-executor-lifecycle-events.test.ts +1 -1
  171. package/src/__tests__/tool-executor.test.ts +16 -4
  172. package/src/__tests__/tool-result-truncate-pipeline.test.ts +0 -12
  173. package/src/__tests__/turn-events-store.test.ts +256 -0
  174. package/src/__tests__/twilio-routes.test.ts +4 -0
  175. package/src/__tests__/user-plugin-loader.test.ts +0 -7
  176. package/src/__tests__/voice-session-bridge.test.ts +198 -0
  177. package/src/__tests__/web-search-catalog-parity.test.ts +32 -10
  178. package/src/__tests__/workspace-migration-057-repair-stale-gemini-model-ids.test.ts +115 -3
  179. package/src/__tests__/workspace-migration-072-seed-reply-suggestion-callsite.test.ts +50 -0
  180. package/src/__tests__/workspace-migration-073-repair-recall-callsite-empty-profile.test.ts +153 -0
  181. package/src/__tests__/workspace-migration-085-memory-v2-bm25-b-reembed-disabled-v2-pages.test.ts +220 -0
  182. package/src/__tests__/workspace-migration-086-revert-stale-gemini-mis-rewrites.test.ts +269 -0
  183. package/src/__tests__/workspace-migration-087-memory-router-balanced-profile.test.ts +228 -0
  184. package/src/__tests__/workspace-migration-remove-legacy-skills-index.test.ts +309 -0
  185. package/src/__tests__/workspace-migrations-runner.test.ts +111 -3
  186. package/src/a2a/__tests__/agent-card.test.ts +98 -0
  187. package/src/a2a/__tests__/e2e-a2a-channel.test.ts +597 -0
  188. package/src/a2a/__tests__/protocol-helpers.test.ts +113 -0
  189. package/src/a2a/__tests__/task-store.test.ts +246 -0
  190. package/src/a2a/agent-card.ts +58 -0
  191. package/src/a2a/feature-gate.ts +8 -0
  192. package/src/a2a/protocol-constants.ts +21 -0
  193. package/src/a2a/protocol-errors.ts +50 -0
  194. package/src/a2a/protocol-types.ts +162 -0
  195. package/src/a2a/task-store.ts +168 -0
  196. package/src/acp/resolve-agent.ts +1 -1
  197. package/src/agent/image-optimize.ts +13 -5
  198. package/src/agent/loop.ts +167 -18
  199. package/src/calls/voice-session-bridge.ts +61 -42
  200. package/src/channels/config.ts +9 -0
  201. package/src/channels/types.ts +122 -0
  202. package/src/cli/__tests__/unknown-command.test.ts +24 -0
  203. package/src/cli/commands/__tests__/changelog.test.ts +304 -319
  204. package/src/cli/{__tests__ → commands/__tests__}/notifications.test.ts +201 -28
  205. package/src/cli/commands/__tests__/schedules.test.ts +960 -0
  206. package/src/cli/commands/changelog.ts +106 -42
  207. package/src/cli/commands/conversations.ts +102 -17
  208. package/src/cli/commands/default-action.ts +10 -53
  209. package/src/cli/commands/notifications.ts +388 -346
  210. package/src/cli/commands/plugins.ts +252 -0
  211. package/src/cli/commands/schedules.ts +683 -0
  212. package/src/cli/commands/telemetry.ts +40 -0
  213. package/src/cli/lib/__tests__/cli-colors.test.ts +48 -0
  214. package/src/cli/lib/__tests__/confirm-prompt.test.ts +159 -0
  215. package/src/cli/lib/__tests__/install-from-github.test.ts +355 -0
  216. package/src/cli/lib/__tests__/list-installed-plugins.test.ts +154 -0
  217. package/src/cli/lib/__tests__/search-plugins.test.ts +261 -0
  218. package/src/cli/lib/__tests__/uninstall-plugin.test.ts +124 -0
  219. package/src/cli/lib/__tests__/unknown-command.test.ts +106 -0
  220. package/src/cli/lib/cli-colors.ts +12 -0
  221. package/src/cli/lib/confirm-prompt.ts +79 -0
  222. package/src/cli/lib/install-from-github.ts +303 -0
  223. package/src/cli/lib/list-installed-plugins.ts +137 -0
  224. package/src/cli/lib/search-plugins.ts +163 -0
  225. package/src/cli/lib/uninstall-plugin.ts +82 -0
  226. package/src/cli/lib/unknown-command.ts +111 -0
  227. package/src/cli/program.ts +52 -2
  228. package/src/config/assistant-feature-flags.ts +24 -54
  229. package/src/config/bundled-skills/app-builder/SKILL.md +140 -22
  230. package/src/config/bundled-skills/app-builder/TOOLS.json +7 -0
  231. package/src/config/bundled-skills/computer-use/TOOLS.json +15 -52
  232. package/src/config/bundled-skills/document/SKILL.md +23 -3
  233. package/src/config/bundled-skills/document/TOOLS.json +53 -0
  234. package/src/config/bundled-skills/document/tools/document-delete.ts +12 -0
  235. package/src/config/bundled-skills/document/tools/document-list.ts +12 -0
  236. package/src/config/bundled-skills/document/tools/document-read.ts +12 -0
  237. package/src/config/bundled-skills/phone-calls/SKILL.md +1 -1
  238. package/src/config/bundled-skills/skill-management/SKILL.md +2 -2
  239. package/src/config/bundled-skills/skill-management/TOOLS.json +7 -7
  240. package/src/config/bundled-tool-registry.ts +6 -0
  241. package/src/config/call-site-defaults.ts +105 -0
  242. package/src/config/feature-flag-registry.json +41 -9
  243. package/src/config/llm-resolver.ts +52 -1
  244. package/src/config/loader.ts +64 -38
  245. package/src/config/schema.ts +9 -10
  246. package/src/config/schemas/__tests__/llm-request-logs.test.ts +36 -0
  247. package/src/config/schemas/__tests__/memory-v2.test.ts +3 -3
  248. package/src/config/schemas/channels.ts +17 -0
  249. package/src/config/schemas/compaction.ts +28 -0
  250. package/src/config/schemas/conversations.ts +10 -0
  251. package/src/config/schemas/heartbeat.ts +23 -0
  252. package/src/config/schemas/llm-request-logs.ts +31 -7
  253. package/src/config/schemas/llm.ts +1 -0
  254. package/src/config/schemas/memory-retrieval.ts +18 -0
  255. package/src/config/schemas/memory-retrospective.ts +1 -1
  256. package/src/config/schemas/memory-v2.ts +4 -4
  257. package/src/config/schemas/memory.ts +3 -1
  258. package/src/config/schemas/tools.ts +14 -0
  259. package/src/config/seed-inference-profiles.ts +99 -29
  260. package/src/config/skills.ts +3 -96
  261. package/src/context/compactor.ts +1107 -0
  262. package/src/context/token-estimator.ts +34 -36
  263. package/src/context/window-manager.ts +197 -1520
  264. package/src/credential-execution/managed-catalog.ts +37 -0
  265. package/src/credential-health/credential-health-service.ts +280 -19
  266. package/src/daemon/__tests__/conversation-lifecycle-auto-analyze.test.ts +33 -18
  267. package/src/daemon/__tests__/conversation-tool-setup-exclude.test.ts +138 -0
  268. package/src/daemon/__tests__/conversation-tool-setup.test.ts +74 -0
  269. package/src/daemon/approval-generators.ts +8 -6
  270. package/src/daemon/config-watcher.ts +94 -31
  271. package/src/daemon/conversation-agent-loop-handlers.ts +78 -0
  272. package/src/daemon/conversation-agent-loop.ts +198 -11
  273. package/src/daemon/conversation-error.ts +171 -37
  274. package/src/daemon/conversation-lifecycle.ts +53 -40
  275. package/src/daemon/conversation-messaging.ts +25 -6
  276. package/src/daemon/conversation-process.ts +49 -12
  277. package/src/daemon/conversation-runtime-assembly.ts +25 -1
  278. package/src/daemon/conversation-slash.ts +12 -5
  279. package/src/daemon/conversation-store.ts +11 -4
  280. package/src/daemon/conversation-tool-setup.ts +39 -7
  281. package/src/daemon/conversation.ts +33 -8
  282. package/src/daemon/date-context.ts +40 -0
  283. package/src/daemon/external-plugins-bootstrap.ts +217 -181
  284. package/src/daemon/first-greeting.ts +22 -2
  285. package/src/daemon/guardian-action-generators.ts +1 -125
  286. package/src/daemon/handlers/__tests__/config-a2a-complete.test.ts +248 -0
  287. package/src/daemon/handlers/__tests__/config-a2a-invite.test.ts +154 -0
  288. package/src/daemon/handlers/__tests__/config-a2a-redeem.test.ts +133 -0
  289. package/src/daemon/handlers/__tests__/config-a2a.test.ts +95 -0
  290. package/src/daemon/handlers/config-a2a.ts +289 -0
  291. package/src/daemon/handlers/config-model.ts +6 -5
  292. package/src/daemon/handlers/config-slack-channel.ts +15 -3
  293. package/src/daemon/handlers/conversations.ts +1 -0
  294. package/src/daemon/handlers/shared.ts +14 -5
  295. package/src/daemon/handlers/skills.ts +111 -108
  296. package/src/daemon/history-repair.ts +28 -1
  297. package/src/daemon/host-app-control-proxy.ts +153 -27
  298. package/src/daemon/host-proxy-preactivation.ts +85 -18
  299. package/src/daemon/lifecycle.ts +89 -91
  300. package/src/daemon/meet-host-supervisor.ts +5 -4
  301. package/src/daemon/memory-v2-startup.ts +85 -0
  302. package/src/daemon/message-protocol.ts +1 -0
  303. package/src/daemon/message-types/conversations.ts +25 -0
  304. package/src/daemon/message-types/messages.ts +61 -0
  305. package/src/daemon/message-types/notifications.ts +21 -0
  306. package/src/daemon/message-types/subagents.ts +1 -0
  307. package/src/daemon/message-types/sync.ts +1 -0
  308. package/src/daemon/pkb-reminder-builder.test.ts +11 -54
  309. package/src/daemon/pkb-reminder-builder.ts +5 -20
  310. package/src/daemon/plugin-source-watcher.ts +146 -0
  311. package/src/daemon/process-message.ts +24 -3
  312. package/src/daemon/server.ts +11 -2
  313. package/src/daemon/skill-memory-refresh.ts +33 -0
  314. package/src/daemon/wake-target-adapter.ts +2 -0
  315. package/src/documents/document-store.ts +221 -3
  316. package/src/embedded/plugin-api.ts +40 -0
  317. package/src/export/__tests__/transcript-formatter.test.ts +121 -0
  318. package/src/export/transcript-formatter.ts +54 -20
  319. package/src/filing/filing-service.ts +39 -0
  320. package/src/heartbeat/__tests__/heartbeat-service.test.ts +135 -6
  321. package/src/heartbeat/heartbeat-run-store.ts +2 -1
  322. package/src/heartbeat/heartbeat-service.ts +73 -189
  323. package/src/home/__tests__/feed-types.test.ts +80 -0
  324. package/src/home/feed-types.ts +36 -2
  325. package/src/home/post-connect-feed.ts +1 -0
  326. package/src/index.ts +18 -1
  327. package/src/ipc/cli-client.ts +147 -45
  328. package/src/live-voice/__tests__/live-voice-stt.test.ts +57 -0
  329. package/src/mcp/client.ts +20 -4
  330. package/src/media/image-credentials.ts +3 -3
  331. package/src/memory/__tests__/bookmark-crud.test.ts +33 -27
  332. package/src/memory/__tests__/conversation-queries.test.ts +483 -0
  333. package/src/memory/__tests__/jobs-worker-v2-graph-trigger-embed.test.ts +113 -0
  334. package/src/memory/__tests__/memory-retrospective-enqueue.test.ts +2 -50
  335. package/src/memory/__tests__/memory-retrospective-job.test.ts +87 -4
  336. package/src/memory/__tests__/memory-retrospective-startup-cleanup.test.ts +119 -14
  337. package/src/memory/__tests__/message-content.test.ts +35 -0
  338. package/src/memory/bookmark-crud.ts +42 -10
  339. package/src/memory/context-search/sources/conversations.ts +62 -2
  340. package/src/memory/context-search/sources/workspace.ts +4 -0
  341. package/src/memory/conversation-crud.ts +63 -19
  342. package/src/memory/conversation-queries.ts +197 -11
  343. package/src/memory/conversation-title-service.ts +26 -4
  344. package/src/memory/db-init.ts +12 -0
  345. package/src/memory/delivery-crud.ts +152 -5
  346. package/src/memory/embedding-backend.ts +4 -4
  347. package/src/memory/external-conversation-store.ts +66 -5
  348. package/src/memory/graph/__tests__/conversation-graph-memory-v2-routing.test.ts +150 -12
  349. package/src/memory/graph/conversation-graph-memory.ts +49 -21
  350. package/src/memory/graph/tools.ts +9 -40
  351. package/src/memory/indexer.ts +34 -29
  352. package/src/memory/invite-store.ts +53 -0
  353. package/src/memory/jobs/__tests__/embed-concept-page.test.ts +73 -0
  354. package/src/memory/jobs/embed-concept-page.ts +20 -11
  355. package/src/memory/jobs-worker.ts +6 -1
  356. package/src/memory/llm-request-log-source-clickhouse.ts +24 -12
  357. package/src/memory/llm-request-log-source.ts +19 -52
  358. package/src/memory/llm-request-log-store.ts +92 -1
  359. package/src/memory/llm-usage-store.ts +125 -5
  360. package/src/memory/memory-retrospective-enqueue.ts +1 -20
  361. package/src/memory/memory-retrospective-job.ts +33 -6
  362. package/src/memory/memory-retrospective-startup-cleanup.ts +72 -5
  363. package/src/memory/message-content.ts +1 -1
  364. package/src/memory/migrations/109-external-conversation-bindings.ts +15 -4
  365. package/src/memory/migrations/229-delete-private-conversations.test.ts +38 -1
  366. package/src/memory/migrations/229-delete-private-conversations.ts +7 -0
  367. package/src/memory/migrations/247-external-conversation-binding-thread-id.ts +78 -0
  368. package/src/memory/migrations/248-create-onboarding-events.ts +21 -0
  369. package/src/memory/migrations/249-normalize-slack-external-content.ts +240 -0
  370. package/src/memory/migrations/250-provider-connection-base-url-and-models.ts +28 -0
  371. package/src/memory/migrations/251-a2a-tasks.ts +49 -0
  372. package/src/memory/migrations/252-llm-request-log-agent-loop-exit-reason.ts +32 -0
  373. package/src/memory/migrations/index.ts +9 -0
  374. package/src/memory/migrations/registry.ts +16 -0
  375. package/src/memory/onboarding-events-store.ts +106 -0
  376. package/src/memory/schema/a2a.ts +15 -0
  377. package/src/memory/schema/bookmarks.ts +0 -2
  378. package/src/memory/schema/calls.ts +1 -0
  379. package/src/memory/schema/index.ts +1 -0
  380. package/src/memory/schema/inference.ts +3 -3
  381. package/src/memory/schema/infrastructure.ts +13 -0
  382. package/src/memory/turn-events-store.ts +127 -2
  383. package/src/memory/v2/__tests__/activation-store.test.ts +25 -23
  384. package/src/memory/v2/__tests__/activation.test.ts +0 -8
  385. package/src/memory/v2/__tests__/cli-command-store.test.ts +404 -0
  386. package/src/memory/v2/__tests__/frontmatter-sweep.test.ts +25 -4
  387. package/src/memory/v2/__tests__/injection.test.ts +288 -11
  388. package/src/memory/v2/__tests__/migration.test.ts +87 -0
  389. package/src/memory/v2/__tests__/page-index.test.ts +83 -0
  390. package/src/memory/v2/__tests__/prompts-router.test.ts +58 -6
  391. package/src/memory/v2/__tests__/qdrant.test.ts +66 -3
  392. package/src/memory/v2/__tests__/router.test.ts +15 -0
  393. package/src/memory/v2/__tests__/skill-store.test.ts +387 -8
  394. package/src/memory/v2/__tests__/static-context.test.ts +12 -1
  395. package/src/memory/v2/activation-store.ts +14 -16
  396. package/src/memory/v2/cli-command-content.ts +19 -0
  397. package/src/memory/v2/cli-command-store.ts +304 -0
  398. package/src/memory/v2/frontmatter-sweep.ts +7 -1
  399. package/src/memory/v2/injection.ts +81 -26
  400. package/src/memory/v2/migration.ts +49 -19
  401. package/src/memory/v2/page-index.ts +63 -8
  402. package/src/memory/v2/prompts/router.ts +11 -8
  403. package/src/memory/v2/prompts/sweep.ts +2 -2
  404. package/src/memory/v2/qdrant.ts +135 -7
  405. package/src/memory/v2/router.ts +9 -8
  406. package/src/memory/v2/skill-store.ts +120 -35
  407. package/src/memory/v2/static-context.ts +4 -4
  408. package/src/memory/v2/types.ts +23 -0
  409. package/src/messaging/providers/a2a/__tests__/deliver.test.ts +274 -0
  410. package/src/messaging/providers/a2a/deliver.ts +156 -0
  411. package/src/messaging/providers/gmail/client.ts +9 -2
  412. package/src/messaging/providers/index.ts +11 -2
  413. package/src/messaging/providers/slack/__tests__/adapter-token-routing.test.ts +45 -5
  414. package/src/messaging/providers/slack/__tests__/download.test.ts +231 -0
  415. package/src/messaging/providers/slack/adapter.ts +43 -5
  416. package/src/messaging/providers/slack/client.ts +27 -0
  417. package/src/messaging/providers/slack/deep-link.ts +65 -0
  418. package/src/messaging/providers/slack/download.ts +104 -0
  419. package/src/messaging/providers/slack/message-metadata.test.ts +32 -0
  420. package/src/messaging/providers/slack/message-metadata.ts +27 -0
  421. package/src/messaging/providers/slack/render-transcript.test.ts +134 -0
  422. package/src/messaging/providers/slack/render-transcript.ts +69 -5
  423. package/src/messaging/providers/slack/types.ts +20 -1
  424. package/src/notifications/__tests__/broadcaster.test.ts +203 -0
  425. package/src/notifications/__tests__/decision-engine.test.ts +283 -0
  426. package/src/notifications/__tests__/deterministic-checks.test.ts +286 -0
  427. package/src/notifications/__tests__/emit-signal-home-feed.test.ts +1 -0
  428. package/src/notifications/__tests__/home-feed-side-effect.test.ts +430 -7
  429. package/src/notifications/adapters/macos.ts +12 -2
  430. package/src/notifications/broadcaster.ts +29 -4
  431. package/src/notifications/conversation-pairing.ts +2 -1
  432. package/src/notifications/copy-composer.ts +17 -64
  433. package/src/notifications/decision-engine.ts +113 -45
  434. package/src/notifications/deterministic-checks.ts +96 -0
  435. package/src/notifications/emit-signal.ts +21 -1
  436. package/src/notifications/home-feed-side-effect.ts +138 -5
  437. package/src/notifications/signal.ts +3 -5
  438. package/src/notifications/types.ts +8 -0
  439. package/src/oauth/connection-resolver.ts +8 -4
  440. package/src/oauth/platform-connection.test.ts +43 -3
  441. package/src/oauth/platform-connection.ts +19 -6
  442. package/src/oauth/seed-providers.ts +10 -1
  443. package/src/permissions/checker.ts +2 -0
  444. package/src/permissions/ipc-risk-types.ts +1 -0
  445. package/src/permissions/question-prompter.test.ts +416 -0
  446. package/src/permissions/question-prompter.ts +294 -0
  447. package/src/platform/client.test.ts +1 -1
  448. package/src/platform/client.ts +1 -1
  449. package/src/plugin-api/constants.ts +26 -0
  450. package/src/plugin-api/index.ts +34 -1
  451. package/src/plugin-api/types.ts +104 -22
  452. package/src/plugins/defaults/circuit-breaker.ts +0 -5
  453. package/src/plugins/defaults/compaction.ts +0 -4
  454. package/src/plugins/defaults/empty-response.ts +0 -2
  455. package/src/plugins/defaults/history-repair.ts +0 -2
  456. package/src/plugins/defaults/injectors.ts +74 -22
  457. package/src/plugins/defaults/llm-call.ts +0 -2
  458. package/src/plugins/defaults/memory-retrieval.ts +0 -1
  459. package/src/plugins/defaults/overflow-reduce.ts +0 -1
  460. package/src/plugins/defaults/persistence.ts +0 -2
  461. package/src/plugins/defaults/title-generate.ts +0 -5
  462. package/src/plugins/defaults/token-estimate.ts +0 -2
  463. package/src/plugins/defaults/tool-error.ts +0 -7
  464. package/src/plugins/defaults/tool-execute.ts +0 -2
  465. package/src/plugins/defaults/tool-result-truncate.ts +0 -4
  466. package/src/plugins/ensure-plugin-api-shim.ts +96 -0
  467. package/src/plugins/external-api.ts +104 -0
  468. package/src/plugins/external-plugin-loader.ts +187 -42
  469. package/src/plugins/feature-gate.ts +22 -0
  470. package/src/plugins/pipeline.ts +37 -0
  471. package/src/plugins/registry.ts +48 -80
  472. package/src/plugins/types.ts +40 -26
  473. package/src/plugins/user-loader.ts +21 -2
  474. package/src/proactive-artifact/aux-message-injector.ts +11 -0
  475. package/src/proactive-artifact/job.test.ts +37 -5
  476. package/src/prompts/__tests__/system-prompt.test.ts +10 -43
  477. package/src/prompts/__tests__/task-progress-hint-section.test.ts +95 -0
  478. package/src/prompts/normalize-onboarding.ts +27 -0
  479. package/src/prompts/sections.ts +302 -0
  480. package/src/prompts/system-prompt.ts +63 -174
  481. package/src/prompts/templates/BOOTSTRAP.md +17 -1
  482. package/src/prompts/templates/system-sections.ts +164 -0
  483. package/src/providers/__tests__/inference.test.ts +24 -7
  484. package/src/providers/anthropic/client.ts +28 -28
  485. package/src/providers/call-site-routing.ts +24 -6
  486. package/src/providers/connection-resolution.ts +68 -11
  487. package/src/providers/inference/__tests__/adapter-factory-openai-compatible.test.ts +74 -0
  488. package/src/providers/inference/__tests__/connections-openai-compatible.test.ts +175 -0
  489. package/src/providers/inference/__tests__/connections-status-label.test.ts +15 -0
  490. package/src/providers/inference/adapter-factory.ts +32 -6
  491. package/src/providers/inference/auth.ts +12 -0
  492. package/src/providers/inference/backfill.ts +14 -1
  493. package/src/providers/inference/connections.ts +159 -34
  494. package/src/providers/inference/resolve-auth.ts +14 -4
  495. package/src/providers/model-catalog.ts +249 -12
  496. package/src/providers/model-intents.ts +3 -3
  497. package/src/providers/openai/__tests__/chat-completions-provider-reasoning.test.ts +235 -0
  498. package/src/providers/openai/chat-completions-provider.ts +169 -8
  499. package/src/providers/openrouter/client.ts +49 -4
  500. package/src/providers/{managed-proxy → platform-proxy}/constants.ts +4 -2
  501. package/src/providers/{managed-proxy → platform-proxy}/context.ts +3 -3
  502. package/src/providers/provider-availability.ts +17 -2
  503. package/src/providers/provider-catalog-visibility.ts +38 -0
  504. package/src/providers/provider-send-message.ts +27 -12
  505. package/src/providers/registry.ts +52 -15
  506. package/src/providers/retry.ts +47 -1
  507. package/src/runtime/__tests__/agent-wake.test.ts +152 -0
  508. package/src/runtime/agent-wake.ts +103 -15
  509. package/src/runtime/auth/route-policy.ts +21 -1
  510. package/src/runtime/btw-sidechain.ts +2 -0
  511. package/src/runtime/http-server.ts +7 -16
  512. package/src/runtime/http-types.ts +19 -47
  513. package/src/runtime/migrations/origin-mode.ts +1 -1
  514. package/src/runtime/pending-interactions.ts +1 -0
  515. package/src/runtime/routes/__tests__/bookmark-routes.test.ts +17 -0
  516. package/src/runtime/routes/__tests__/consolidation-routes.test.ts +258 -0
  517. package/src/runtime/routes/__tests__/conversation-management-routes.test.ts +5 -1
  518. package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +172 -23
  519. package/src/runtime/routes/__tests__/inference-provider-connection-routes.test.ts +275 -44
  520. package/src/runtime/routes/__tests__/llm-call-sites-routes.test.ts +12 -0
  521. package/src/runtime/routes/__tests__/question-routes.test.ts +395 -0
  522. package/src/runtime/routes/__tests__/tts-routes.test.ts +64 -1
  523. package/src/runtime/routes/acp-routes-list.test.ts +143 -0
  524. package/src/runtime/routes/acp-routes.ts +5 -3
  525. package/src/runtime/routes/auth-routes.ts +1 -1
  526. package/src/runtime/routes/bookmark-routes.ts +5 -3
  527. package/src/runtime/routes/btw-routes.ts +5 -1
  528. package/src/runtime/routes/channel-availability-routes.ts +126 -0
  529. package/src/runtime/routes/consolidation-routes.ts +100 -0
  530. package/src/runtime/routes/conversation-cli-routes.ts +44 -3
  531. package/src/runtime/routes/conversation-list-routes.ts +3 -20
  532. package/src/runtime/routes/conversation-management-routes.ts +17 -42
  533. package/src/runtime/routes/conversation-query-routes.ts +99 -35
  534. package/src/runtime/routes/conversation-routes.ts +97 -11
  535. package/src/runtime/routes/documents-routes.ts +25 -86
  536. package/src/runtime/routes/group-routes.ts +5 -0
  537. package/src/runtime/routes/inbound-conversation.ts +28 -8
  538. package/src/runtime/routes/inbound-message-handler.ts +236 -41
  539. package/src/runtime/routes/inbound-stages/background-dispatch.test.ts +111 -0
  540. package/src/runtime/routes/inbound-stages/background-dispatch.ts +32 -1
  541. package/src/runtime/routes/inbound-stages/edit-intercept.ts +17 -4
  542. package/src/runtime/routes/index.ts +8 -0
  543. package/src/runtime/routes/inference-profile-session-handler.ts +17 -44
  544. package/src/runtime/routes/inference-profile-session-reaper.ts +7 -21
  545. package/src/runtime/routes/inference-provider-connection-routes.ts +199 -22
  546. package/src/runtime/routes/integrations/a2a.ts +235 -0
  547. package/src/runtime/routes/integrations/slack/share.ts +4 -52
  548. package/src/runtime/routes/integrations/slack/token.ts +43 -0
  549. package/src/runtime/routes/integrations/twilio.ts +6 -13
  550. package/src/runtime/routes/llm-call-sites-routes.ts +11 -1
  551. package/src/runtime/routes/notification-routes.ts +1 -1
  552. package/src/runtime/routes/oauth-commands-routes.ts +105 -15
  553. package/src/runtime/routes/oauth-lifecycle-routes.ts +43 -0
  554. package/src/runtime/routes/question-routes.ts +259 -0
  555. package/src/runtime/routes/rename-conversation-routes.ts +2 -33
  556. package/src/runtime/routes/schedule-routes.ts +4 -7
  557. package/src/runtime/routes/subagents-routes.ts +98 -18
  558. package/src/runtime/routes/telemetry-routes.ts +27 -0
  559. package/src/runtime/routes/tts-routes.ts +27 -2
  560. package/src/runtime/routes/workspace-routes.test.ts +43 -0
  561. package/src/runtime/routes/workspace-routes.ts +28 -0
  562. package/src/runtime/services/conversation-serializer.ts +39 -7
  563. package/src/runtime/sync/resource-sync-events.ts +93 -1
  564. package/src/schedule/schedule-store.ts +27 -2
  565. package/src/schedule/scheduler.ts +9 -1
  566. package/src/security/__tests__/untrusted-content.test.ts +86 -0
  567. package/src/security/untrusted-content.ts +93 -8
  568. package/src/skills/catalog-files.ts +1 -1
  569. package/src/skills/catalog-install.ts +233 -116
  570. package/src/skills/clawhub.ts +70 -13
  571. package/src/skills/managed-store.ts +4 -119
  572. package/src/skills/skillssh-registry.ts +27 -48
  573. package/src/subagent/manager.ts +17 -7
  574. package/src/telemetry/types.ts +113 -1
  575. package/src/telemetry/usage-telemetry-reporter.test.ts +312 -5
  576. package/src/telemetry/usage-telemetry-reporter.ts +113 -7
  577. package/src/tools/apps/executors.ts +58 -7
  578. package/src/tools/ask-question/ask-question-tool.test.ts +509 -0
  579. package/src/tools/ask-question/ask-question-tool.ts +304 -0
  580. package/src/tools/browser/browser-execution.ts +15 -11
  581. package/src/tools/computer-use/definitions.ts +3 -3
  582. package/src/tools/credentials/vault.ts +1 -1
  583. package/src/tools/document/document-tool.ts +124 -1
  584. package/src/tools/filesystem/edit.ts +1 -1
  585. package/src/tools/filesystem/list.ts +1 -1
  586. package/src/tools/filesystem/read.ts +1 -1
  587. package/src/tools/filesystem/write.ts +5 -2
  588. package/src/tools/host-filesystem/transfer.ts +1 -1
  589. package/src/tools/host-terminal/host-shell.ts +1 -1
  590. package/src/tools/memory/register.ts +1 -9
  591. package/src/tools/permission-checker.ts +1 -1
  592. package/src/tools/registry.ts +17 -7
  593. package/src/tools/schedule/create.ts +2 -2
  594. package/src/tools/schema-transforms.ts +7 -2
  595. package/src/tools/side-effects.ts +1 -0
  596. package/src/tools/skills/delete-managed.ts +4 -4
  597. package/src/tools/skills/execute.ts +1 -1
  598. package/src/tools/skills/scaffold-managed.ts +3 -2
  599. package/src/tools/subagent/notify-parent.ts +1 -1
  600. package/src/tools/system/request-permission.ts +2 -2
  601. package/src/tools/terminal/safe-env.ts +60 -1
  602. package/src/tools/tool-manifest.ts +2 -0
  603. package/src/tools/types.ts +107 -21
  604. package/src/tools/ui-surface/definitions.ts +6 -5
  605. package/src/tts/__tests__/provider-adapters.test.ts +76 -2
  606. package/src/tts/providers/elevenlabs-provider.ts +75 -1
  607. package/src/types/onboarding-context.ts +2 -0
  608. package/src/util/errors.ts +17 -0
  609. package/src/util/platform.ts +10 -0
  610. package/src/watcher/__tests__/engine.test.ts +22 -0
  611. package/src/watcher/engine.ts +6 -2
  612. package/src/workspace/migrations/057-repair-stale-gemini-model-ids.ts +80 -15
  613. package/src/workspace/migrations/072-seed-reply-suggestion-callsite.ts +35 -22
  614. package/src/workspace/migrations/073-repair-recall-callsite-empty-profile.ts +3 -1
  615. package/src/workspace/migrations/083-system-prompt-prefix-to-file.ts +191 -0
  616. package/src/workspace/migrations/084-remove-legacy-skills-index.ts +276 -0
  617. package/src/workspace/migrations/085-memory-v2-bm25-b-reembed-disabled-v2-pages.ts +137 -0
  618. package/src/workspace/migrations/086-revert-stale-gemini-mis-rewrites.ts +198 -0
  619. package/src/workspace/migrations/087-memory-router-balanced-profile.ts +91 -0
  620. package/src/workspace/migrations/registry.ts +10 -0
  621. package/src/workspace/migrations/runner.ts +39 -9
  622. package/src/workspace/migrations/types.ts +4 -0
  623. package/examples/plugins/echo/bun.lock +0 -25
  624. package/src/__tests__/context-window-manager.test.ts +0 -2481
  625. package/src/__tests__/guardian-action-conversation-turn.test.ts +0 -441
  626. package/src/context/__tests__/compact-prompt.test.ts +0 -63
  627. package/src/context/prompts/compact.md +0 -26
  628. package/src/memory/graph/__tests__/remember-description.test.ts +0 -55
  629. package/src/prompts/__tests__/build-cli-reference-section.test.ts +0 -37
  630. package/src/runtime/guardian-action-conversation-turn.ts +0 -99
@@ -70,8 +70,6 @@ describe("loadExternalPlugin — manifest", () => {
70
70
  );
71
71
  expect(registered).toBeDefined();
72
72
  expect(registered?.manifest.version).toBe("1.2.3");
73
- // Defaults to pluginRuntime v1 when no `vellum.requires` is set.
74
- expect(registered?.manifest.requires).toEqual({ pluginRuntime: "v1" });
75
73
  });
76
74
 
77
75
  test("strips npm scope from name", async () => {
@@ -97,56 +95,84 @@ describe("loadExternalPlugin — manifest", () => {
97
95
  );
98
96
  expect(registered?.manifest.version).toBe("0.0.0");
99
97
  });
98
+ });
100
99
 
101
- test("reads vellum.requires from package.json when present", async () => {
102
- // Future direction (out of scope this PR): `requires` may carry
103
- // `assistantVersion` alongside `pluginRuntime`. For today the registry
104
- // validates against `ASSISTANT_API_VERSIONS` and only `pluginRuntime`
105
- // is a known capability — so the loader's pass-through behavior is
106
- // exercised with the keys the registry already accepts. The loader is
107
- // opaque to the shape; additional keys propagate identically.
108
- const dir = freshPluginDir("custom-requires");
100
+ describe("loadExternalPlugin plugin-api peerDependency", () => {
101
+ // Tests anchor against assistantPkg.version (read from the assistant's
102
+ // own package.json) so the matrix below stays correct across version
103
+ // bumps. Constructing a range from the live version + nudging up/down
104
+ // by one keeps the satisfy/un-satisfy cases honest.
105
+ test("loads when peerDependency range satisfies assistant version", async () => {
106
+ const dir = freshPluginDir("compat-ok");
109
107
  writePackageJson(dir, {
110
- name: "custom-requires",
108
+ name: "compat-ok",
111
109
  version: "0.1.0",
112
- vellum: { requires: { pluginRuntime: "v1" } },
110
+ peerDependencies: { "@vellumai/plugin-api": "*" },
113
111
  });
114
112
 
115
113
  await loadExternalPlugin(dir);
116
114
 
117
- const registered = getRegisteredPlugins().find(
118
- (p) => p.manifest.name === "custom-requires",
119
- );
120
- expect(registered?.manifest.requires).toEqual({ pluginRuntime: "v1" });
115
+ expect(registeredNames()).toContain("compat-ok");
121
116
  });
122
117
 
123
- test("vellum.requires overrides the default empty requires fails registration", async () => {
124
- // Belt-and-suspenders proof that the loader reads `vellum.requires`
125
- // rather than always defaulting: an empty `vellum.requires` propagates
126
- // through, the registry rejects the plugin (missing pluginRuntime),
127
- // and the registry stays empty.
128
- const dir = freshPluginDir("empty-requires");
118
+ test("loads plugin whose peerDependency range excludes assistant version (logs error)", async () => {
119
+ // The host-compat gate is soft while the installation flow is in
120
+ // flux an unsatisfied range produces a `log.error` but the
121
+ // plugin still loads. Once installation settles, this case should
122
+ // harden back into a hard reject.
123
+ const dir = freshPluginDir("compat-bad");
129
124
  writePackageJson(dir, {
130
- name: "empty-requires",
125
+ name: "compat-bad",
131
126
  version: "0.1.0",
132
- vellum: { requires: {} },
127
+ // A range that no real assistant version will satisfy.
128
+ peerDependencies: { "@vellumai/plugin-api": ">=999.0.0" },
133
129
  });
134
130
 
135
131
  await loadExternalPlugin(dir);
136
132
 
137
- expect(registeredNames()).not.toContain("empty-requires");
133
+ expect(registeredNames()).toContain("compat-bad");
138
134
  });
139
135
 
140
- test("does not synthesize a provides field", async () => {
141
- const dir = freshPluginDir("no-provides");
142
- writePackageJson(dir, { name: "no-provides", version: "0.1.0" });
136
+ test("loads plugin whose peerDependency range is unparseable (logs error)", async () => {
137
+ // Same soft-gate rationale as the excluded-range case above.
138
+ const dir = freshPluginDir("compat-bogus");
139
+ writePackageJson(dir, {
140
+ name: "compat-bogus",
141
+ version: "0.1.0",
142
+ peerDependencies: { "@vellumai/plugin-api": "not-a-real-range" },
143
+ });
143
144
 
144
145
  await loadExternalPlugin(dir);
145
146
 
146
- const registered = getRegisteredPlugins().find(
147
- (p) => p.manifest.name === "no-provides",
148
- );
149
- expect(registered?.manifest.provides).toBeUndefined();
147
+ expect(registeredNames()).toContain("compat-bogus");
148
+ });
149
+
150
+ test("loads with warning when no peerDependency on plugin-api is declared", async () => {
151
+ // Absent peerDep is non-fatal — the loader logs a warn and proceeds
152
+ // with no host-compat claim. The convention is opt-in while the
153
+ // plugin-api framework is experimental.
154
+ const dir = freshPluginDir("compat-absent");
155
+ writePackageJson(dir, {
156
+ name: "compat-absent",
157
+ version: "0.1.0",
158
+ });
159
+
160
+ await loadExternalPlugin(dir);
161
+
162
+ expect(registeredNames()).toContain("compat-absent");
163
+ });
164
+
165
+ test("loads with warning when peerDependencies is present but lacks plugin-api key", async () => {
166
+ const dir = freshPluginDir("compat-other-peer");
167
+ writePackageJson(dir, {
168
+ name: "compat-other-peer",
169
+ version: "0.1.0",
170
+ peerDependencies: { react: "^18.0.0" },
171
+ });
172
+
173
+ await loadExternalPlugin(dir);
174
+
175
+ expect(registeredNames()).toContain("compat-other-peer");
150
176
  });
151
177
 
152
178
  test("malformed package.json is logged and skipped (registry untouched)", async () => {
@@ -197,9 +223,9 @@ describe("loadExternalPlugin — hooks", () => {
197
223
  );
198
224
  expect(typeof registered?.hooks?.init).toBe("function");
199
225
  await registered?.hooks?.init?.({} as never);
200
- expect(
201
- (globalThis as Record<string, unknown>).__externalInitCalled,
202
- ).toBe(true);
226
+ expect((globalThis as Record<string, unknown>).__externalInitCalled).toBe(
227
+ true,
228
+ );
203
229
  delete (globalThis as Record<string, unknown>).__externalInitCalled;
204
230
  });
205
231
 
@@ -264,9 +290,9 @@ describe("loadExternalPlugin — hooks", () => {
264
290
  expect(Object.keys(registered?.hooks ?? {})).toEqual(["init"]);
265
291
  expect(typeof registered?.hooks?.init).toBe("function");
266
292
  await registered?.hooks?.init?.({} as never);
267
- expect((globalThis as Record<string, unknown>).__externalDtsInitCalled).toBe(
268
- true,
269
- );
293
+ expect(
294
+ (globalThis as Record<string, unknown>).__externalDtsInitCalled,
295
+ ).toBe(true);
270
296
  delete (globalThis as Record<string, unknown>).__externalDtsInitCalled;
271
297
  });
272
298
 
@@ -319,11 +345,9 @@ describe("loadExternalPlugin — tools", () => {
319
345
  dir,
320
346
  "tools/alpha.ts",
321
347
  `export default {
322
- name: "two_tools_alpha",
323
348
  description: "alpha",
324
- category: "plugin",
325
349
  defaultRiskLevel: "low" as const,
326
- getDefinition() { return { name: "two_tools_alpha", description: "alpha", input_schema: { type: "object", properties: {}, required: [] } }; },
350
+ input_schema: { type: "object", properties: {}, required: [] },
327
351
  async execute() { return { content: "a", isError: false }; },
328
352
  };
329
353
  `,
@@ -332,11 +356,9 @@ describe("loadExternalPlugin — tools", () => {
332
356
  dir,
333
357
  "tools/beta.ts",
334
358
  `export default {
335
- name: "two_tools_beta",
336
359
  description: "beta",
337
- category: "plugin",
338
360
  defaultRiskLevel: "low" as const,
339
- getDefinition() { return { name: "two_tools_beta", description: "beta", input_schema: { type: "object", properties: {}, required: [] } }; },
361
+ input_schema: { type: "object", properties: {}, required: [] },
340
362
  async execute() { return { content: "b", isError: false }; },
341
363
  };
342
364
  `,
@@ -350,7 +372,7 @@ describe("loadExternalPlugin — tools", () => {
350
372
  const names = (registered?.tools ?? []).map(
351
373
  (t) => (t as { name: string }).name,
352
374
  );
353
- expect(names).toEqual(["two_tools_alpha", "two_tools_beta"]);
375
+ expect(names).toEqual(["alpha", "beta"]);
354
376
  });
355
377
 
356
378
  test("plugin.tools is undefined when tools/ is absent", async () => {
@@ -379,18 +401,95 @@ describe("loadExternalPlugin — tools", () => {
379
401
  expect(registeredNames()).toHaveLength(0);
380
402
  });
381
403
 
382
- test("a tool default export missing string name is logged and skipped", async () => {
383
- const dir = freshPluginDir("tool-no-name");
384
- writePackageJson(dir, { name: "tool-no-name", version: "0.1.0" });
404
+ test("a tool default export with missing fields loads with documented defaults", async () => {
405
+ const dir = freshPluginDir("tool-with-defaults");
406
+ writePackageJson(dir, { name: "tool-with-defaults", version: "0.1.0" });
407
+ // The default export is a bare empty object — no description,
408
+ // defaultRiskLevel, input_schema, or execute. The loader must fill
409
+ // each slot with its documented default and still register the plugin.
410
+ writeSurfaceFile(dir, "tools/empty.ts", `export default {};\n`);
411
+
412
+ await loadExternalPlugin(dir);
413
+
414
+ const registered = getRegisteredPlugins().find(
415
+ (p) => p.manifest.name === "tool-with-defaults",
416
+ );
417
+ expect(registered).toBeDefined();
418
+ const tools = (registered?.tools ?? []) as Array<{
419
+ name: string;
420
+ description: string;
421
+ defaultRiskLevel: string;
422
+ input_schema: Record<string, unknown>;
423
+ execute: (
424
+ input: Record<string, unknown>,
425
+ context: unknown,
426
+ ) => Promise<{ content: string; isError: boolean }>;
427
+ }>;
428
+ expect(tools).toHaveLength(1);
429
+ const empty = tools[0]!;
430
+ expect(empty.name).toBe("empty");
431
+ expect(empty.description).toBe("");
432
+ expect(empty.defaultRiskLevel).toBe("medium");
433
+ expect(empty.input_schema).toEqual({
434
+ type: "object",
435
+ properties: {},
436
+ additionalProperties: false,
437
+ });
438
+ expect(typeof empty.execute).toBe("function");
439
+
440
+ const result = await empty.execute({}, {} as unknown);
441
+ expect(result.isError).toBe(true);
442
+ expect(result.content).toContain("empty");
443
+ expect(result.content).toContain("no execute implementation");
444
+ });
445
+
446
+ test("a partial tool default export merges author fields with defaults", async () => {
447
+ const dir = freshPluginDir("tool-partial-defaults");
448
+ writePackageJson(dir, {
449
+ name: "tool-partial-defaults",
450
+ version: "0.1.0",
451
+ });
452
+ // Author supplies only description + execute; the loader must default
453
+ // defaultRiskLevel and input_schema while keeping the author's fields.
385
454
  writeSurfaceFile(
386
455
  dir,
387
- "tools/nameless.ts",
388
- `export default { description: "missing name" };\n`,
456
+ "tools/partial.ts",
457
+ `export default {
458
+ description: "custom description",
459
+ async execute() {
460
+ return { content: "ran", isError: false };
461
+ },
462
+ };
463
+ `,
389
464
  );
390
465
 
391
466
  await loadExternalPlugin(dir);
392
467
 
393
- expect(registeredNames()).toHaveLength(0);
468
+ const registered = getRegisteredPlugins().find(
469
+ (p) => p.manifest.name === "tool-partial-defaults",
470
+ );
471
+ const tool = (registered?.tools ?? [])[0] as
472
+ | {
473
+ description: string;
474
+ defaultRiskLevel: string;
475
+ input_schema: object;
476
+ execute: (
477
+ input: Record<string, unknown>,
478
+ context: unknown,
479
+ ) => Promise<{ content: string; isError: boolean }>;
480
+ }
481
+ | undefined;
482
+ expect(tool).toBeDefined();
483
+ expect(tool?.description).toBe("custom description");
484
+ expect(tool?.defaultRiskLevel).toBe("medium");
485
+ expect(tool?.input_schema).toEqual({
486
+ type: "object",
487
+ properties: {},
488
+ additionalProperties: false,
489
+ });
490
+ const result = await tool!.execute({}, {} as unknown);
491
+ expect(result.isError).toBe(false);
492
+ expect(result.content).toBe("ran");
394
493
  });
395
494
  });
396
495
 
@@ -450,9 +549,6 @@ describe("loadExternalPlugin — end-to-end @vellumai/simple-memory", () => {
450
549
  const toolNames = (registered?.tools ?? [])
451
550
  .map((t) => (t as { name: string }).name)
452
551
  .sort();
453
- expect(toolNames).toEqual([
454
- "simple_memory_recall",
455
- "simple_memory_remember",
456
- ]);
552
+ expect(toolNames).toEqual(["recall", "remember"]);
457
553
  });
458
554
  });
@@ -303,6 +303,146 @@ describe("FilingService", () => {
303
303
  expect(processMessageCalls).toHaveLength(1);
304
304
  });
305
305
 
306
+ // Helpers for the compaction-retry tests: hold the filing run open by
307
+ // making processMessage return a manually-resolved promise, so `activeRun`
308
+ // stays set and runCompactionOnce() sees the contention path.
309
+ function holdFilingRun(): {
310
+ release: () => void;
311
+ filingCalls: () => number;
312
+ compactionCalls: () => number;
313
+ waitForFilingStarted: () => Promise<void>;
314
+ } {
315
+ let release: (() => void) | undefined;
316
+ let started = false;
317
+ let filingCalls = 0;
318
+ let compactionCalls = 0;
319
+
320
+ setTestProcessMessage((...args: unknown[]) => {
321
+ const callSite = (args[3] as { callSite?: string } | undefined)
322
+ ?.callSite;
323
+ if (callSite === "filingAgent") {
324
+ filingCalls += 1;
325
+ started = true;
326
+ return new Promise((resolve) => {
327
+ release = () => resolve({ messageId: "filing-done" });
328
+ });
329
+ }
330
+ if (callSite === "compactionAgent") {
331
+ compactionCalls += 1;
332
+ }
333
+ return Promise.resolve({ messageId: "mock" });
334
+ });
335
+
336
+ return {
337
+ release: () => release?.(),
338
+ filingCalls: () => filingCalls,
339
+ compactionCalls: () => compactionCalls,
340
+ waitForFilingStarted: async () => {
341
+ while (!started) await Promise.resolve();
342
+ },
343
+ };
344
+ }
345
+
346
+ test("schedules a near-term retry when filing run is in-flight", async () => {
347
+ const hold = holdFilingRun();
348
+ // 5s retry override paired with a production-realistic 24h compaction
349
+ // interval — the assertion proves retry << interval.
350
+ const retryMs = 5_000;
351
+ mockConfig.filing.compactionIntervalMs = 24 * 60 * 60 * 1000;
352
+ const service = new FilingService({
353
+ compactionContendedRetryMs: retryMs,
354
+ });
355
+ const filingPromise = service.runOnce();
356
+ await hold.waitForFilingStarted();
357
+
358
+ const beforeRetry = Date.now();
359
+ const ran = await service.runCompactionOnce();
360
+
361
+ expect(ran).toBe(false);
362
+ expect(service.nextCompactionAt).not.toBeNull();
363
+ const nextAt = service.nextCompactionAt!;
364
+ expect(nextAt - beforeRetry).toBeLessThan(
365
+ mockConfig.filing.compactionIntervalMs,
366
+ );
367
+ expect(nextAt - beforeRetry).toBeLessThanOrEqual(retryMs + 100);
368
+
369
+ hold.release();
370
+ await filingPromise;
371
+ await service.stop();
372
+ });
373
+
374
+ test("retry fires after filing run completes", async () => {
375
+ const hold = holdFilingRun();
376
+ const service = new FilingService({ compactionContendedRetryMs: 1 });
377
+ const filingPromise = service.runOnce();
378
+ await hold.waitForFilingStarted();
379
+
380
+ const skipped = await service.runCompactionOnce();
381
+ expect(skipped).toBe(false);
382
+ expect(hold.compactionCalls()).toBe(0);
383
+
384
+ hold.release();
385
+ await filingPromise;
386
+
387
+ const start = Date.now();
388
+ while (hold.compactionCalls() === 0 && Date.now() - start < 1000) {
389
+ await new Promise((resolve) => setTimeout(resolve, 5));
390
+ }
391
+
392
+ expect(hold.filingCalls()).toBe(1);
393
+ expect(hold.compactionCalls()).toBe(1);
394
+
395
+ await service.stop();
396
+ });
397
+
398
+ test("stop() clears a scheduled compaction retry", async () => {
399
+ const hold = holdFilingRun();
400
+ const service = new FilingService({ compactionContendedRetryMs: 50 });
401
+ const filingPromise = service.runOnce();
402
+ await hold.waitForFilingStarted();
403
+ await service.runCompactionOnce();
404
+ expect(service.nextCompactionAt).not.toBeNull();
405
+
406
+ hold.release();
407
+ await filingPromise;
408
+ await service.stop();
409
+
410
+ // After stop, the retry timer must be cleared and never fire.
411
+ expect(service.nextCompactionAt).toBeNull();
412
+ await new Promise((resolve) => setTimeout(resolve, 100));
413
+ expect(hold.compactionCalls()).toBe(0);
414
+ });
415
+
416
+ test("stop() prevents retry callback from re-arming a fresh timer", async () => {
417
+ // Race: the retry callback fires while filing is still in-flight and
418
+ // stop() has begun. The callback already cleared compactionRetryTimer,
419
+ // so clearCompactionRetry is a no-op. Without a stopped flag, the
420
+ // callback's runCompactionOnce() hits the activeRun branch and schedules
421
+ // a fresh retry, leaving a live timer after stop() resolves.
422
+ const hold = holdFilingRun();
423
+ const service = new FilingService({ compactionContendedRetryMs: 5 });
424
+ const filingPromise = service.runOnce();
425
+ await hold.waitForFilingStarted();
426
+ await service.runCompactionOnce();
427
+ expect(service.nextCompactionAt).not.toBeNull();
428
+
429
+ // Begin stop without awaiting — it would block on the held filing run.
430
+ // stop() flips `stopped` synchronously before the retry timer fires.
431
+ const stopPromise = service.stop();
432
+
433
+ // Wait past the retry delay. Without the guard, the callback would call
434
+ // runCompactionOnce(), observe activeRun, and re-arm a new retry.
435
+ await new Promise((resolve) => setTimeout(resolve, 50));
436
+ expect(service.nextCompactionAt).toBeNull();
437
+
438
+ hold.release();
439
+ await filingPromise;
440
+ await stopPromise;
441
+
442
+ expect(service.nextCompactionAt).toBeNull();
443
+ expect(hold.compactionCalls()).toBe(0);
444
+ });
445
+
306
446
  test("respects active hours", async () => {
307
447
  mockConfig.filing.activeHoursStart = 9;
308
448
  mockConfig.filing.activeHoursEnd = 17;
@@ -150,13 +150,11 @@ mock.module("../skills/clawhub-files.js", () => ({
150
150
 
151
151
  mock.module("../skills/catalog-install.js", () => ({
152
152
  installSkillLocally: async () => {},
153
- upsertSkillsIndex: () => {},
154
153
  }));
155
154
 
156
155
  mock.module("../skills/managed-store.js", () => ({
157
156
  createManagedSkill: () => ({ created: true }),
158
157
  deleteManagedSkill: () => ({ deleted: true }),
159
- removeSkillsIndexEntry: () => {},
160
158
  validateManagedSkillId: () => null,
161
159
  }));
162
160
 
@@ -188,8 +186,6 @@ import { getSkill } from "../daemon/handlers/skills.js";
188
186
  // Helpers
189
187
  // ---------------------------------------------------------------------------
190
188
 
191
-
192
-
193
189
  // ---------------------------------------------------------------------------
194
190
  // Tests
195
191
  // ---------------------------------------------------------------------------
@@ -20,7 +20,6 @@ const SCANNED_FILES = [
20
20
  "calls/call-controller.ts",
21
21
  "calls/guardian-action-sweep.ts",
22
22
  "runtime/routes/inbound-message-handler.ts",
23
- "runtime/guardian-action-conversation-turn.ts",
24
23
  "daemon/conversation-process.ts",
25
24
  ];
26
25
 
@@ -295,6 +295,7 @@ describe("guardian-dispatch", () => {
295
295
  conversationId: "conv-from-thread-created",
296
296
  title: "Guardian alert",
297
297
  sourceEventName: "guardian.question",
298
+ silent: false,
298
299
  };
299
300
  mockEmitResult = {
300
301
  signalId: "sig-4",
@@ -3,18 +3,13 @@
3
3
  * `assistant/src/daemon/handlers/skills.ts`.
4
4
  *
5
5
  * One representative call site (the `installSkill` bundled branch) is
6
- * exercised all 5 sites share the same delegation to
7
- * `maybeSeedMemoryV2Skills`, so a single suite covers behavior. Validates:
8
- * - config on helper invoked after seedSkillGraphNodes and the seed
9
- * observed (callOrder picks up "v2")
10
- * - config off → helper still invoked, but the seed short-circuits
6
+ * exercised; all handler seed sites share the same delegation to
7
+ * `refreshSkillCapabilityMemories`, so a single suite covers behavior. Validates:
8
+ * - handler invokes the centralized refresh helper with the live config.
11
9
  *
12
- * The handler delegates to `maybeSeedMemoryV2Skills` from
13
- * `daemon/memory-v2-startup.ts`. We mock that module directly so the test
14
- * does not have to drain the dynamic-import microtask chain. The helper's
15
- * gate semantics are covered by `lifecycle-memory-v2-seed.test.ts`; here
16
- * we only verify that the handler invokes the helper synchronously with
17
- * the live config.
10
+ * The helper's gate semantics (flag + config + rejection swallowing) are
11
+ * covered by `lifecycle-memory-v2-seed.test.ts`; here we only verify that the
12
+ * handler delegates to the centralized refresh path synchronously.
18
13
  */
19
14
  import { beforeEach, describe, expect, mock, test } from "bun:test";
20
15
 
@@ -22,16 +17,9 @@ import { beforeEach, describe, expect, mock, test } from "bun:test";
22
17
  // Programmable test state
23
18
  // ---------------------------------------------------------------------------
24
19
 
25
- const flagsState = { configV2Enabled: true };
20
+ const configState = { v2Enabled: true };
26
21
 
27
- const callOrder: string[] = [];
28
-
29
- const mockSeedSkillGraphNodes = mock(() => {
30
- callOrder.push("v1");
31
- });
32
- // Body installed in `beforeEach` so each test sees a fresh implementation
33
- // that closes over the up-to-date `flagsState`.
34
- const mockMaybeSeedMemoryV2Skills = mock(
22
+ const mockRefreshSkillCapabilityMemories = mock(
35
23
  (_config: { memory: { v2: { enabled: boolean } } }) => {},
36
24
  );
37
25
 
@@ -53,10 +41,6 @@ mock.module("../config/skills.js", () => ({
53
41
  ],
54
42
  }));
55
43
 
56
- mock.module("../config/assistant-feature-flags.js", () => ({
57
- isAssistantFeatureFlagEnabled: () => true,
58
- }));
59
-
60
44
  // Stub both `getConfig` and `loadConfig`. `loadConfig` is reached by code
61
45
  // paths transitively imported during teardown (e.g. dynamic imports inside
62
46
  // `oauth2.ts`); leaving it undefined here would break sibling test files
@@ -68,13 +52,13 @@ mock.module("../config/loader.js", () => ({
68
52
  deepMergeOverwrite: (a: unknown) => a,
69
53
  mergeDefaultWorkspaceConfig: () => {},
70
54
  getConfig: () => ({
71
- memory: { v2: { enabled: flagsState.configV2Enabled } },
55
+ memory: { v2: { enabled: configState.v2Enabled } },
72
56
  }),
73
57
  getConfigReadOnly: () => ({
74
- memory: { v2: { enabled: flagsState.configV2Enabled } },
58
+ memory: { v2: { enabled: configState.v2Enabled } },
75
59
  }),
76
60
  loadConfig: () => ({
77
- memory: { v2: { enabled: flagsState.configV2Enabled } },
61
+ memory: { v2: { enabled: configState.v2Enabled } },
78
62
  }),
79
63
  invalidateConfigCache: () => {},
80
64
  loadRawConfig: () => ({}),
@@ -147,9 +131,11 @@ mock.module("../skills/catalog-cache.js", () => ({
147
131
  }));
148
132
 
149
133
  mock.module("../skills/catalog-install.js", () => ({
150
- installSkillLocally: async () => {},
151
- upsertSkillsIndex: () => {},
134
+ commitStagedSkillInstall: () => {},
135
+ createSkillInstallStagingDir: () => "/tmp/test-skills/.install-staging/test",
152
136
  getRepoSkillsDir: () => undefined,
137
+ installSkillDependenciesIfPresent: () => {},
138
+ installSkillLocally: async () => {},
153
139
  }));
154
140
 
155
141
  mock.module("../skills/catalog-search.js", () => ({
@@ -159,23 +145,15 @@ mock.module("../skills/catalog-search.js", () => ({
159
145
  mock.module("../skills/managed-store.js", () => ({
160
146
  createManagedSkill: () => ({ created: true }),
161
147
  deleteManagedSkill: () => ({ deleted: true }),
162
- removeSkillsIndexEntry: () => {},
163
148
  validateManagedSkillId: () => null,
164
149
  }));
165
150
 
166
151
  mock.module("../memory/graph/capability-seed.js", () => ({
167
152
  deleteSkillCapabilityNode: () => {},
168
- seedSkillGraphNodes: mockSeedSkillGraphNodes,
169
- seedUninstalledCatalogSkillMemories: async () => {},
170
- }));
171
-
172
- mock.module("../memory/v2/skill-store.js", () => ({
173
- seedV2SkillEntries: mock(async () => {}),
174
- getSkillCapability: () => null,
175
153
  }));
176
154
 
177
- mock.module("../daemon/memory-v2-startup.js", () => ({
178
- maybeSeedMemoryV2Skills: mockMaybeSeedMemoryV2Skills,
155
+ mock.module("../daemon/skill-memory-refresh.js", () => ({
156
+ refreshSkillCapabilityMemories: mockRefreshSkillCapabilityMemories,
179
157
  }));
180
158
 
181
159
  mock.module("../util/platform.js", () => ({
@@ -204,45 +182,48 @@ mock.module("../daemon/config-watcher.js", () => ({
204
182
  }));
205
183
 
206
184
  // Import after mocking
207
- const { installSkill } = await import("../daemon/handlers/skills.js");
208
-
209
- // ---------------------------------------------------------------------------
210
- // Helpers
211
- // ---------------------------------------------------------------------------
185
+ const { installSkill, uninstallSkill } =
186
+ await import("../daemon/handlers/skills.js");
212
187
 
213
188
  // ---------------------------------------------------------------------------
214
189
  // Tests
215
190
  // ---------------------------------------------------------------------------
216
191
 
217
- describe("v2 skill re-seed gating in skill handlers", () => {
192
+ describe("v2 skill refresh delegation in skill handlers", () => {
218
193
  beforeEach(() => {
219
- flagsState.configV2Enabled = true;
220
- callOrder.length = 0;
221
- mockSeedSkillGraphNodes.mockClear();
222
- mockMaybeSeedMemoryV2Skills.mockClear();
223
- mockMaybeSeedMemoryV2Skills.mockImplementation((config) => {
224
- if (!config.memory.v2.enabled) return;
225
- callOrder.push("v2");
226
- });
194
+ configState.v2Enabled = true;
195
+ mockRefreshSkillCapabilityMemories.mockClear();
227
196
  });
228
197
 
229
- test("config on maybeSeedMemoryV2Skills invoked after seedSkillGraphNodes", async () => {
198
+ test("enabled config → refresh helper invoked with live config", async () => {
230
199
  const result = await installSkill({ slug: "bundled-skill" });
231
200
 
232
201
  expect(result.success).toBe(true);
233
- expect(mockSeedSkillGraphNodes).toHaveBeenCalledTimes(1);
234
- expect(mockMaybeSeedMemoryV2Skills).toHaveBeenCalledTimes(1);
235
- expect(callOrder).toEqual(["v1", "v2"]);
202
+ expect(mockRefreshSkillCapabilityMemories).toHaveBeenCalledTimes(1);
203
+ expect(mockRefreshSkillCapabilityMemories.mock.calls[0]?.[0]).toEqual({
204
+ memory: { v2: { enabled: true } },
205
+ });
236
206
  });
237
207
 
238
- test("config.memory.v2.enabled off → seed mock observes config and skips", async () => {
239
- flagsState.configV2Enabled = false;
208
+ test("config.memory.v2.enabled off → helper receives disabled config", async () => {
209
+ configState.v2Enabled = false;
240
210
 
241
211
  const result = await installSkill({ slug: "bundled-skill" });
242
212
 
243
213
  expect(result.success).toBe(true);
244
- expect(mockSeedSkillGraphNodes).toHaveBeenCalledTimes(1);
245
- expect(mockMaybeSeedMemoryV2Skills).toHaveBeenCalledTimes(1);
246
- expect(callOrder).toEqual(["v1"]);
214
+ expect(mockRefreshSkillCapabilityMemories).toHaveBeenCalledTimes(1);
215
+ expect(mockRefreshSkillCapabilityMemories.mock.calls[0]?.[0]).toEqual({
216
+ memory: { v2: { enabled: false } },
217
+ });
218
+ });
219
+
220
+ test("uninstall delegates to refresh helper", async () => {
221
+ const result = await uninstallSkill("managed-skill");
222
+
223
+ expect(result.success).toBe(true);
224
+ expect(mockRefreshSkillCapabilityMemories).toHaveBeenCalledTimes(1);
225
+ expect(mockRefreshSkillCapabilityMemories.mock.calls[0]?.[0]).toEqual({
226
+ memory: { v2: { enabled: true } },
227
+ });
247
228
  });
248
229
  });