@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
@@ -1,8 +1,9 @@
1
- import { existsSync, mkdirSync, readFileSync } from "node:fs";
1
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
2
2
  import { join } from "node:path";
3
3
  import { beforeEach, describe, expect, mock, test } from "bun:test";
4
4
 
5
5
  let TEST_DIR = "";
6
+ const seedUpsertSlugs: string[] = [];
6
7
 
7
8
  const mockConfig = {
8
9
  provider: "anthropic",
@@ -32,6 +33,10 @@ const mockConfig = {
32
33
  },
33
34
  "web-search": { mode: "your-own", provider: "inference-provider-native" },
34
35
  },
36
+ skills: {
37
+ entries: {},
38
+ allowBundled: [],
39
+ },
35
40
  };
36
41
 
37
42
  mock.module("../util/logger.js", () => ({
@@ -51,7 +56,36 @@ mock.module("../config/loader.js", () => ({
51
56
  setNestedValue: () => {},
52
57
  }));
53
58
 
59
+ mock.module("../skills/catalog-cache.js", () => ({
60
+ getCatalog: async () => [],
61
+ }));
62
+
63
+ mock.module("../memory/embedding-backend.js", () => ({
64
+ embedWithBackend: async (_config: unknown, inputs: unknown[]) => ({
65
+ provider: "local",
66
+ model: "test-model",
67
+ vectors: inputs.map(() => [0.1, 0.2, 0.3]),
68
+ }),
69
+ generateSparseEmbedding: () => ({ indices: [1], values: [1] }),
70
+ }));
71
+
72
+ mock.module("../memory/v2/qdrant.js", () => ({
73
+ upsertConceptPageEmbedding: async (params: { slug: string }) => {
74
+ seedUpsertSlugs.push(params.slug);
75
+ },
76
+ pruneSlugsWithPrefixExcept: async () => {},
77
+ }));
78
+
79
+ mock.module("../daemon/skill-memory-refresh.js", () => ({
80
+ refreshSkillCapabilityMemories: mock(() => {}),
81
+ }));
82
+
54
83
  import { loadSkillCatalog } from "../config/skills.js";
84
+ import {
85
+ _resetSkillStoreForTests,
86
+ getSkillCapability,
87
+ seedV2SkillEntries,
88
+ } from "../memory/v2/skill-store.js";
55
89
  import { executeDeleteManagedSkill } from "../tools/skills/delete-managed.js";
56
90
  import { SkillLoadTool } from "../tools/skills/load.js";
57
91
  import { executeScaffoldManagedSkill } from "../tools/skills/scaffold-managed.js";
@@ -68,9 +102,69 @@ function makeContext(): ToolContext {
68
102
  beforeEach(() => {
69
103
  TEST_DIR = process.env.VELLUM_WORKSPACE_DIR!;
70
104
  mkdirSync(join(TEST_DIR, "skills"), { recursive: true });
105
+ seedUpsertSlugs.length = 0;
106
+ _resetSkillStoreForTests();
71
107
  });
72
108
 
73
109
  describe("managed skill lifecycle: scaffold → catalog → prompt → delete", () => {
110
+ test("valid managed skill without SKILLS.md works across catalog, skill_load, and Memory V2 seeding", async () => {
111
+ const skillId = "e2e-custom-skill";
112
+ const skillSlug = `skills/${skillId}`;
113
+ const skillDir = join(TEST_DIR, "skills", skillId);
114
+ mkdirSync(skillDir, { recursive: true });
115
+ writeFileSync(
116
+ join(skillDir, "SKILL.md"),
117
+ `---
118
+ name: "E2E Custom Skill"
119
+ description: "Exercises custom managed skill loading."
120
+ metadata:
121
+ vellum:
122
+ activation-hints:
123
+ - user asks for custom lifecycle verification
124
+ ---
125
+
126
+ Run the custom lifecycle verification procedure.
127
+ `,
128
+ "utf-8",
129
+ );
130
+
131
+ expect(existsSync(join(TEST_DIR, "skills", "SKILLS.md"))).toBe(false);
132
+
133
+ const catalog = loadSkillCatalog();
134
+ const catalogSkill = catalog.find((s) => s.id === skillId);
135
+ expect(catalogSkill).toBeDefined();
136
+ expect(catalogSkill!.source).toBe("managed");
137
+ expect(catalogSkill!.displayName).toBe("E2E Custom Skill");
138
+
139
+ const skillLoadTool = new (SkillLoadTool as any)() as InstanceType<
140
+ typeof SkillLoadTool
141
+ >;
142
+ const loadResult = await skillLoadTool.execute(
143
+ { skill: skillId },
144
+ makeContext(),
145
+ );
146
+ expect(loadResult.isError).not.toBe(true);
147
+ expect(loadResult.content as string).toContain("Skill: E2E Custom Skill");
148
+ expect(loadResult.content as string).toContain("ID: e2e-custom-skill");
149
+ expect(loadResult.content as string).toContain(
150
+ "Run the custom lifecycle verification procedure.",
151
+ );
152
+
153
+ await seedV2SkillEntries();
154
+
155
+ expect(seedUpsertSlugs).toContain(skillSlug);
156
+ const capability = getSkillCapability(skillSlug);
157
+ expect(capability).not.toBeNull();
158
+ expect(capability!.id).toBe("e2e-custom-skill");
159
+ expect(capability!.content).toContain('The "E2E Custom Skill" skill');
160
+ expect(capability!.content).toContain(
161
+ "Exercises custom managed skill loading.",
162
+ );
163
+ expect(capability!.content).toContain(
164
+ "Use when: user asks for custom lifecycle verification.",
165
+ );
166
+ }, 15_000);
167
+
74
168
  test("full lifecycle: create skill, verify in catalog and prompt, then delete", async () => {
75
169
  // Step 1: Scaffold a managed skill
76
170
  const scaffoldResult = await executeScaffoldManagedSkill(
@@ -87,9 +181,11 @@ describe("managed skill lifecycle: scaffold → catalog → prompt → delete",
87
181
  expect(scaffoldResult.isError).not.toBe(true);
88
182
  const scaffoldData = JSON.parse(scaffoldResult.content as string);
89
183
  expect(scaffoldData.created).toBe(true);
184
+ expect(scaffoldData).not.toHaveProperty("index_updated");
90
185
 
91
186
  // Step 2: Verify SKILL.md was written
92
- const skillMdPath = join(TEST_DIR, "skills", "lifecycle-test", "SKILL.md");
187
+ const skillDir = join(TEST_DIR, "skills", "lifecycle-test");
188
+ const skillMdPath = join(skillDir, "SKILL.md");
93
189
  expect(existsSync(skillMdPath)).toBe(true);
94
190
  const skillContent = readFileSync(skillMdPath, "utf-8");
95
191
  expect(skillContent).toContain('name: "Lifecycle Test"');
@@ -102,6 +198,7 @@ describe("managed skill lifecycle: scaffold → catalog → prompt → delete",
102
198
  expect(found).toBeDefined();
103
199
  expect(found!.name).toBe("Lifecycle Test");
104
200
  expect(found!.description).toBe("Integration test skill.");
201
+ expect(existsSync(join(TEST_DIR, "skills", "SKILLS.md"))).toBe(false);
105
202
 
106
203
  // Step 4: Delete the skill
107
204
  const deleteResult = await executeDeleteManagedSkill(
@@ -114,20 +211,15 @@ describe("managed skill lifecycle: scaffold → catalog → prompt → delete",
114
211
  expect(deleteResult.isError).not.toBe(true);
115
212
  const deleteData = JSON.parse(deleteResult.content as string);
116
213
  expect(deleteData.deleted).toBe(true);
214
+ expect(deleteData).not.toHaveProperty("index_updated");
117
215
 
118
- // Step 5: Verify skill is gone from filesystem
119
- expect(existsSync(skillMdPath)).toBe(false);
216
+ // Step 5: Verify skill directory is gone from filesystem
217
+ expect(existsSync(skillDir)).toBe(false);
120
218
 
121
219
  // Step 6: Verify skill no longer in catalog
122
220
  const catalogAfter = loadSkillCatalog();
123
221
  expect(catalogAfter.find((s) => s.id === "lifecycle-test")).toBeUndefined();
124
-
125
- // Step 7: Verify SKILLS.md index no longer has the entry
126
- const indexPath = join(TEST_DIR, "skills", "SKILLS.md");
127
- if (existsSync(indexPath)) {
128
- const indexContent = readFileSync(indexPath, "utf-8");
129
- expect(indexContent).not.toContain("lifecycle-test");
130
- }
222
+ expect(existsSync(join(TEST_DIR, "skills", "SKILLS.md"))).toBe(false);
131
223
  });
132
224
 
133
225
  test("scaffold with overwrite replaces existing skill", async () => {
@@ -166,13 +258,12 @@ describe("managed skill lifecycle: scaffold → catalog → prompt → delete",
166
258
  expect(skillContent).toContain("Updated body.");
167
259
  expect(skillContent).not.toContain("Original body.");
168
260
 
169
- // Index should still have exactly one entry
170
- const indexContent = readFileSync(
171
- join(TEST_DIR, "skills", "SKILLS.md"),
172
- "utf-8",
173
- );
174
- const matches = indexContent.match(/overwrite-test/g);
175
- expect(matches?.length).toBe(1);
261
+ expect(existsSync(join(TEST_DIR, "skills", "SKILLS.md"))).toBe(false);
262
+
263
+ const catalog = loadSkillCatalog();
264
+ const skill = catalog.find((s) => s.id === "overwrite-test");
265
+ expect(skill).toBeDefined();
266
+ expect(skill!.name).toBe("V2");
176
267
  });
177
268
 
178
269
  test("delete non-existent skill returns error", async () => {
@@ -34,9 +34,6 @@ import {
34
34
  buildSkillMarkdown,
35
35
  createManagedSkill,
36
36
  deleteManagedSkill,
37
- readSkillVersion,
38
- removeSkillsIndexEntry,
39
- upsertSkillsIndexEntry,
40
37
  validateManagedSkillId,
41
38
  } from "../skills/managed-store.js";
42
39
 
@@ -48,6 +45,22 @@ afterEach(() => {
48
45
  rmSync(join(TEST_DIR, "skills"), { recursive: true, force: true });
49
46
  });
50
47
 
48
+ interface TestInstallMeta {
49
+ origin?: string;
50
+ version?: string;
51
+ installedAt?: string;
52
+ installedBy?: string;
53
+ }
54
+
55
+ function readInstallMetaFile(skillId: string): TestInstallMeta {
56
+ return JSON.parse(
57
+ readFileSync(
58
+ join(TEST_DIR, "skills", skillId, "install-meta.json"),
59
+ "utf-8",
60
+ ),
61
+ );
62
+ }
63
+
51
64
  describe("validateManagedSkillId", () => {
52
65
  test("accepts valid slug IDs", () => {
53
66
  expect(validateManagedSkillId("my-skill")).toBeNull();
@@ -142,7 +155,7 @@ describe("buildSkillMarkdown", () => {
142
155
  });
143
156
 
144
157
  // Load it back via loadSkillCatalog (which uses parseFrontmatter)
145
- const catalog = loadSkillCatalog(undefined, [join(TEST_DIR, "skills")]);
158
+ const catalog = loadSkillCatalog();
146
159
  const skill = catalog.find((s) => s.id === "roundtrip-test");
147
160
  expect(skill).toBeDefined();
148
161
  expect(skill!.name).toBe('Say "hello" & back\\slash');
@@ -159,7 +172,7 @@ describe("buildSkillMarkdown", () => {
159
172
  bodyMarkdown: "Body.",
160
173
  });
161
174
 
162
- const catalog = loadSkillCatalog(undefined, [join(TEST_DIR, "skills")]);
175
+ const catalog = loadSkillCatalog();
163
176
  const skill = catalog.find((s) => s.id === "backslash-n-test");
164
177
  expect(skill).toBeDefined();
165
178
  expect(skill!.name).toBe("path\\name");
@@ -205,7 +218,7 @@ describe("buildSkillMarkdown", () => {
205
218
  includes: ["child-x", "child-y"],
206
219
  });
207
220
 
208
- const catalog = loadSkillCatalog(undefined, [join(TEST_DIR, "skills")]);
221
+ const catalog = loadSkillCatalog();
209
222
  const skill = catalog.find((s) => s.id === "roundtrip-includes");
210
223
  expect(skill).toBeDefined();
211
224
  expect(skill!.includes).toEqual(["child-x", "child-y"]);
@@ -231,113 +244,6 @@ describe("buildSkillMarkdown", () => {
231
244
  });
232
245
  });
233
246
 
234
- describe("SKILLS.md index management", () => {
235
- test("SKILLS.md is created when absent", () => {
236
- upsertSkillsIndexEntry("my-skill");
237
- const indexPath = join(TEST_DIR, "skills", "SKILLS.md");
238
- expect(existsSync(indexPath)).toBe(true);
239
- const content = readFileSync(indexPath, "utf-8");
240
- expect(content).toContain("- my-skill");
241
- });
242
-
243
- test("index add is idempotent", () => {
244
- upsertSkillsIndexEntry("my-skill");
245
- upsertSkillsIndexEntry("my-skill");
246
- const content = readFileSync(
247
- join(TEST_DIR, "skills", "SKILLS.md"),
248
- "utf-8",
249
- );
250
- const matches = content.match(/- my-skill/g);
251
- expect(matches?.length).toBe(1);
252
- });
253
-
254
- test("delete removes directory and index entry", () => {
255
- // Set up a skill
256
- const skillDir = join(TEST_DIR, "skills", "doomed");
257
- mkdirSync(skillDir, { recursive: true });
258
- writeFileSync(join(skillDir, "SKILL.md"), "test");
259
- writeFileSync(
260
- join(TEST_DIR, "skills", "SKILLS.md"),
261
- "- doomed\n- survivor\n",
262
- );
263
-
264
- const result = deleteManagedSkill("doomed");
265
- expect(result.deleted).toBe(true);
266
- expect(result.indexUpdated).toBe(true);
267
- expect(existsSync(skillDir)).toBe(false);
268
-
269
- const content = readFileSync(
270
- join(TEST_DIR, "skills", "SKILLS.md"),
271
- "utf-8",
272
- );
273
- expect(content).not.toContain("doomed");
274
- expect(content).toContain("survivor");
275
- });
276
-
277
- test("upsert recognizes * bullet entries", () => {
278
- writeFileSync(join(TEST_DIR, "skills", "SKILLS.md"), "* existing-skill\n");
279
- upsertSkillsIndexEntry("existing-skill");
280
- const content = readFileSync(
281
- join(TEST_DIR, "skills", "SKILLS.md"),
282
- "utf-8",
283
- );
284
- const matches = content.match(/existing-skill/g);
285
- expect(matches?.length).toBe(1);
286
- });
287
-
288
- test("remove handles * bullet entries", () => {
289
- writeFileSync(
290
- join(TEST_DIR, "skills", "SKILLS.md"),
291
- "* doomed\n- survivor\n",
292
- );
293
- removeSkillsIndexEntry("doomed");
294
- const content = readFileSync(
295
- join(TEST_DIR, "skills", "SKILLS.md"),
296
- "utf-8",
297
- );
298
- expect(content).not.toContain("doomed");
299
- expect(content).toContain("survivor");
300
- });
301
-
302
- test("remove handles markdown link entries", () => {
303
- writeFileSync(
304
- join(TEST_DIR, "skills", "SKILLS.md"),
305
- "- [My Skill](my-skill/SKILL.md)\n- survivor\n",
306
- );
307
- removeSkillsIndexEntry("my-skill");
308
- const content = readFileSync(
309
- join(TEST_DIR, "skills", "SKILLS.md"),
310
- "utf-8",
311
- );
312
- expect(content).not.toContain("my-skill");
313
- expect(content).toContain("survivor");
314
- });
315
-
316
- test("upsert recognizes markdown link entries as existing", () => {
317
- writeFileSync(
318
- join(TEST_DIR, "skills", "SKILLS.md"),
319
- "- [My Skill](my-skill/SKILL.md)\n",
320
- );
321
- upsertSkillsIndexEntry("my-skill");
322
- const content = readFileSync(
323
- join(TEST_DIR, "skills", "SKILLS.md"),
324
- "utf-8",
325
- );
326
- const matches = content.match(/my-skill/g);
327
- expect(matches?.length).toBe(1);
328
- });
329
-
330
- test("remove from index handles missing entry gracefully", () => {
331
- writeFileSync(join(TEST_DIR, "skills", "SKILLS.md"), "- other-skill\n");
332
- removeSkillsIndexEntry("nonexistent");
333
- const content = readFileSync(
334
- join(TEST_DIR, "skills", "SKILLS.md"),
335
- "utf-8",
336
- );
337
- expect(content).toContain("other-skill");
338
- });
339
- });
340
-
341
247
  describe("createManagedSkill", () => {
342
248
  test("creates skill and writes to expected path", () => {
343
249
  const result = createManagedSkill({
@@ -429,35 +335,20 @@ describe("createManagedSkill", () => {
429
335
  expect(result.error).toContain("description is required");
430
336
  });
431
337
 
432
- test("updates SKILLS.md index", () => {
338
+ test("does not create SKILLS.md and is discovered through SKILL.md directory", () => {
433
339
  createManagedSkill({
434
- id: "indexed-skill",
435
- name: "Indexed",
436
- description: "Gets indexed",
340
+ id: "discovered-skill",
341
+ name: "Discovered",
342
+ description: "Found by directory discovery",
437
343
  bodyMarkdown: "Body.",
438
344
  });
439
345
 
440
- const indexContent = readFileSync(
441
- join(TEST_DIR, "skills", "SKILLS.md"),
442
- "utf-8",
443
- );
444
- expect(indexContent).toContain("- indexed-skill");
445
- });
446
-
447
- test("skips index when addToIndex=false", () => {
448
- createManagedSkill({
449
- id: "no-index",
450
- name: "No Index",
451
- description: "Not indexed",
452
- bodyMarkdown: "Body.",
453
- addToIndex: false,
454
- });
346
+ expect(existsSync(join(TEST_DIR, "skills", "SKILLS.md"))).toBe(false);
455
347
 
456
- const indexPath = join(TEST_DIR, "skills", "SKILLS.md");
457
- if (existsSync(indexPath)) {
458
- const content = readFileSync(indexPath, "utf-8");
459
- expect(content).not.toContain("no-index");
460
- }
348
+ const catalog = loadSkillCatalog(undefined, [join(TEST_DIR, "skills")]);
349
+ const skill = catalog.find((s) => s.id === "discovered-skill");
350
+ expect(skill).toBeDefined();
351
+ expect(skill!.name).toBe("Discovered");
461
352
  });
462
353
  });
463
354
 
@@ -564,6 +455,9 @@ describe("deleteManagedSkill", () => {
564
455
  const result = deleteManagedSkill("to-delete");
565
456
  expect(result.deleted).toBe(true);
566
457
  expect(existsSync(join(TEST_DIR, "skills", "to-delete"))).toBe(false);
458
+
459
+ const catalog = loadSkillCatalog(undefined, [join(TEST_DIR, "skills")]);
460
+ expect(catalog.find((s) => s.id === "to-delete")).toBeUndefined();
567
461
  });
568
462
 
569
463
  test("returns error for non-existent skill", () => {
@@ -578,75 +472,60 @@ describe("deleteManagedSkill", () => {
578
472
  expect(result.error).toContain("traversal");
579
473
  });
580
474
 
581
- test("skips index removal when removeFromIndex=false", () => {
475
+ test("delete leaves a stale SKILLS.md index unchanged", () => {
582
476
  createManagedSkill({
583
477
  id: "keep-index",
584
478
  name: "Keep Index",
585
479
  description: "Index stays",
586
480
  bodyMarkdown: "Body.",
587
481
  });
482
+ writeFileSync(
483
+ join(TEST_DIR, "skills", "SKILLS.md"),
484
+ "- keep-index\n- survivor\n",
485
+ );
588
486
 
589
- const result = deleteManagedSkill("keep-index", false);
487
+ const result = deleteManagedSkill("keep-index");
590
488
  expect(result.deleted).toBe(true);
591
- expect(result.indexUpdated).toBe(false);
592
489
 
593
490
  const indexContent = readFileSync(
594
491
  join(TEST_DIR, "skills", "SKILLS.md"),
595
492
  "utf-8",
596
493
  );
597
494
  expect(indexContent).toContain("- keep-index");
495
+ expect(indexContent).toContain("- survivor");
496
+
497
+ const catalog = loadSkillCatalog(undefined, [join(TEST_DIR, "skills")]);
498
+ expect(catalog.find((s) => s.id === "keep-index")).toBeUndefined();
598
499
  });
599
500
 
600
- test("succeeds even when index cleanup throws", () => {
501
+ test("delete does not create or edit SKILLS.md", () => {
601
502
  createManagedSkill({
602
- id: "index-fail",
603
- name: "Index Fail",
604
- description: "Index removal will throw",
503
+ id: "delete-no-index",
504
+ name: "Delete No Index",
505
+ description: "No index write",
605
506
  bodyMarkdown: "Body.",
606
507
  });
607
508
 
608
- // Intercept the atomic write (.tmp- file) used by removeSkillsIndexEntry
609
509
  const skillsDir = join(TEST_DIR, "skills");
610
- const originalWrite = fs.writeFileSync;
611
- const spy = spyOn(fs, "writeFileSync").mockImplementation(((
612
- path: fs.PathOrFileDescriptor,
613
- data: string | NodeJS.ArrayBufferView,
614
- options?: fs.WriteFileOptions,
615
- ) => {
616
- if (
617
- typeof path === "string" &&
618
- path.startsWith(skillsDir) &&
619
- path.includes(".tmp-")
620
- ) {
621
- throw new Error("Simulated write failure");
622
- }
623
- return originalWrite(path, data, options);
624
- }) as typeof fs.writeFileSync);
625
-
626
- try {
627
- const result = deleteManagedSkill("index-fail");
628
- expect(result.deleted).toBe(true);
629
- expect(result.indexUpdated).toBe(false);
630
- expect(existsSync(join(skillsDir, "index-fail"))).toBe(false);
631
- } finally {
632
- spy.mockRestore();
633
- }
510
+ const result = deleteManagedSkill("delete-no-index");
511
+ expect(result.deleted).toBe(true);
512
+ expect(existsSync(join(skillsDir, "delete-no-index"))).toBe(false);
513
+ expect(existsSync(join(skillsDir, "SKILLS.md"))).toBe(false);
634
514
  });
635
515
  });
636
516
 
637
517
  describe("version metadata", () => {
638
- test("readSkillVersion returns null for non-existent skill", () => {
639
- expect(readSkillVersion("nonexistent")).toBeNull();
640
- });
641
-
642
- test("readSkillVersion returns null when skill exists but has no version.json", () => {
518
+ test("createManagedSkill writes install-meta.json without version when version is omitted", () => {
643
519
  createManagedSkill({
644
520
  id: "no-version",
645
521
  name: "No Version",
646
522
  description: "Created without version",
647
523
  bodyMarkdown: "Body.",
648
524
  });
649
- expect(readSkillVersion("no-version")).toBeNull();
525
+
526
+ const meta = readInstallMetaFile("no-version");
527
+ expect(meta.origin).toBe("custom");
528
+ expect(meta.version).toBeUndefined();
650
529
  });
651
530
 
652
531
  test("createManagedSkill writes install-meta.json when version is provided", () => {
@@ -658,8 +537,8 @@ describe("version metadata", () => {
658
537
  version: "v1:abc123",
659
538
  });
660
539
 
661
- const version = readSkillVersion("versioned");
662
- expect(version).toBe("v1:abc123");
540
+ const meta = readInstallMetaFile("versioned");
541
+ expect(meta.version).toBe("v1:abc123");
663
542
  });
664
543
 
665
544
  test("install-meta.json contains valid JSON with origin, version, and installedAt", () => {
@@ -678,10 +557,13 @@ describe("version metadata", () => {
678
557
  "install-meta.json",
679
558
  );
680
559
  expect(existsSync(metaPath)).toBe(true);
681
- const meta = JSON.parse(readFileSync(metaPath, "utf-8"));
560
+ const meta = readInstallMetaFile("version-meta");
682
561
  expect(meta.origin).toBe("custom");
683
562
  expect(meta.version).toBe("v1:deadbeef");
684
563
  expect(typeof meta.installedAt).toBe("string");
564
+ if (typeof meta.installedAt !== "string") {
565
+ throw new Error("installedAt must be a string");
566
+ }
685
567
  // installedAt should be a valid ISO date
686
568
  expect(new Date(meta.installedAt).toISOString()).toBe(meta.installedAt);
687
569
  });
@@ -702,7 +584,7 @@ describe("version metadata", () => {
702
584
  "install-meta.json",
703
585
  );
704
586
  expect(existsSync(metaPath)).toBe(true);
705
- const meta = JSON.parse(readFileSync(metaPath, "utf-8"));
587
+ const meta = readInstallMetaFile("with-contact");
706
588
  expect(meta.origin).toBe("custom");
707
589
  expect(meta.installedBy).toBe("contact-uuid-456");
708
590
  });
@@ -715,7 +597,8 @@ describe("version metadata", () => {
715
597
  bodyMarkdown: "Body.",
716
598
  version: "v1:first",
717
599
  });
718
- expect(readSkillVersion("update-version")).toBe("v1:first");
600
+
601
+ expect(readInstallMetaFile("update-version").version).toBe("v1:first");
719
602
 
720
603
  createManagedSkill({
721
604
  id: "update-version",
@@ -725,28 +608,37 @@ describe("version metadata", () => {
725
608
  version: "v1:second",
726
609
  overwrite: true,
727
610
  });
728
- expect(readSkillVersion("update-version")).toBe("v1:second");
611
+ expect(readInstallMetaFile("update-version").version).toBe("v1:second");
729
612
  });
730
613
 
731
- test("readSkillVersion returns null for corrupted install-meta.json", () => {
614
+ test("overwrite removes legacy version.json", () => {
732
615
  createManagedSkill({
733
- id: "corrupt-version",
734
- name: "Corrupt",
735
- description: "Will corrupt meta file",
616
+ id: "legacy-version",
617
+ name: "Legacy",
618
+ description: "Has legacy metadata",
736
619
  bodyMarkdown: "Body.",
737
- version: "v1:valid",
620
+ version: "v1:first",
738
621
  });
739
622
 
740
- // Corrupt the install-meta.json
741
- const metaPath = join(
623
+ const legacyMetaPath = join(
742
624
  TEST_DIR,
743
625
  "skills",
744
- "corrupt-version",
745
- "install-meta.json",
626
+ "legacy-version",
627
+ "version.json",
746
628
  );
747
- writeFileSync(metaPath, "{invalid json!!!", "utf-8");
629
+ writeFileSync(legacyMetaPath, '{"version":"legacy"}', "utf-8");
630
+ expect(existsSync(legacyMetaPath)).toBe(true);
631
+
632
+ createManagedSkill({
633
+ id: "legacy-version",
634
+ name: "Legacy Updated",
635
+ description: "Has current metadata",
636
+ bodyMarkdown: "Body.",
637
+ version: "v1:second",
638
+ overwrite: true,
639
+ });
748
640
 
749
- expect(readSkillVersion("corrupt-version")).toBeNull();
641
+ expect(existsSync(legacyMetaPath)).toBe(false);
750
642
  });
751
643
  });
752
644
 
@@ -81,7 +81,7 @@ let mockManagedProxyContext = {
81
81
  assistantApiKey: "",
82
82
  };
83
83
 
84
- mock.module("../providers/managed-proxy/context.js", () => ({
84
+ mock.module("../providers/platform-proxy/context.js", () => ({
85
85
  buildManagedBaseUrl: async () => mockManagedBaseUrl,
86
86
  resolveManagedProxyContext: async () => mockManagedProxyContext,
87
87
  }));
@@ -273,7 +273,6 @@ describe("memoryRetrieval pipeline — default vs custom plugin", () => {
273
273
  manifest: {
274
274
  name: "custom-memory-retrieval",
275
275
  version: "0.0.1",
276
- requires: { pluginRuntime: "v1", memoryApi: "v1" },
277
276
  },
278
277
  middleware: { memoryRetrieval: customMiddleware },
279
278
  };
@@ -318,7 +317,6 @@ describe("memoryRetrieval pipeline — default vs custom plugin", () => {
318
317
  manifest: {
319
318
  name: "hanging-memory-plugin",
320
319
  version: "0.0.1",
321
- requires: { pluginRuntime: "v1", memoryApi: "v1" },
322
320
  },
323
321
  middleware: { memoryRetrieval: hanging },
324
322
  };