@vellumai/assistant 0.8.1 → 0.8.2

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 (506) hide show
  1. package/ARCHITECTURE.md +2 -7
  2. package/Dockerfile +75 -1
  3. package/bun.lock +11 -1
  4. package/docker-entrypoint.sh +5 -0
  5. package/docker-init-apt-root.sh +94 -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 +325 -3
  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-wake-disk-pressure-callsite.test.ts +131 -0
  21. package/src/__tests__/anthropic-provider.test.ts +45 -0
  22. package/src/__tests__/app-builder-tool-scripts.test.ts +9 -3
  23. package/src/__tests__/app-executors.test.ts +220 -4
  24. package/src/__tests__/auto-analysis-end-to-end.test.ts +35 -0
  25. package/src/__tests__/bundled-asset.test.ts +6 -6
  26. package/src/__tests__/channel-availability-routes.test.ts +206 -0
  27. package/src/__tests__/channel-delivery-store.test.ts +289 -1
  28. package/src/__tests__/circuit-breaker-pipeline.test.ts +0 -1
  29. package/src/__tests__/clawhub.test.ts +75 -16
  30. package/src/__tests__/compactor-tail-resolution.test.ts +41 -0
  31. package/src/__tests__/config-schema.test.ts +21 -0
  32. package/src/__tests__/config-set-route.test.ts +80 -0
  33. package/src/__tests__/config-sounds-sync.test.ts +97 -0
  34. package/src/__tests__/config-watcher-skill-reseed.test.ts +453 -0
  35. package/src/__tests__/context-search-conversations-source.test.ts +117 -2
  36. package/src/__tests__/context-search-memory-v2-source.test.ts +0 -1
  37. package/src/__tests__/context-search-workspace-source.test.ts +7 -0
  38. package/src/__tests__/context-token-estimator.test.ts +1 -0
  39. package/src/__tests__/conversation-abort-tool-results.test.ts +4 -1
  40. package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +1 -0
  41. package/src/__tests__/conversation-agent-loop-overflow.test.ts +92 -92
  42. package/src/__tests__/conversation-agent-loop.test.ts +2 -0
  43. package/src/__tests__/conversation-error.test.ts +42 -3
  44. package/src/__tests__/conversation-fork-crud.test.ts +82 -0
  45. package/src/__tests__/conversation-inference-profile-route.test.ts +40 -4
  46. package/src/__tests__/conversation-lifecycle.test.ts +173 -0
  47. package/src/__tests__/conversation-message-sync-tags.test.ts +97 -0
  48. package/src/__tests__/conversation-pairing.test.ts +54 -0
  49. package/src/__tests__/conversation-process-callsite.test.ts +4 -1
  50. package/src/__tests__/conversation-provider-retry-repair.test.ts +5 -1
  51. package/src/__tests__/conversation-queue.test.ts +4 -1
  52. package/src/__tests__/conversation-runtime-assembly.test.ts +76 -9
  53. package/src/__tests__/conversation-slash-queue.test.ts +59 -1
  54. package/src/__tests__/conversation-slash-unknown.test.ts +4 -1
  55. package/src/__tests__/conversation-surfaces-table-action.test.ts +360 -0
  56. package/src/__tests__/conversation-sync-tags.test.ts +235 -0
  57. package/src/__tests__/conversation-workspace-injection.test.ts +5 -1
  58. package/src/__tests__/conversation-workspace-tool-tracking.test.ts +5 -1
  59. package/src/__tests__/credential-security-invariants.test.ts +3 -2
  60. package/src/__tests__/db-slack-external-content-normalization.test.ts +301 -0
  61. package/src/__tests__/delete-managed-skill-tool.test.ts +55 -13
  62. package/src/__tests__/disk-pressure-tools.test.ts +1 -0
  63. package/src/__tests__/dm-backfill.test.ts +121 -10
  64. package/src/__tests__/document-tool-security.test.ts +258 -0
  65. package/src/__tests__/dynamic-skill-workflow-prompt.test.ts +0 -1
  66. package/src/__tests__/edit-propagation.test.ts +33 -0
  67. package/src/__tests__/empty-response-pipeline.test.ts +0 -4
  68. package/src/__tests__/external-plugin-loader.test.ts +60 -36
  69. package/src/__tests__/filing-service.test.ts +140 -0
  70. package/src/__tests__/get-skill-detail-audit.test.ts +0 -4
  71. package/src/__tests__/handlers-skills-memory-v2-reseed.test.ts +43 -62
  72. package/src/__tests__/helpers/tar-fixtures.ts +39 -0
  73. package/src/__tests__/helpers/wait-for.ts +21 -0
  74. package/src/__tests__/history-repair-pipeline.test.ts +0 -3
  75. package/src/__tests__/history-repair.test.ts +73 -0
  76. package/src/__tests__/host-app-control-proxy.test.ts +266 -10
  77. package/src/__tests__/image-credentials.test.ts +1 -1
  78. package/src/__tests__/inbound-slack-persistence.test.ts +2 -0
  79. package/src/__tests__/inference-no-mode-boot-e2e.test.ts +1 -1
  80. package/src/__tests__/inference-profile-reaper.test.ts +4 -2
  81. package/src/__tests__/inference-profile-session-handler.test.ts +18 -6
  82. package/src/__tests__/inference-profile-session-ipc.test.ts +17 -5
  83. package/src/__tests__/injector-chain.test.ts +10 -8
  84. package/src/__tests__/install-skill-routing.test.ts +155 -37
  85. package/src/__tests__/lifecycle-memory-v2-seed.test.ts +92 -3
  86. package/src/__tests__/list-messages-page-latest.test.ts +55 -0
  87. package/src/__tests__/llm-call-pipeline.test.ts +0 -3
  88. package/src/__tests__/llm-catalog-parity.test.ts +55 -13
  89. package/src/__tests__/llm-request-log-source-clickhouse.test.ts +34 -0
  90. package/src/__tests__/llm-request-log-source-factory.test.ts +29 -53
  91. package/src/__tests__/llm-usage-store.test.ts +114 -0
  92. package/src/__tests__/managed-profile-guard.test.ts +31 -29
  93. package/src/__tests__/managed-skill-lifecycle.test.ts +109 -18
  94. package/src/__tests__/managed-store.test.ts +84 -192
  95. package/src/__tests__/media-generate-image.test.ts +1 -1
  96. package/src/__tests__/memory-retrieval-pipeline.test.ts +0 -2
  97. package/src/__tests__/messages-after-tiebreaker.test.ts +122 -0
  98. package/src/__tests__/oauth-commands-routes.test.ts +168 -16
  99. package/src/__tests__/oauth-provider-profiles.test.ts +9 -0
  100. package/src/__tests__/openai-provider.test.ts +24 -0
  101. package/src/__tests__/openai-responses-cutover-guard.test.ts +17 -9
  102. package/src/__tests__/overflow-reduce-pipeline.test.ts +0 -2
  103. package/src/__tests__/persistence-pipeline.test.ts +0 -2
  104. package/src/__tests__/{managed-proxy-context.test.ts → platform-proxy-context.test.ts} +1 -1
  105. package/src/__tests__/platform.test.ts +2 -0
  106. package/src/__tests__/plugin-api-shim.test.ts +125 -0
  107. package/src/__tests__/plugin-bootstrap.test.ts +10 -36
  108. package/src/__tests__/plugin-external-api.test.ts +68 -0
  109. package/src/__tests__/plugin-registry.test.ts +0 -77
  110. package/src/__tests__/plugin-route-contribution.test.ts +0 -1
  111. package/src/__tests__/plugin-skill-contribution.test.ts +0 -2
  112. package/src/__tests__/plugin-tool-contribution.test.ts +16 -15
  113. package/src/__tests__/plugin-types.test.ts +3 -13
  114. package/src/__tests__/process-message-background-slack.test.ts +8 -1
  115. package/src/__tests__/process-message-display-content.test.ts +421 -0
  116. package/src/__tests__/provider-catalog-visibility.test.ts +142 -0
  117. package/src/__tests__/provider-error-scenarios.test.ts +111 -0
  118. package/src/__tests__/{provider-managed-proxy-integration.test.ts → provider-platform-proxy-integration.test.ts} +8 -8
  119. package/src/__tests__/scaffold-managed-skill-tool.test.ts +65 -13
  120. package/src/__tests__/schedule-routes.test.ts +50 -3
  121. package/src/__tests__/schedule-store.test.ts +94 -0
  122. package/src/__tests__/scheduler-reuse-conversation.test.ts +54 -7
  123. package/src/__tests__/schema-transforms.test.ts +20 -0
  124. package/src/__tests__/search-skills-unified.test.ts +0 -5
  125. package/src/__tests__/server-history-render.test.ts +43 -0
  126. package/src/__tests__/skill-load-feature-flag.test.ts +0 -12
  127. package/src/__tests__/skill-load-tool.test.ts +27 -89
  128. package/src/__tests__/skill-memory.test.ts +23 -3
  129. package/src/__tests__/skills-file-content-endpoint.test.ts +9 -38
  130. package/src/__tests__/skills-files-catalog-fallback.test.ts +0 -3
  131. package/src/__tests__/skills-install-extract.test.ts +49 -38
  132. package/src/__tests__/skills-install-staging.test.ts +159 -0
  133. package/src/__tests__/skills-uninstall.test.ts +9 -41
  134. package/src/__tests__/skills.test.ts +51 -58
  135. package/src/__tests__/slack-channel-config.test.ts +9 -0
  136. package/src/__tests__/subagent-tool-filtering.test.ts +50 -0
  137. package/src/__tests__/system-prompt.test.ts +737 -63
  138. package/src/__tests__/terminal-tools.test.ts +28 -1
  139. package/src/__tests__/thread-backfill.test.ts +557 -27
  140. package/src/__tests__/title-generate-pipeline.test.ts +0 -13
  141. package/src/__tests__/token-estimate-pipeline.test.ts +0 -3
  142. package/src/__tests__/tool-error-pipeline.test.ts +0 -3
  143. package/src/__tests__/tool-execute-pipeline.test.ts +0 -5
  144. package/src/__tests__/tool-executor-lifecycle-events.test.ts +1 -1
  145. package/src/__tests__/tool-executor.test.ts +16 -4
  146. package/src/__tests__/tool-result-truncate-pipeline.test.ts +0 -12
  147. package/src/__tests__/turn-events-store.test.ts +256 -0
  148. package/src/__tests__/twilio-routes.test.ts +4 -0
  149. package/src/__tests__/user-plugin-loader.test.ts +0 -7
  150. package/src/__tests__/voice-session-bridge.test.ts +198 -0
  151. package/src/__tests__/web-search-catalog-parity.test.ts +32 -10
  152. package/src/__tests__/workspace-migration-057-repair-stale-gemini-model-ids.test.ts +115 -3
  153. package/src/__tests__/workspace-migration-072-seed-reply-suggestion-callsite.test.ts +50 -0
  154. package/src/__tests__/workspace-migration-073-repair-recall-callsite-empty-profile.test.ts +153 -0
  155. package/src/__tests__/workspace-migration-085-memory-v2-bm25-b-reembed-disabled-v2-pages.test.ts +220 -0
  156. package/src/__tests__/workspace-migration-086-revert-stale-gemini-mis-rewrites.test.ts +269 -0
  157. package/src/__tests__/workspace-migration-remove-legacy-skills-index.test.ts +309 -0
  158. package/src/__tests__/workspace-migrations-runner.test.ts +111 -3
  159. package/src/acp/resolve-agent.ts +1 -1
  160. package/src/agent/image-optimize.ts +13 -5
  161. package/src/calls/voice-session-bridge.ts +61 -42
  162. package/src/channels/types.ts +108 -0
  163. package/src/cli/__tests__/unknown-command.test.ts +24 -0
  164. package/src/cli/commands/__tests__/changelog.test.ts +304 -319
  165. package/src/cli/commands/__tests__/schedules.test.ts +491 -0
  166. package/src/cli/commands/changelog.ts +106 -42
  167. package/src/cli/commands/conversations.ts +102 -17
  168. package/src/cli/commands/default-action.ts +10 -53
  169. package/src/cli/commands/notifications.ts +329 -317
  170. package/src/cli/commands/plugins.ts +185 -0
  171. package/src/cli/commands/schedules.ts +391 -0
  172. package/src/cli/commands/telemetry.ts +40 -0
  173. package/src/cli/lib/__tests__/cli-colors.test.ts +48 -0
  174. package/src/cli/lib/__tests__/confirm-prompt.test.ts +159 -0
  175. package/src/cli/lib/__tests__/install-from-github.test.ts +355 -0
  176. package/src/cli/lib/__tests__/list-installed-plugins.test.ts +154 -0
  177. package/src/cli/lib/__tests__/uninstall-plugin.test.ts +124 -0
  178. package/src/cli/lib/__tests__/unknown-command.test.ts +106 -0
  179. package/src/cli/lib/cli-colors.ts +12 -0
  180. package/src/cli/lib/confirm-prompt.ts +79 -0
  181. package/src/cli/lib/install-from-github.ts +304 -0
  182. package/src/cli/lib/list-installed-plugins.ts +137 -0
  183. package/src/cli/lib/uninstall-plugin.ts +82 -0
  184. package/src/cli/lib/unknown-command.ts +111 -0
  185. package/src/cli/program.ts +38 -2
  186. package/src/config/bundled-skills/app-builder/SKILL.md +23 -21
  187. package/src/config/bundled-skills/app-builder/TOOLS.json +7 -0
  188. package/src/config/bundled-skills/computer-use/TOOLS.json +15 -52
  189. package/src/config/bundled-skills/document/SKILL.md +23 -3
  190. package/src/config/bundled-skills/document/TOOLS.json +53 -0
  191. package/src/config/bundled-skills/document/tools/document-delete.ts +12 -0
  192. package/src/config/bundled-skills/document/tools/document-list.ts +12 -0
  193. package/src/config/bundled-skills/document/tools/document-read.ts +12 -0
  194. package/src/config/bundled-skills/skill-management/SKILL.md +2 -2
  195. package/src/config/bundled-skills/skill-management/TOOLS.json +7 -7
  196. package/src/config/bundled-tool-registry.ts +6 -0
  197. package/src/config/feature-flag-registry.json +41 -1
  198. package/src/config/loader.ts +64 -38
  199. package/src/config/schema.ts +7 -10
  200. package/src/config/schemas/__tests__/llm-request-logs.test.ts +36 -0
  201. package/src/config/schemas/channels.ts +8 -0
  202. package/src/config/schemas/compaction.ts +28 -0
  203. package/src/config/schemas/heartbeat.ts +9 -0
  204. package/src/config/schemas/llm-request-logs.ts +31 -7
  205. package/src/config/schemas/llm.ts +3 -0
  206. package/src/config/schemas/memory-retrieval.ts +18 -0
  207. package/src/config/schemas/tools.ts +14 -0
  208. package/src/config/skills.ts +3 -96
  209. package/src/context/compactor.ts +1047 -0
  210. package/src/context/token-estimator.ts +2 -2
  211. package/src/context/window-manager.ts +197 -1520
  212. package/src/credential-execution/managed-catalog.ts +37 -0
  213. package/src/credential-health/credential-health-service.ts +280 -19
  214. package/src/daemon/__tests__/conversation-lifecycle-auto-analyze.test.ts +34 -0
  215. package/src/daemon/__tests__/conversation-tool-setup-exclude.test.ts +138 -0
  216. package/src/daemon/__tests__/conversation-tool-setup.test.ts +74 -0
  217. package/src/daemon/approval-generators.ts +8 -6
  218. package/src/daemon/config-watcher.ts +94 -31
  219. package/src/daemon/conversation-agent-loop.ts +169 -9
  220. package/src/daemon/conversation-error.ts +171 -37
  221. package/src/daemon/conversation-lifecycle.ts +53 -40
  222. package/src/daemon/conversation-messaging.ts +25 -6
  223. package/src/daemon/conversation-process.ts +49 -12
  224. package/src/daemon/conversation-runtime-assembly.ts +16 -1
  225. package/src/daemon/conversation-slash.ts +12 -5
  226. package/src/daemon/conversation-store.ts +11 -4
  227. package/src/daemon/conversation-tool-setup.ts +39 -7
  228. package/src/daemon/conversation.ts +33 -1
  229. package/src/daemon/external-plugins-bootstrap.ts +217 -181
  230. package/src/daemon/first-greeting.ts +22 -2
  231. package/src/daemon/handlers/config-model.ts +6 -5
  232. package/src/daemon/handlers/config-slack-channel.ts +15 -3
  233. package/src/daemon/handlers/shared.ts +14 -5
  234. package/src/daemon/handlers/skills.ts +111 -108
  235. package/src/daemon/history-repair.ts +28 -1
  236. package/src/daemon/host-app-control-proxy.ts +98 -23
  237. package/src/daemon/lifecycle.ts +45 -35
  238. package/src/daemon/meet-host-supervisor.ts +5 -4
  239. package/src/daemon/memory-v2-startup.ts +49 -0
  240. package/src/daemon/message-protocol.ts +1 -0
  241. package/src/daemon/message-types/conversations.ts +25 -0
  242. package/src/daemon/message-types/messages.ts +61 -0
  243. package/src/daemon/message-types/subagents.ts +1 -0
  244. package/src/daemon/message-types/sync.ts +1 -0
  245. package/src/daemon/pkb-reminder-builder.test.ts +1 -1
  246. package/src/daemon/pkb-reminder-builder.ts +1 -1
  247. package/src/daemon/plugin-source-watcher.ts +146 -0
  248. package/src/daemon/process-message.ts +21 -3
  249. package/src/daemon/server.ts +11 -2
  250. package/src/daemon/skill-memory-refresh.ts +29 -0
  251. package/src/documents/document-store.ts +221 -3
  252. package/src/embedded/plugin-api.ts +40 -0
  253. package/src/filing/filing-service.ts +39 -0
  254. package/src/heartbeat/__tests__/heartbeat-service.test.ts +91 -6
  255. package/src/heartbeat/heartbeat-run-store.ts +2 -1
  256. package/src/heartbeat/heartbeat-service.ts +41 -0
  257. package/src/home/__tests__/feed-types.test.ts +40 -0
  258. package/src/home/feed-types.ts +22 -0
  259. package/src/home/post-connect-feed.ts +1 -0
  260. package/src/index.ts +18 -1
  261. package/src/live-voice/__tests__/live-voice-stt.test.ts +57 -0
  262. package/src/mcp/client.ts +20 -4
  263. package/src/media/image-credentials.ts +3 -3
  264. package/src/memory/__tests__/bookmark-crud.test.ts +33 -27
  265. package/src/memory/__tests__/conversation-queries.test.ts +263 -0
  266. package/src/memory/__tests__/jobs-worker-v2-graph-trigger-embed.test.ts +113 -0
  267. package/src/memory/__tests__/memory-retrospective-startup-cleanup.test.ts +119 -14
  268. package/src/memory/__tests__/message-content.test.ts +35 -0
  269. package/src/memory/bookmark-crud.ts +42 -10
  270. package/src/memory/context-search/sources/conversations.ts +62 -2
  271. package/src/memory/context-search/sources/workspace.ts +4 -0
  272. package/src/memory/conversation-crud.ts +63 -19
  273. package/src/memory/conversation-queries.ts +110 -10
  274. package/src/memory/db-init.ts +6 -0
  275. package/src/memory/delivery-crud.ts +152 -5
  276. package/src/memory/embedding-backend.ts +4 -4
  277. package/src/memory/external-conversation-store.ts +66 -5
  278. package/src/memory/graph/__tests__/conversation-graph-memory-v2-routing.test.ts +66 -9
  279. package/src/memory/graph/conversation-graph-memory.ts +31 -15
  280. package/src/memory/graph/tools.ts +3 -3
  281. package/src/memory/indexer.ts +34 -29
  282. package/src/memory/jobs/__tests__/embed-concept-page.test.ts +73 -0
  283. package/src/memory/jobs/embed-concept-page.ts +20 -11
  284. package/src/memory/jobs-worker.ts +6 -1
  285. package/src/memory/llm-request-log-source-clickhouse.ts +17 -10
  286. package/src/memory/llm-request-log-source.ts +19 -52
  287. package/src/memory/llm-usage-store.ts +125 -5
  288. package/src/memory/memory-retrospective-startup-cleanup.ts +72 -5
  289. package/src/memory/message-content.ts +1 -1
  290. package/src/memory/migrations/109-external-conversation-bindings.ts +15 -4
  291. package/src/memory/migrations/229-delete-private-conversations.test.ts +38 -1
  292. package/src/memory/migrations/229-delete-private-conversations.ts +7 -0
  293. package/src/memory/migrations/247-external-conversation-binding-thread-id.ts +78 -0
  294. package/src/memory/migrations/248-create-onboarding-events.ts +21 -0
  295. package/src/memory/migrations/249-normalize-slack-external-content.ts +240 -0
  296. package/src/memory/migrations/index.ts +6 -0
  297. package/src/memory/migrations/registry.ts +8 -0
  298. package/src/memory/onboarding-events-store.ts +106 -0
  299. package/src/memory/schema/bookmarks.ts +0 -2
  300. package/src/memory/schema/calls.ts +1 -0
  301. package/src/memory/schema/inference.ts +1 -3
  302. package/src/memory/schema/infrastructure.ts +12 -0
  303. package/src/memory/turn-events-store.ts +127 -2
  304. package/src/memory/v2/__tests__/activation.test.ts +0 -8
  305. package/src/memory/v2/__tests__/injection.test.ts +98 -8
  306. package/src/memory/v2/__tests__/migration.test.ts +87 -0
  307. package/src/memory/v2/__tests__/page-index.test.ts +83 -0
  308. package/src/memory/v2/__tests__/prompts-router.test.ts +58 -6
  309. package/src/memory/v2/__tests__/qdrant.test.ts +66 -3
  310. package/src/memory/v2/__tests__/router.test.ts +15 -0
  311. package/src/memory/v2/__tests__/skill-store.test.ts +387 -8
  312. package/src/memory/v2/injection.ts +32 -6
  313. package/src/memory/v2/migration.ts +49 -19
  314. package/src/memory/v2/page-index.ts +35 -5
  315. package/src/memory/v2/prompts/router.ts +11 -8
  316. package/src/memory/v2/prompts/sweep.ts +2 -2
  317. package/src/memory/v2/qdrant.ts +135 -7
  318. package/src/memory/v2/router.ts +9 -8
  319. package/src/memory/v2/skill-store.ts +120 -35
  320. package/src/messaging/providers/slack/__tests__/adapter-token-routing.test.ts +45 -5
  321. package/src/messaging/providers/slack/__tests__/download.test.ts +231 -0
  322. package/src/messaging/providers/slack/adapter.ts +43 -5
  323. package/src/messaging/providers/slack/client.ts +27 -0
  324. package/src/messaging/providers/slack/deep-link.ts +65 -0
  325. package/src/messaging/providers/slack/download.ts +104 -0
  326. package/src/messaging/providers/slack/message-metadata.test.ts +32 -0
  327. package/src/messaging/providers/slack/message-metadata.ts +27 -0
  328. package/src/messaging/providers/slack/render-transcript.test.ts +134 -0
  329. package/src/messaging/providers/slack/render-transcript.ts +69 -5
  330. package/src/messaging/providers/slack/types.ts +20 -1
  331. package/src/notifications/conversation-pairing.ts +2 -1
  332. package/src/notifications/decision-engine.ts +2 -1
  333. package/src/notifications/emit-signal.ts +20 -1
  334. package/src/notifications/home-feed-side-effect.ts +54 -0
  335. package/src/notifications/signal.ts +3 -1
  336. package/src/oauth/connection-resolver.ts +8 -4
  337. package/src/oauth/platform-connection.ts +6 -2
  338. package/src/oauth/seed-providers.ts +10 -1
  339. package/src/permissions/checker.ts +2 -0
  340. package/src/permissions/ipc-risk-types.ts +1 -0
  341. package/src/permissions/question-prompter.test.ts +416 -0
  342. package/src/permissions/question-prompter.ts +294 -0
  343. package/src/platform/client.test.ts +1 -1
  344. package/src/platform/client.ts +1 -1
  345. package/src/plugin-api/constants.ts +26 -0
  346. package/src/plugin-api/index.ts +34 -1
  347. package/src/plugin-api/types.ts +104 -22
  348. package/src/plugins/defaults/circuit-breaker.ts +0 -5
  349. package/src/plugins/defaults/compaction.ts +0 -4
  350. package/src/plugins/defaults/empty-response.ts +0 -2
  351. package/src/plugins/defaults/history-repair.ts +0 -2
  352. package/src/plugins/defaults/injectors.ts +36 -3
  353. package/src/plugins/defaults/llm-call.ts +0 -2
  354. package/src/plugins/defaults/memory-retrieval.ts +0 -1
  355. package/src/plugins/defaults/overflow-reduce.ts +0 -1
  356. package/src/plugins/defaults/persistence.ts +0 -2
  357. package/src/plugins/defaults/title-generate.ts +0 -5
  358. package/src/plugins/defaults/token-estimate.ts +0 -2
  359. package/src/plugins/defaults/tool-error.ts +0 -7
  360. package/src/plugins/defaults/tool-execute.ts +0 -2
  361. package/src/plugins/defaults/tool-result-truncate.ts +0 -4
  362. package/src/plugins/ensure-plugin-api-shim.ts +96 -0
  363. package/src/plugins/external-api.ts +104 -0
  364. package/src/plugins/external-plugin-loader.ts +105 -32
  365. package/src/plugins/feature-gate.ts +22 -0
  366. package/src/plugins/pipeline.ts +37 -0
  367. package/src/plugins/registry.ts +48 -80
  368. package/src/plugins/types.ts +31 -26
  369. package/src/plugins/user-loader.ts +21 -2
  370. package/src/proactive-artifact/aux-message-injector.ts +11 -0
  371. package/src/proactive-artifact/job.test.ts +37 -5
  372. package/src/prompts/__tests__/system-prompt.test.ts +12 -0
  373. package/src/prompts/__tests__/task-progress-hint-section.test.ts +99 -0
  374. package/src/prompts/normalize-onboarding.ts +27 -0
  375. package/src/prompts/sections.ts +302 -0
  376. package/src/prompts/system-prompt.ts +63 -166
  377. package/src/prompts/templates/BOOTSTRAP.md +17 -1
  378. package/src/prompts/templates/system-sections.ts +173 -0
  379. package/src/providers/__tests__/inference.test.ts +22 -7
  380. package/src/providers/anthropic/client.ts +28 -28
  381. package/src/providers/connection-resolution.ts +7 -0
  382. package/src/providers/inference/adapter-factory.ts +41 -4
  383. package/src/providers/inference/connections.ts +74 -29
  384. package/src/providers/inference/resolve-auth.ts +12 -4
  385. package/src/providers/model-catalog.ts +294 -12
  386. package/src/providers/openai/chat-completions-provider.ts +10 -2
  387. package/src/providers/openrouter/client.ts +7 -0
  388. package/src/providers/{managed-proxy → platform-proxy}/constants.ts +4 -1
  389. package/src/providers/{managed-proxy → platform-proxy}/context.ts +3 -3
  390. package/src/providers/provider-availability.ts +17 -2
  391. package/src/providers/provider-catalog-visibility.ts +36 -0
  392. package/src/providers/registry.ts +22 -14
  393. package/src/providers/retry.ts +47 -1
  394. package/src/runtime/__tests__/agent-wake.test.ts +152 -0
  395. package/src/runtime/agent-wake.ts +42 -14
  396. package/src/runtime/auth/route-policy.ts +8 -1
  397. package/src/runtime/btw-sidechain.ts +2 -0
  398. package/src/runtime/http-types.ts +19 -0
  399. package/src/runtime/migrations/origin-mode.ts +1 -1
  400. package/src/runtime/pending-interactions.ts +1 -0
  401. package/src/runtime/routes/__tests__/bookmark-routes.test.ts +17 -0
  402. package/src/runtime/routes/__tests__/conversation-management-routes.test.ts +5 -1
  403. package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +107 -20
  404. package/src/runtime/routes/__tests__/question-routes.test.ts +395 -0
  405. package/src/runtime/routes/__tests__/tts-routes.test.ts +64 -1
  406. package/src/runtime/routes/acp-routes-list.test.ts +143 -0
  407. package/src/runtime/routes/acp-routes.ts +5 -3
  408. package/src/runtime/routes/auth-routes.ts +1 -1
  409. package/src/runtime/routes/bookmark-routes.ts +5 -3
  410. package/src/runtime/routes/btw-routes.ts +5 -1
  411. package/src/runtime/routes/channel-availability-routes.ts +121 -0
  412. package/src/runtime/routes/conversation-cli-routes.ts +44 -3
  413. package/src/runtime/routes/conversation-list-routes.ts +3 -20
  414. package/src/runtime/routes/conversation-management-routes.ts +17 -42
  415. package/src/runtime/routes/conversation-query-routes.ts +40 -35
  416. package/src/runtime/routes/conversation-routes.ts +90 -11
  417. package/src/runtime/routes/documents-routes.ts +25 -86
  418. package/src/runtime/routes/group-routes.ts +5 -0
  419. package/src/runtime/routes/inbound-conversation.ts +28 -8
  420. package/src/runtime/routes/inbound-message-handler.ts +236 -41
  421. package/src/runtime/routes/inbound-stages/background-dispatch.test.ts +111 -0
  422. package/src/runtime/routes/inbound-stages/background-dispatch.ts +32 -1
  423. package/src/runtime/routes/inbound-stages/edit-intercept.ts +17 -4
  424. package/src/runtime/routes/index.ts +6 -0
  425. package/src/runtime/routes/inference-profile-session-handler.ts +17 -44
  426. package/src/runtime/routes/inference-profile-session-reaper.ts +7 -21
  427. package/src/runtime/routes/inference-provider-connection-routes.ts +65 -21
  428. package/src/runtime/routes/integrations/slack/share.ts +4 -52
  429. package/src/runtime/routes/integrations/slack/token.ts +43 -0
  430. package/src/runtime/routes/integrations/twilio.ts +6 -13
  431. package/src/runtime/routes/notification-routes.ts +1 -1
  432. package/src/runtime/routes/oauth-commands-routes.ts +105 -15
  433. package/src/runtime/routes/oauth-lifecycle-routes.ts +43 -0
  434. package/src/runtime/routes/question-routes.ts +259 -0
  435. package/src/runtime/routes/rename-conversation-routes.ts +2 -33
  436. package/src/runtime/routes/schedule-routes.ts +4 -7
  437. package/src/runtime/routes/subagents-routes.ts +57 -18
  438. package/src/runtime/routes/telemetry-routes.ts +27 -0
  439. package/src/runtime/routes/tts-routes.ts +27 -2
  440. package/src/runtime/routes/workspace-routes.test.ts +43 -0
  441. package/src/runtime/routes/workspace-routes.ts +28 -0
  442. package/src/runtime/services/conversation-serializer.ts +39 -7
  443. package/src/runtime/sync/resource-sync-events.ts +93 -1
  444. package/src/schedule/schedule-store.ts +27 -2
  445. package/src/schedule/scheduler.ts +9 -1
  446. package/src/security/__tests__/untrusted-content.test.ts +86 -0
  447. package/src/security/untrusted-content.ts +93 -8
  448. package/src/skills/catalog-files.ts +1 -1
  449. package/src/skills/catalog-install.ts +233 -116
  450. package/src/skills/clawhub.ts +70 -13
  451. package/src/skills/managed-store.ts +4 -119
  452. package/src/skills/skillssh-registry.ts +27 -48
  453. package/src/subagent/manager.ts +15 -7
  454. package/src/telemetry/types.ts +113 -1
  455. package/src/telemetry/usage-telemetry-reporter.test.ts +312 -5
  456. package/src/telemetry/usage-telemetry-reporter.ts +113 -7
  457. package/src/tools/apps/executors.ts +58 -7
  458. package/src/tools/ask-question/ask-question-tool.test.ts +509 -0
  459. package/src/tools/ask-question/ask-question-tool.ts +304 -0
  460. package/src/tools/browser/browser-execution.ts +15 -11
  461. package/src/tools/computer-use/definitions.ts +3 -3
  462. package/src/tools/credentials/vault.ts +1 -1
  463. package/src/tools/document/document-tool.ts +124 -1
  464. package/src/tools/filesystem/edit.ts +1 -1
  465. package/src/tools/filesystem/list.ts +1 -1
  466. package/src/tools/filesystem/read.ts +1 -1
  467. package/src/tools/filesystem/write.ts +5 -2
  468. package/src/tools/host-filesystem/transfer.ts +1 -1
  469. package/src/tools/host-terminal/host-shell.ts +1 -1
  470. package/src/tools/permission-checker.ts +1 -1
  471. package/src/tools/registry.ts +17 -7
  472. package/src/tools/schedule/create.ts +2 -2
  473. package/src/tools/schema-transforms.ts +7 -2
  474. package/src/tools/side-effects.ts +1 -0
  475. package/src/tools/skills/delete-managed.ts +4 -4
  476. package/src/tools/skills/execute.ts +1 -1
  477. package/src/tools/skills/scaffold-managed.ts +3 -2
  478. package/src/tools/subagent/notify-parent.ts +1 -1
  479. package/src/tools/system/request-permission.ts +2 -2
  480. package/src/tools/terminal/safe-env.ts +60 -1
  481. package/src/tools/tool-manifest.ts +2 -0
  482. package/src/tools/types.ts +72 -21
  483. package/src/tools/ui-surface/definitions.ts +6 -5
  484. package/src/tts/__tests__/provider-adapters.test.ts +76 -2
  485. package/src/tts/providers/elevenlabs-provider.ts +75 -1
  486. package/src/types/onboarding-context.ts +2 -0
  487. package/src/util/errors.ts +17 -0
  488. package/src/util/platform.ts +10 -0
  489. package/src/watcher/__tests__/engine.test.ts +22 -0
  490. package/src/watcher/engine.ts +6 -2
  491. package/src/workspace/migrations/057-repair-stale-gemini-model-ids.ts +80 -15
  492. package/src/workspace/migrations/072-seed-reply-suggestion-callsite.ts +35 -22
  493. package/src/workspace/migrations/073-repair-recall-callsite-empty-profile.ts +3 -1
  494. package/src/workspace/migrations/083-system-prompt-prefix-to-file.ts +191 -0
  495. package/src/workspace/migrations/084-remove-legacy-skills-index.ts +276 -0
  496. package/src/workspace/migrations/085-memory-v2-bm25-b-reembed-disabled-v2-pages.ts +137 -0
  497. package/src/workspace/migrations/086-revert-stale-gemini-mis-rewrites.ts +198 -0
  498. package/src/workspace/migrations/registry.ts +8 -0
  499. package/src/workspace/migrations/runner.ts +39 -9
  500. package/src/workspace/migrations/types.ts +4 -0
  501. package/examples/plugins/echo/bun.lock +0 -25
  502. package/src/__tests__/context-window-manager.test.ts +0 -2481
  503. package/src/context/__tests__/compact-prompt.test.ts +0 -63
  504. package/src/context/prompts/compact.md +0 -26
  505. package/src/prompts/__tests__/build-cli-reference-section.test.ts +0 -37
  506. /package/src/__tests__/{secret-routes-managed-proxy.test.ts → secret-routes-platform-proxy.test.ts} +0 -0
