@vellumai/assistant 0.12.2 → 0.12.3-staging.1

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 (298) hide show
  1. package/.env.example +2 -0
  2. package/AGENTS.md +1 -1
  3. package/docs/architecture/integrations.md +7 -0
  4. package/docs/architecture/memory.md +17 -2
  5. package/docs/credential-execution-service.md +1 -1
  6. package/docs/desktop-browser-cli.md +7 -3
  7. package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/__tests__/secret-detection.test.ts +1 -0
  8. package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/secret-detection.ts +4 -1
  9. package/node_modules/@vellumai/environments/src/shell.test.ts +21 -0
  10. package/node_modules/@vellumai/environments/src/shell.ts +24 -0
  11. package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/__tests__/secret-detection.test.ts +1 -0
  12. package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/secret-detection.ts +4 -1
  13. package/node_modules/@vellumai/gateway-client/src/inbound-contract.ts +8 -2
  14. package/node_modules/@vellumai/service-contracts/src/__tests__/secret-detection.test.ts +1 -0
  15. package/node_modules/@vellumai/service-contracts/src/secret-detection.ts +4 -1
  16. package/openapi.yaml +54 -5
  17. package/package.json +1 -1
  18. package/scripts/smoke-desktop-browser-cli.ts +1 -0
  19. package/scripts/sync-llm-catalog.ts +3 -0
  20. package/scripts/voice-ttft-spike.ts +2 -2
  21. package/src/__tests__/agent-loop.test.ts +259 -0
  22. package/src/__tests__/agent-wake-delegation-prompt.test.ts +64 -2
  23. package/src/__tests__/anthropic-provider.test.ts +55 -0
  24. package/src/__tests__/approval-interception-trust-gates.test.ts +40 -0
  25. package/src/__tests__/attachments-store.test.ts +22 -3
  26. package/src/__tests__/channel-approval.test.ts +9 -14
  27. package/src/__tests__/channel-reply-delivery.test.ts +56 -0
  28. package/src/__tests__/chat-credential-redaction.test.ts +23 -0
  29. package/src/__tests__/computer-use-screenshot-attachments.test.ts +411 -0
  30. package/src/__tests__/computer-use-screenshot-selection.test.ts +50 -0
  31. package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +2 -0
  32. package/src/__tests__/conversation-agent-loop-overflow.test.ts +2 -0
  33. package/src/__tests__/conversation-agent-loop.test.ts +257 -6
  34. package/src/__tests__/conversation-attachments.test.ts +106 -0
  35. package/src/__tests__/conversation-fork-crud.test.ts +191 -0
  36. package/src/__tests__/conversation-rendered-delegation-state.test.ts +276 -0
  37. package/src/__tests__/credential-execution-client.test.ts +70 -53
  38. package/src/__tests__/db-conversation-tool-surface.test.ts +221 -0
  39. package/src/__tests__/events-tail-route.test.ts +33 -0
  40. package/src/__tests__/history-repair.test.ts +53 -0
  41. package/src/__tests__/inference-profile-session-handler.test.ts +26 -0
  42. package/src/__tests__/list-messages-tool-merge.test.ts +85 -1
  43. package/src/__tests__/llm-catalog-parity.test.ts +27 -4
  44. package/src/__tests__/oauth-apps-routes.test.ts +1 -0
  45. package/src/__tests__/oauth-commands-routes.test.ts +114 -101
  46. package/src/__tests__/oauth-connect-orchestrator.test.ts +2 -0
  47. package/src/__tests__/oauth-provider-serializer.test.ts +1 -0
  48. package/src/__tests__/oauth-providers-routes.test.ts +2 -0
  49. package/src/__tests__/persist-media-references.test.ts +50 -0
  50. package/src/__tests__/plugin-import-boundary-guard.test.ts +0 -1
  51. package/src/__tests__/run-conversation-turn-persistence.test.ts +138 -1
  52. package/src/__tests__/schedule-routes.test.ts +20 -0
  53. package/src/__tests__/scheduler-result-notification.test.ts +23 -4
  54. package/src/__tests__/script-proxy-certs.test.ts +1 -1
  55. package/src/__tests__/secret-routes-platform-proxy.test.ts +86 -31
  56. package/src/__tests__/secret-routes-scrub.test.ts +22 -27
  57. package/src/__tests__/secret-scanner.test.ts +20 -0
  58. package/src/__tests__/secure-keys-managed-failover.test.ts +8 -0
  59. package/src/__tests__/secure-keys.test.ts +7 -3
  60. package/src/__tests__/server-tool-pairing.test.ts +107 -0
  61. package/src/__tests__/skills.test.ts +5 -4
  62. package/src/__tests__/subagent-tool-gate-mode.test.ts +226 -0
  63. package/src/__tests__/terminal-tools.test.ts +8 -0
  64. package/src/__tests__/tool-result-follow-up.test.ts +99 -0
  65. package/src/__tests__/tool-result-metadata-plumbing.test.ts +63 -0
  66. package/src/__tests__/unicode.test.ts +36 -0
  67. package/src/agent/loop.ts +37 -12
  68. package/src/agent/tool-result-follow-up.ts +70 -0
  69. package/src/api/attachment-provenance.test.ts +68 -0
  70. package/src/api/computer-use-tool.test.ts +55 -0
  71. package/src/api/computer-use-tool.ts +27 -0
  72. package/src/api/events/assistant-outbound-attachment.ts +3 -0
  73. package/src/api/events/desktop-activity-changed.ts +10 -0
  74. package/src/api/events/question-request.ts +1 -0
  75. package/src/api/index.ts +11 -0
  76. package/src/api/responses/conversation-message.ts +3 -0
  77. package/src/approvals/approval-primitive.ts +5 -2
  78. package/src/approvals/scoped-approval-grants.ts +6 -2
  79. package/src/browser/virtual-desktop-target.ts +1 -2
  80. package/src/cli/commands/__tests__/cli-test-harness.ts +21 -3
  81. package/src/cli/commands/__tests__/plugins.test.ts +20 -1
  82. package/src/cli/commands/__tests__/schedules.test.ts +14 -0
  83. package/src/cli/commands/bash.help.ts +4 -3
  84. package/src/cli/commands/browser.help.ts +5 -1
  85. package/src/cli/commands/credentials.help.ts +3 -3
  86. package/src/cli/commands/oauth/index.help.ts +10 -0
  87. package/src/cli/commands/oauth/providers.ts +12 -0
  88. package/src/cli/commands/oauth/request.test.ts +179 -2
  89. package/src/cli/commands/oauth/request.ts +41 -10
  90. package/src/cli/commands/plugins.ts +12 -5
  91. package/src/cli/commands/schedules.ts +2 -0
  92. package/src/cli/lib/__tests__/inspect-plugin.test.ts +54 -0
  93. package/src/cli/lib/__tests__/install-from-github.test.ts +41 -0
  94. package/src/cli/lib/__tests__/local-plugin-upgrade.test.ts +133 -51
  95. package/src/cli/lib/__tests__/plugin-catalog-cache.test.ts +8 -2
  96. package/src/cli/lib/bundled-marketplace.json +855 -0
  97. package/src/cli/lib/bundled-plugin-packages.json +783 -1
  98. package/src/cli/lib/inspect-plugin.ts +11 -4
  99. package/src/cli/lib/upgrade-plugin.ts +19 -11
  100. package/src/cli/output.ts +18 -4
  101. package/src/config/__tests__/default-provider.test.ts +4 -0
  102. package/src/config/bundled-skills/schedule/SKILL.md +6 -6
  103. package/src/config/loader.ts +9 -17
  104. package/src/config/platform-identity.ts +8 -8
  105. package/src/config/profile-text-generation.test.ts +51 -0
  106. package/src/config/profile-text-generation.ts +51 -0
  107. package/src/config/schemas/__tests__/memory-v3.test.ts +12 -0
  108. package/src/config/schemas/llm.ts +7 -2
  109. package/src/config/schemas/mcp.ts +5 -1
  110. package/src/config/schemas/memory-v3.ts +12 -0
  111. package/src/credential-execution/ces-runtime.ts +2 -2
  112. package/src/credential-execution/executable-discovery.ts +36 -134
  113. package/src/credential-execution/process-manager.test.ts +20 -24
  114. package/src/credential-execution/process-manager.ts +24 -19
  115. package/src/daemon/__tests__/conversation-tool-setup.test.ts +43 -0
  116. package/src/daemon/__tests__/turn-tail-deleted-conversation.test.ts +36 -0
  117. package/src/daemon/assistant-attachments.ts +20 -12
  118. package/src/daemon/chat-credential-redaction.ts +16 -1
  119. package/src/daemon/conversation-agent-loop-handlers.ts +74 -3
  120. package/src/daemon/conversation-agent-loop.ts +12 -0
  121. package/src/daemon/conversation-attachments.ts +93 -12
  122. package/src/daemon/conversation-tool-setup.ts +71 -3
  123. package/src/daemon/conversation-turn-finalize.ts +26 -13
  124. package/src/daemon/conversation.ts +72 -17
  125. package/src/daemon/daemon-control.ts +3 -7
  126. package/src/daemon/lifecycle.ts +4 -4
  127. package/src/daemon/mcp-reload-service.ts +1 -1
  128. package/src/daemon/message-types/shared.ts +1 -0
  129. package/src/daemon/orphan-reaper.ts +4 -3
  130. package/src/daemon/persist-media-references.ts +20 -3
  131. package/src/daemon/tool-setup-types.ts +6 -0
  132. package/src/daemon/wake-conversation-ops.ts +50 -15
  133. package/src/desktop/desktop-automation-lease.test.ts +230 -0
  134. package/src/desktop/desktop-automation-lease.ts +90 -6
  135. package/src/desktop/desktop-help.ts +15 -0
  136. package/src/desktop/desktop-stream-bridge.test.ts +65 -10
  137. package/src/desktop/desktop-stream-bridge.ts +3 -3
  138. package/src/desktop/desktop-wallpaper-renderer.ts +132 -0
  139. package/src/desktop/desktop-wallpaper-worker.ts +17 -0
  140. package/src/desktop/desktop-wallpaper.test.ts +48 -7
  141. package/src/desktop/desktop-wallpaper.ts +34 -102
  142. package/src/desktop/virtual-desktop-feature.ts +1 -1
  143. package/src/mcp/__tests__/manager-tool-caps.test.ts +111 -0
  144. package/src/mcp/__tests__/startup.test.ts +30 -7
  145. package/src/mcp/__tests__/tool-caps.test.ts +107 -0
  146. package/src/mcp/manager.ts +168 -101
  147. package/src/mcp/startup.ts +33 -13
  148. package/src/mcp/tool-caps.ts +176 -0
  149. package/src/messaging/provider-message-metadata.ts +3 -3
  150. package/src/notifications/__tests__/copy-composer.test.ts +70 -0
  151. package/src/notifications/copy-composer.ts +11 -3
  152. package/src/notifications/schedule-result-producer.ts +4 -6
  153. package/src/oauth/AGENTS.md +2 -0
  154. package/src/oauth/__tests__/identity-verifier.test.ts +36 -1
  155. package/src/oauth/identity-verifier.ts +24 -0
  156. package/src/oauth/oauth-store.ts +11 -1
  157. package/src/oauth/provider-serializer.ts +1 -0
  158. package/src/oauth/seed-providers.ts +12 -0
  159. package/src/permissions/confirmation-guardian-request.test.ts +16 -0
  160. package/src/permissions/confirmation-guardian-request.ts +2 -3
  161. package/src/permissions/question-prompter.test.ts +31 -0
  162. package/src/permissions/question-prompter.ts +2 -0
  163. package/src/persistence/attachments-store.ts +84 -44
  164. package/src/persistence/conversation-crud.ts +111 -20
  165. package/src/persistence/conversation-plugin-facade.ts +30 -0
  166. package/src/persistence/conversation-tool-surface.ts +135 -0
  167. package/src/persistence/conversation-types.test.ts +32 -0
  168. package/src/persistence/conversation-types.ts +26 -4
  169. package/src/persistence/migrations/378-create-conversation-tool-surfaces.test.ts +78 -0
  170. package/src/persistence/migrations/378-create-conversation-tool-surfaces.ts +29 -0
  171. package/src/persistence/migrations/379-oauth-providers-response-ok-field.test.ts +92 -0
  172. package/src/persistence/migrations/379-oauth-providers-response-ok-field.ts +32 -0
  173. package/src/persistence/migrations/380-conversation-tool-surfaces-delegate-independent-tasks.test.ts +90 -0
  174. package/src/persistence/migrations/380-conversation-tool-surfaces-delegate-independent-tasks.ts +34 -0
  175. package/src/persistence/schema/conversation-tool-surfaces.ts +32 -0
  176. package/src/persistence/schema/index.ts +1 -0
  177. package/src/persistence/schema/oauth.ts +1 -0
  178. package/src/persistence/steps.ts +24 -3
  179. package/src/plugin-api/conversation-turn.ts +31 -7
  180. package/src/plugin-api/index.ts +11 -1
  181. package/src/plugin-api/plugin-channel-turn-trust.test.ts +133 -0
  182. package/src/plugin-api/plugin-channel-turn-trust.ts +71 -0
  183. package/src/plugins/defaults/memory/AGENTS.md +39 -5
  184. package/src/plugins/defaults/memory/__tests__/buffer-file.test.ts +365 -0
  185. package/src/plugins/defaults/memory/__tests__/buffer-format.test.ts +43 -0
  186. package/src/plugins/defaults/memory/__tests__/conversation-memory-purge.test.ts +1 -0
  187. package/src/plugins/defaults/memory/__tests__/db-memory-attach.test.ts +2 -0
  188. package/src/plugins/defaults/memory/__tests__/fixtures/buffer-appender.ts +17 -0
  189. package/src/plugins/defaults/memory/__tests__/memory-retrospective-job.test.ts +74 -0
  190. package/src/plugins/defaults/memory/__tests__/memory-run-evidence.test.ts +161 -0
  191. package/src/plugins/defaults/memory/__tests__/relocated-memory-test-rows.ts +10 -0
  192. package/src/plugins/defaults/memory/buffer-file.ts +354 -0
  193. package/src/plugins/defaults/memory/buffer-format.ts +40 -0
  194. package/src/plugins/defaults/memory/context-search/agent-runner.ts +1 -2
  195. package/src/plugins/defaults/memory/context-search/format.ts +2 -1
  196. package/src/plugins/defaults/memory/context-search/sources/memory-v2.ts +2 -1
  197. package/src/plugins/defaults/memory/context-search/sources/workspace.ts +2 -1
  198. package/src/plugins/defaults/memory/conversation-memory-purge.ts +4 -0
  199. package/src/plugins/defaults/memory/graph/capability-seed.ts +1 -2
  200. package/src/plugins/defaults/memory/graph/tool-handlers.ts +1 -42
  201. package/src/plugins/defaults/memory/host-utils.ts +0 -10
  202. package/src/plugins/defaults/memory/injectors.ts +4 -3
  203. package/src/plugins/defaults/memory/memory-retrospective-job.ts +72 -183
  204. package/src/plugins/defaults/memory/memory-run-evidence.ts +213 -0
  205. package/src/plugins/defaults/memory/src/memory-item-routes.test.ts +1 -1
  206. package/src/plugins/defaults/memory/substrate/__tests__/consolidation-job.test.ts +407 -99
  207. package/src/plugins/defaults/memory/substrate/__tests__/consolidation-prompt-flag-gating-guard.test.ts +10 -0
  208. package/src/plugins/defaults/memory/substrate/__tests__/prompts-consolidation.test.ts +107 -7
  209. package/src/plugins/defaults/memory/substrate/consolidation-job.ts +307 -86
  210. package/src/plugins/defaults/memory/substrate/consolidation-tool-surface.ts +34 -0
  211. package/src/plugins/defaults/memory/substrate/page-index.ts +2 -1
  212. package/src/plugins/defaults/memory/substrate/prompts/consolidation.ts +89 -49
  213. package/src/plugins/defaults/memory/substrate/sweep-job.ts +1 -1
  214. package/src/plugins/defaults/memory/tools.ts +1 -1
  215. package/src/plugins/defaults/memory/v1/graph/consolidation.ts +2 -2
  216. package/src/plugins/defaults/memory/v1/graph/extraction.ts +2 -1
  217. package/src/plugins/defaults/memory/v1/graph/retriever.ts +1 -1
  218. package/src/plugins/defaults/memory/v2/__tests__/migration.test.ts +5 -0
  219. package/src/plugins/defaults/memory/v2/__tests__/reranker.test.ts +5 -2
  220. package/src/plugins/defaults/memory/v2/reranker.ts +2 -1
  221. package/src/plugins/defaults/memory/v3/__tests__/injection.test.ts +81 -1
  222. package/src/plugins/defaults/memory/v3/__tests__/orchestrate.test.ts +87 -0
  223. package/src/plugins/defaults/memory/v3/__tests__/plugin-schema.test.ts +12 -0
  224. package/src/plugins/defaults/memory/v3/__tests__/pool-log-store.test.ts +183 -4
  225. package/src/plugins/defaults/memory/v3/__tests__/shadow-plugin.test.ts +103 -2
  226. package/src/plugins/defaults/memory/v3/card.ts +2 -1
  227. package/src/plugins/defaults/memory/v3/injector.ts +212 -178
  228. package/src/plugins/defaults/memory/v3/orchestrate.ts +112 -25
  229. package/src/plugins/defaults/memory/v3/plugin-schema.ts +54 -4
  230. package/src/plugins/defaults/memory/v3/pool-log-store.ts +253 -1
  231. package/src/plugins/defaults/memory/v3/pool-select.test.ts +35 -2
  232. package/src/plugins/defaults/memory/v3/pool-select.ts +31 -19
  233. package/src/plugins/defaults/memory/v3/sections.ts +2 -1
  234. package/src/plugins/defaults/memory/v3/shadow-plugin.ts +41 -14
  235. package/src/plugins/defaults/tool-error/hooks/post-tool-use.ts +4 -1
  236. package/src/plugins/defaults/tool-result-truncate/terminal.ts +1 -46
  237. package/src/prompts/__tests__/parallel-tasks-section.test.ts +25 -0
  238. package/src/prompts/delegation-gate.ts +57 -0
  239. package/src/prompts/system-prompt.ts +14 -32
  240. package/src/providers/inference/adapter-factory.ts +6 -0
  241. package/src/providers/jev/client.test.ts +260 -0
  242. package/src/providers/jev/client.ts +518 -0
  243. package/src/providers/model-catalog.ts +58 -3
  244. package/src/providers/server-tool-pairing.ts +16 -7
  245. package/src/runtime/AGENTS.md +2 -2
  246. package/src/runtime/__tests__/agent-wake.test.ts +94 -1
  247. package/src/runtime/agent-wake.ts +31 -4
  248. package/src/runtime/guardian-action-service.ts +2 -17
  249. package/src/runtime/guardian-reply-router.ts +1 -8
  250. package/src/runtime/http-server.ts +2 -2
  251. package/src/runtime/migrations/__tests__/vbundle-import-policy.test.ts +89 -0
  252. package/src/runtime/migrations/vbundle-import-policy.ts +33 -11
  253. package/src/runtime/routes/__tests__/inference-profiles-routes.test.ts +24 -2
  254. package/src/runtime/routes/channel-route-shared.ts +1 -9
  255. package/src/runtime/routes/conversation-routes.ts +29 -4
  256. package/src/runtime/routes/desktop-setup-routes.test.ts +2 -2
  257. package/src/runtime/routes/desktop-setup-routes.ts +8 -5
  258. package/src/runtime/routes/guardian-approval-interception.ts +24 -0
  259. package/src/runtime/routes/inbound-message-handler.ts +2 -3
  260. package/src/runtime/routes/inbound-stages/background-dispatch.test.ts +1 -1
  261. package/src/runtime/routes/inbound-stages/background-dispatch.ts +7 -4
  262. package/src/runtime/routes/inference-profile-session-handler.ts +11 -1
  263. package/src/runtime/routes/inference-profiles-routes.ts +9 -0
  264. package/src/runtime/routes/oauth-commands-routes.ts +57 -54
  265. package/src/runtime/routes/oauth-providers.ts +4 -0
  266. package/src/runtime/routes/oauth-request-hints.test.ts +246 -0
  267. package/src/runtime/routes/oauth-request-hints.ts +149 -0
  268. package/src/runtime/routes/schedule-routes.ts +2 -0
  269. package/src/runtime/routes/secret-routes.ts +99 -119
  270. package/src/schedule/__tests__/worker-mcp-bootstrap.test.ts +107 -0
  271. package/src/schedule/__tests__/worker-mcp-readiness.test.ts +173 -0
  272. package/src/schedule/__tests__/worker-mcp-tools.test.ts +13 -47
  273. package/src/schedule/run-script.ts +2 -2
  274. package/src/schedule/scheduler.ts +47 -11
  275. package/src/schedule/tool-surface-readiness.ts +65 -0
  276. package/src/schedule/worker-mcp.ts +85 -0
  277. package/src/schedule/worker.ts +9 -31
  278. package/src/security/secure-keys.ts +60 -48
  279. package/src/tools/ask-question/ask-question-tool.test.ts +167 -5
  280. package/src/tools/ask-question/ask-question-tool.ts +119 -23
  281. package/src/tools/browser/browser-execution.ts +7 -1
  282. package/src/tools/host-terminal/host-shell.ts +12 -6
  283. package/src/tools/shared/filesystem/file-ops-service.ts +1 -31
  284. package/src/tools/shared/shell-output.test.ts +10 -0
  285. package/src/tools/shared/shell-output.ts +14 -2
  286. package/src/tools/skills/sandbox-runner.ts +13 -2
  287. package/src/tools/skills/scaffold-managed.ts +2 -1
  288. package/src/tools/terminal/__tests__/safe-env.test.ts +33 -4
  289. package/src/tools/terminal/__tests__/sanitized-bash.test.ts +0 -14
  290. package/src/tools/terminal/safe-env.ts +35 -24
  291. package/src/tools/terminal/sanitized-bash.ts +15 -2
  292. package/src/tools/terminal/shell-launch.test.ts +162 -0
  293. package/src/tools/terminal/shell.test.ts +29 -0
  294. package/src/tools/terminal/shell.ts +13 -7
  295. package/src/util/browser-human-verification.ts +14 -0
  296. package/src/util/host-process.test.ts +17 -1
  297. package/src/util/host-process.ts +24 -0
  298. package/src/util/unicode.ts +29 -0