@@ -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
  };
@@ -0,0 +1,122 @@
1
+ import { beforeEach, describe, expect, mock, test } from "bun:test";
2
+
3
+ import { makeMockLogger } from "./helpers/mock-logger.js";
4
+
5
+ mock.module("../util/logger.js", () => ({
6
+ getLogger: () => makeMockLogger(),
7
+ }));
8
+
9
+ import {
10
+ countMessagesAfter,
11
+ getMessagesAfter,
12
+ } from "../memory/conversation-crud.js";
13
+ import { getDb } from "../memory/db-connection.js";
14
+ import { initializeDb } from "../memory/db-init.js";
15
+ import { conversations, messages } from "../memory/schema.js";
16
+
17
+ initializeDb();
18
+
19
+ const CONV_ID = "conv-tiebreaker";
20
+
21
+ function clearDb(): void {
22
+ const db = getDb();
23
+ db.delete(messages).run();
24
+ db.delete(conversations).run();
25
+ }
26
+
27
+ function seedConversation(): void {
28
+ const now = Date.now();
29
+ getDb()
30
+ .insert(conversations)
31
+ .values({
32
+ id: CONV_ID,
33
+ title: null,
34
+ createdAt: now,
35
+ updatedAt: now,
36
+ source: "test",
37
+ conversationType: "default",
38
+ memoryScopeId: "default",
39
+ })
40
+ .run();
41
+ }
42
+
43
+ function insertMessage(id: string, createdAt: number): void {
44
+ getDb()
45
+ .insert(messages)
46
+ .values({
47
+ id,
48
+ conversationId: CONV_ID,
49
+ role: "user",
50
+ content: "",
51
+ createdAt,
52
+ metadata: null,
53
+ })
54
+ .run();
55
+ }
56
+
57
+ describe("countMessagesAfter / getMessagesAfter — millisecond-collision tie-breaker", () => {
58
+ beforeEach(() => {
59
+ clearDb();
60
+ seedConversation();
61
+ });
62
+
63
+ test("messages sharing a millisecond timestamp with the reference are NOT permanently skipped (countMessagesAfter)", () => {
64
+ const ts = 1_700_000_000_000;
65
+ // Both messages share the exact same createdAt — id "b" sorts after
66
+ // id "a" lexicographically. Without a tie-breaker the second message
67
+ // would never be counted.
68
+ insertMessage("a", ts);
69
+ insertMessage("b", ts);
70
+
71
+ expect(countMessagesAfter(CONV_ID, "a")).toBe(1);
72
+ });
73
+
74
+ test("messages sharing a millisecond timestamp with the reference are NOT permanently skipped (getMessagesAfter)", () => {
75
+ const ts = 1_700_000_000_000;
76
+ insertMessage("a", ts);
77
+ insertMessage("b", ts);
78
+
79
+ const result = getMessagesAfter(CONV_ID, "a");
80
+ expect(result.map((m) => m.id)).toEqual(["b"]);
81
+ });
82
+
83
+ test("strict-after semantics: a message with id sorting BEFORE the reference but identical timestamp is NOT included", () => {
84
+ const ts = 1_700_000_000_000;
85
+ // "a" sorts before "b". Reference is "b", so "a" should be excluded
86
+ // (strictly-after semantics, not "all rows at the same timestamp").
87
+ insertMessage("a", ts);
88
+ insertMessage("b", ts);
89
+
90
+ expect(countMessagesAfter(CONV_ID, "b")).toBe(0);
91
+ expect(getMessagesAfter(CONV_ID, "b")).toEqual([]);
92
+ });
93
+
94
+ test("mixed collision + later timestamps: counts both same-ts tie-breaker rows and strictly-later rows", () => {
95
+ const ts = 1_700_000_000_000;
96
+ insertMessage("a", ts);
97
+ insertMessage("b", ts);
98
+ insertMessage("c", ts);
99
+ insertMessage("d", ts + 1);
100
+
101
+ expect(countMessagesAfter(CONV_ID, "a")).toBe(3);
102
+ const result = getMessagesAfter(CONV_ID, "a");
103
+ expect(result.map((m) => m.id)).toEqual(["b", "c", "d"]);
104
+ });
105
+
106
+ test("missing reference returns 0/[] (conservative semantics preserved)", () => {
107
+ insertMessage("a", 1_700_000_000_000);
108
+ expect(countMessagesAfter(CONV_ID, "nonexistent")).toBe(0);
109
+ expect(getMessagesAfter(CONV_ID, "nonexistent")).toEqual([]);
110
+ });
111
+
112
+ test("null/empty reference still returns all messages", () => {
113
+ const ts = 1_700_000_000_000;
114
+ insertMessage("a", ts);
115
+ insertMessage("b", ts);
116
+
117
+ expect(countMessagesAfter(CONV_ID, null)).toBe(2);
118
+ expect(countMessagesAfter(CONV_ID, "")).toBe(2);
119
+ expect(getMessagesAfter(CONV_ID, null)).toHaveLength(2);
120
+ expect(getMessagesAfter(CONV_ID, "")).toHaveLength(2);
121
+ });
122
+ });