@@ -6,7 +6,7 @@ import { afterEach, describe, expect, test } from "bun:test";
6
6
  import type { FetchLike } from "../fetch-like.js";
7
7
  import { installPlugin, readInstallMeta } from "../install-from-github.js";
8
8
  import type { PluginCatalog } from "../search-plugins.js";
9
- import { upgradePlugin } from "../upgrade-plugin.js";
9
+ import { PluginMergeBaselineError, upgradePlugin } from "../upgrade-plugin.js";
10
10
 
11
11
  const roots: string[] = [];
12
12
 
@@ -82,81 +82,65 @@ function localCatalog(version: string): PluginCatalog {
82
82
  };
83
83
  }
84
84
 
85
+ async function installBundledPlugin(version = "1.0.0") {
86
+ const root = mkdtempSync(join(tmpdir(), "local-plugin-upgrade-"));
87
+ roots.push(root);
88
+ const pluginsDir = join(root, "plugins");
89
+ const calls: string[] = [];
90
+ const materializeLocalPackage = packageMaterializer(calls);
91
+ const installed = await installPlugin(
92
+ {
93
+ name: "fathom",
94
+ trustedSource: {
95
+ kind: "local",
96
+ path: "plugins/mcp-catalog/fathom",
97
+ version,
98
+ },
99
+ },
100
+ {
101
+ fetch: marketplaceFetch(version),
102
+ workspacePluginsDir: pluginsDir,
103
+ materializeLocalPackage,
104
+ },
105
+ );
106
+ return { calls, installed, materializeLocalPackage, pluginsDir };
107
+ }
108
+
85
109
  describe("bundled plugin upgrades", () => {
86
110
  test("ignores a newer live manifest version the current binary cannot materialize", async () => {
87
- const root = mkdtempSync(join(tmpdir(), "local-plugin-upgrade-"));
88
- roots.push(root);
89
- const pluginsDir = join(root, "plugins");
90
- const calls: string[] = [];
91
- const materializeLocalPackage = packageMaterializer(calls);
92
-
93
- await installPlugin(
94
- {
95
- name: "fathom",
96
- trustedSource: {
97
- kind: "local",
98
- path: "plugins/mcp-catalog/fathom",
99
- version: "1.0.0",
100
- },
101
- },
102
- {
103
- fetch: marketplaceFetch("1.0.0"),
104
- workspacePluginsDir: pluginsDir,
105
- materializeLocalPackage,
106
- },
107
- );
111
+ const fixture = await installBundledPlugin();
108
112
 
109
113
  const result = await upgradePlugin(
110
- { name: "fathom" },
114
+ { name: "fathom", strategy: "theirs" },
111
115
  {
112
116
  fetch: marketplaceFetch("9.0.0"),
113
- workspacePluginsDir: pluginsDir,
114
- materializeLocalPackage,
117
+ workspacePluginsDir: fixture.pluginsDir,
118
+ materializeLocalPackage: fixture.materializeLocalPackage,
115
119
  localCatalog: localCatalog("1.0.0"),
116
120
  beforeSwap: async () => {},
117
121
  },
118
122
  );
119
123
 
120
124
  expect(result.outcome).toBe("already-up-to-date");
121
- expect(calls).toEqual(["plugins/mcp-catalog/fathom@1.0.0"]);
125
+ expect(fixture.calls).toEqual(["plugins/mcp-catalog/fathom@1.0.0"]);
122
126
  });
123
127
 
124
128
  test("materializes the exact inspected path and version", async () => {
125
- const root = mkdtempSync(join(tmpdir(), "local-plugin-upgrade-"));
126
- roots.push(root);
127
- const pluginsDir = join(root, "plugins");
128
- const calls: string[] = [];
129
- const materializeLocalPackage = packageMaterializer(calls);
130
-
131
- await installPlugin(
132
- {
133
- name: "fathom",
134
- trustedSource: {
135
- kind: "local",
136
- path: "plugins/mcp-catalog/fathom",
137
- version: "1.0.0",
138
- },
139
- },
140
- {
141
- fetch: marketplaceFetch("1.0.0"),
142
- workspacePluginsDir: pluginsDir,
143
- materializeLocalPackage,
144
- },
145
- );
129
+ const fixture = await installBundledPlugin();
146
130
 
147
131
  const result = await upgradePlugin(
148
- { name: "fathom" },
132
+ { name: "fathom", strategy: "theirs" },
149
133
  {
150
134
  fetch: marketplaceFetch("1.1.0"),
151
- workspacePluginsDir: pluginsDir,
152
- materializeLocalPackage,
135
+ workspacePluginsDir: fixture.pluginsDir,
136
+ materializeLocalPackage: fixture.materializeLocalPackage,
153
137
  localCatalog: localCatalog("1.1.0"),
154
138
  beforeSwap: async () => {},
155
139
  },
156
140
  );
157
141
 
158
142
  expect(result.outcome).toBe("upgraded");
159
- expect(calls).toEqual([
143
+ expect(fixture.calls).toEqual([
160
144
  "plugins/mcp-catalog/fathom@1.0.0",
161
145
  "plugins/mcp-catalog/fathom@1.1.0",
162
146
  ]);
@@ -165,5 +149,103 @@ describe("bundled plugin upgrades", () => {
165
149
  path: "plugins/mcp-catalog/fathom",
166
150
  version: "1.1.0",
167
151
  });
152
+ expect(result.strategy).toBe("theirs");
153
+ });
154
+
155
+ test("refuses a theirs upgrade when the installed package has local edits", async () => {
156
+ const fixture = await installBundledPlugin();
157
+ writeFileSync(
158
+ join(fixture.installed.target, "mcp.json"),
159
+ '{"edited":true}',
160
+ );
161
+
162
+ for (const dryRun of [false, true]) {
163
+ await expect(
164
+ upgradePlugin(
165
+ { name: "fathom", strategy: "theirs", dryRun },
166
+ {
167
+ fetch: marketplaceFetch("1.1.0"),
168
+ workspacePluginsDir: fixture.pluginsDir,
169
+ materializeLocalPackage: fixture.materializeLocalPackage,
170
+ localCatalog: localCatalog("1.1.0"),
171
+ beforeSwap: async () => {},
172
+ },
173
+ ),
174
+ ).rejects.toBeInstanceOf(PluginMergeBaselineError);
175
+ }
176
+
177
+ expect(fixture.calls).toEqual(["plugins/mcp-catalog/fathom@1.0.0"]);
178
+ });
179
+
180
+ test("refuses a theirs upgrade when a clean install cannot be proven", async () => {
181
+ const fixture = await installBundledPlugin();
182
+ const meta = readInstallMeta(fixture.installed.target);
183
+ expect(meta).not.toBeNull();
184
+ writeFileSync(
185
+ join(fixture.installed.target, "install-meta.json"),
186
+ `${JSON.stringify({ ...meta, fingerprint: null }, null, 2)}\n`,
187
+ );
188
+
189
+ await expect(
190
+ upgradePlugin(
191
+ { name: "fathom", strategy: "theirs" },
192
+ {
193
+ fetch: marketplaceFetch("1.1.0"),
194
+ workspacePluginsDir: fixture.pluginsDir,
195
+ materializeLocalPackage: fixture.materializeLocalPackage,
196
+ localCatalog: localCatalog("1.1.0"),
197
+ beforeSwap: async () => {},
198
+ },
199
+ ),
200
+ ).rejects.toBeInstanceOf(PluginMergeBaselineError);
201
+
202
+ expect(fixture.calls).toEqual(["plugins/mcp-catalog/fathom@1.0.0"]);
203
+ });
204
+
205
+ test("preserves explicit overwrite behavior for edited bundled packages", async () => {
206
+ const fixture = await installBundledPlugin();
207
+ writeFileSync(
208
+ join(fixture.installed.target, "mcp.json"),
209
+ '{"edited":true}',
210
+ );
211
+
212
+ const result = await upgradePlugin(
213
+ { name: "fathom", strategy: "overwrite" },
214
+ {
215
+ fetch: marketplaceFetch("1.1.0"),
216
+ workspacePluginsDir: fixture.pluginsDir,
217
+ materializeLocalPackage: fixture.materializeLocalPackage,
218
+ localCatalog: localCatalog("1.1.0"),
219
+ beforeSwap: async () => {},
220
+ },
221
+ );
222
+
223
+ expect(result.outcome).toBe("upgraded");
224
+ expect(result.strategy).toBe("overwrite");
225
+ expect(fixture.calls).toEqual([
226
+ "plugins/mcp-catalog/fathom@1.0.0",
227
+ "plugins/mcp-catalog/fathom@1.1.0",
228
+ ]);
229
+ });
230
+
231
+ test("keeps explicit ours and assistant strategies unavailable for bundled packages", async () => {
232
+ const fixture = await installBundledPlugin();
233
+
234
+ for (const strategy of ["ours", "assistant"] as const) {
235
+ await expect(
236
+ upgradePlugin(
237
+ { name: "fathom", strategy },
238
+ {
239
+ fetch: marketplaceFetch("1.1.0"),
240
+ workspacePluginsDir: fixture.pluginsDir,
241
+ materializeLocalPackage: fixture.materializeLocalPackage,
242
+ localCatalog: localCatalog("1.1.0"),
243
+ beforeSwap: async () => {},
244
+ },
245
+ ),
246
+ ).rejects.toBeInstanceOf(PluginMergeBaselineError);
247
+ }
248
+
249
+ expect(fixture.calls).toEqual(["plugins/mcp-catalog/fathom@1.0.0"]);
168
250
  });
169
251
  });
@@ -31,6 +31,12 @@ import type { PluginCatalog } from "../search-plugins.js";
31
31
  // the real clock", so the fake clock must start from a positive epoch.
32
32
  const BASE_TIME_MS = 1_700_000_000_000;
33
33
 
34
+ function githubNames(catalog: PluginCatalog): string[] {
35
+ return catalog.matches
36
+ .filter((match) => match.source.kind === "github")
37
+ .map((match) => match.name);
38
+ }
39
+
34
40
  /** A `deps.fetch` that serves a `/v1/plugins/` payload and counts its calls. */
35
41
  function platformFetch(names: string[]): {
36
42
  fetch: FetchLike;
@@ -97,7 +103,7 @@ describe("getPluginCatalog", () => {
97
103
  // THEN the platform is fetched exactly once — the second call is cached
98
104
  expect(calls()).toBe(1);
99
105
  expect(first).toBe(second);
100
- expect(first.matches.map((m) => m.name)).toEqual(["a"]);
106
+ expect(githubNames(first)).toEqual(["a"]);
101
107
  expect(first.ref).toBe("main");
102
108
  });
103
109
 
@@ -135,7 +141,7 @@ describe("getPluginCatalog", () => {
135
141
  setSystemTime(new Date(BASE_TIME_MS));
136
142
  const good = platformFetch(["good"]);
137
143
  const cached = await getPluginCatalog("main", { fetch: good.fetch });
138
- expect(cached.matches.map((m) => m.name)).toEqual(["good"]);
144
+ expect(githubNames(cached)).toEqual(["good"]);
139
145
 
140
146
  // WHEN the TTL has elapsed and the refresh fails
141
147
  setSystemTime(new Date(BASE_TIME_MS + PLUGIN_CATALOG_CACHE_TTL_MS + 1));