@vellumai/assistant 0.9.0 → 0.10.0-dev.202606192226.8ed8a6a

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 (624) hide show
  1. package/ARCHITECTURE.md +19 -35
  2. package/bun.lock +7 -8
  3. package/docs/activation-funnel-telemetry.md +28 -22
  4. package/docs/architecture/security.md +29 -28
  5. package/docs/stt-provider-onboarding.md +3 -5
  6. package/docs/workflows-testing.md +13 -44
  7. package/docs/workflows.md +3 -5
  8. package/node_modules/@vellumai/ces-client/src/__tests__/ces-client.test.ts +47 -0
  9. package/node_modules/@vellumai/ces-client/src/rpc-client.ts +28 -5
  10. package/node_modules/@vellumai/environments/src/seeds.ts +2 -5
  11. package/node_modules/@vellumai/gateway-client/src/admission-policy-contract.ts +97 -0
  12. package/node_modules/@vellumai/gateway-client/src/inbound-contract.ts +10 -0
  13. package/node_modules/@vellumai/gateway-client/src/index.ts +32 -6
  14. package/node_modules/@vellumai/gateway-client/src/outbound-contract.ts +119 -0
  15. package/node_modules/@vellumai/gateway-client/src/types.ts +15 -84
  16. package/openapi.yaml +1128 -119
  17. package/package.json +2 -1
  18. package/scripts/sync-llm-catalog.ts +6 -15
  19. package/scripts/sync-web-search-catalog.ts +3 -11
  20. package/src/__tests__/access-request-card-view.test.ts +98 -0
  21. package/src/__tests__/access-request-seed-content-blocks.test.ts +2 -4
  22. package/src/__tests__/actor-trust-resolver-address-fallback.test.ts +72 -32
  23. package/src/__tests__/agent-loop-compaction-strip.test.ts +241 -0
  24. package/src/__tests__/agent-loop-mutable-latest-user-message.test.ts +16 -13
  25. package/src/__tests__/agent-loop-output-hooks.test.ts +69 -0
  26. package/src/__tests__/agent-loop-override-profile.test.ts +25 -0
  27. package/src/__tests__/agent-loop-pushes-post-hook-prompt.test.ts +306 -0
  28. package/src/__tests__/always-loaded-tools-guard.test.ts +2 -3
  29. package/src/__tests__/app-compiler.test.ts +15 -1
  30. package/src/__tests__/app-dir-path-guard.test.ts +0 -1
  31. package/src/__tests__/approval-cascade.test.ts +6 -0
  32. package/src/__tests__/approval-routes-http.test.ts +128 -0
  33. package/src/__tests__/assistant-feature-flag-guard.test.ts +1 -4
  34. package/src/__tests__/assistant-feature-flag-guardrails.test.ts +0 -2
  35. package/src/__tests__/auth-fallback-events-store.test.ts +6 -14
  36. package/src/__tests__/avatar-identity-sync.test.ts +2 -27
  37. package/src/__tests__/btw-routes.test.ts +6 -8
  38. package/src/__tests__/call-pointer-messages.test.ts +28 -0
  39. package/src/__tests__/cancel-clears-processing.test.ts +89 -0
  40. package/src/__tests__/channel-approval-routes.test.ts +0 -4
  41. package/src/__tests__/channel-inbound-disk-pressure.test.ts +5 -15
  42. package/src/__tests__/checker.test.ts +0 -3
  43. package/src/__tests__/cli-memory-v2-reembed-skills.test.ts +3 -4
  44. package/src/__tests__/compaction-events.test.ts +6 -0
  45. package/src/__tests__/compactor-image-manifest-trust.test.ts +21 -1
  46. package/src/__tests__/compactor-summary-call-truncation.test.ts +223 -0
  47. package/src/__tests__/config-loader-backfill.test.ts +268 -27
  48. package/src/__tests__/config-schema.test.ts +35 -0
  49. package/src/__tests__/config-watcher.test.ts +0 -18
  50. package/src/__tests__/confirmation-request-guardian-bridge.test.ts +2 -2
  51. package/src/__tests__/contact-store-user-file.test.ts +0 -6
  52. package/src/__tests__/contacts-tools.test.ts +29 -0
  53. package/src/__tests__/conversation-abort-tool-results.test.ts +6 -0
  54. package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +22 -0
  55. package/src/__tests__/conversation-agent-loop-overflow.test.ts +1 -0
  56. package/src/__tests__/conversation-agent-loop.test.ts +58 -0
  57. package/src/__tests__/conversation-attention-telegram.test.ts +0 -1
  58. package/src/__tests__/conversation-confirmation-signals.test.ts +6 -0
  59. package/src/__tests__/conversation-lifecycle.test.ts +7 -9
  60. package/src/__tests__/conversation-load-history-repair.test.ts +101 -0
  61. package/src/__tests__/conversation-process-callsite.test.ts +6 -0
  62. package/src/__tests__/conversation-provider-retry-repair.test.ts +6 -0
  63. package/src/__tests__/conversation-queue.test.ts +6 -0
  64. package/src/__tests__/conversation-routes-guardian-reply.test.ts +15 -12
  65. package/src/__tests__/conversation-slash-queue.test.ts +6 -0
  66. package/src/__tests__/conversation-slash-unknown.test.ts +6 -0
  67. package/src/__tests__/conversation-speed-override.test.ts +6 -0
  68. package/src/__tests__/conversation-surfaces-activation-emit.test.ts +6 -3
  69. package/src/__tests__/conversation-title-service.test.ts +62 -0
  70. package/src/__tests__/conversation-workspace-cache-state.test.ts +6 -0
  71. package/src/__tests__/conversation-workspace-injection.test.ts +6 -0
  72. package/src/__tests__/conversation-workspace-tool-tracking.test.ts +6 -0
  73. package/src/__tests__/credential-broker.test.ts +449 -1
  74. package/src/__tests__/credential-execution-shell-lockdown.test.ts +18 -11
  75. package/src/__tests__/credential-execution-tools.test.ts +0 -1
  76. package/src/__tests__/credential-prompt-route.test.ts +4 -4
  77. package/src/__tests__/credential-routes.test.ts +360 -0
  78. package/src/__tests__/credential-security-invariants.test.ts +4 -13
  79. package/src/__tests__/disk-pressure-policy.test.ts +12 -0
  80. package/src/__tests__/disk-pressure-tools.test.ts +41 -1
  81. package/src/__tests__/disk-usage.test.ts +65 -0
  82. package/src/__tests__/document-tool-security.test.ts +9 -6
  83. package/src/__tests__/document-update-default-surface.test.ts +202 -0
  84. package/src/__tests__/dynamic-page-surface.test.ts +152 -1
  85. package/src/__tests__/fixtures/credential-security-fixtures.ts +2 -33
  86. package/src/__tests__/gateway-flag-listener.test.ts +110 -1
  87. package/src/__tests__/gateway-only-guard.test.ts +3 -7
  88. package/src/__tests__/gemini-count-tokens.test.ts +70 -0
  89. package/src/__tests__/guardian-binding-drift-heal.test.ts +1 -1
  90. package/src/__tests__/guardian-card-withdrawal.test.ts +403 -0
  91. package/src/__tests__/guardian-decision-primitive-canonical.test.ts +5 -3
  92. package/src/__tests__/guardian-grant-minting.test.ts +3 -35
  93. package/src/__tests__/guardian-routing-invariants.test.ts +64 -26
  94. package/src/__tests__/guardian-routing-state.test.ts +0 -1
  95. package/src/__tests__/headless-browser-mode.test.ts +10 -0
  96. package/src/__tests__/headless-browser-navigate.test.ts +8 -3
  97. package/src/__tests__/helpers/create-guardian-binding.ts +0 -1
  98. package/src/__tests__/host-browser-proxy.test.ts +87 -0
  99. package/src/__tests__/identity-routes.test.ts +0 -189
  100. package/src/__tests__/inbound-invite-redemption.test.ts +4 -4
  101. package/src/__tests__/injector-disk-pressure.test.ts +10 -5
  102. package/src/__tests__/injector-v3-suppression.test.ts +27 -20
  103. package/src/__tests__/internal-telemetry-routes.test.ts +6 -14
  104. package/src/__tests__/invite-redemption-service.test.ts +4 -7
  105. package/src/__tests__/llm-callsite-catalog.test.ts +5 -6
  106. package/src/__tests__/llm-catalog-parity.test.ts +30 -23
  107. package/src/__tests__/llm-resolver.test.ts +70 -24
  108. package/src/__tests__/llm-schema.test.ts +1 -0
  109. package/src/__tests__/managed-profile-guard.test.ts +163 -4
  110. package/src/__tests__/mcp-health-check.test.ts +6 -7
  111. package/src/__tests__/media-stream-server-integration.test.ts +317 -13
  112. package/src/__tests__/oauth-provider-seed-logos.test.ts +4 -6
  113. package/src/__tests__/onboarding-persona-write.test.ts +1 -1
  114. package/src/__tests__/path-policy.test.ts +34 -0
  115. package/src/__tests__/persona-resolver.test.ts +49 -14
  116. package/src/__tests__/plugin-api-model-profiles.test.ts +178 -0
  117. package/src/__tests__/plugin-api-provider.test.ts +24 -0
  118. package/src/__tests__/plugin-tool-contribution.test.ts +6 -3
  119. package/src/__tests__/post-compaction-reinjection-idempotency.test.ts +214 -0
  120. package/src/__tests__/provider-send-message-override-profile.test.ts +76 -0
  121. package/src/__tests__/reaction-persistence.test.ts +150 -29
  122. package/src/__tests__/registry.test.ts +2 -7
  123. package/src/__tests__/relay-server.test.ts +285 -0
  124. package/src/__tests__/runtime-attachment-metadata.test.ts +0 -1
  125. package/src/__tests__/schedule-routes-workflow-validation.test.ts +1 -10
  126. package/src/__tests__/schedule-routes.test.ts +0 -30
  127. package/src/__tests__/schedule-tools.test.ts +2 -18
  128. package/src/__tests__/scheduler-reuse-conversation.test.ts +8 -5
  129. package/src/__tests__/secret-response-routing.test.ts +35 -0
  130. package/src/__tests__/secret-routes-platform-proxy.test.ts +16 -2
  131. package/src/__tests__/skill-execute-input.test.ts +177 -1
  132. package/src/__tests__/skill-load-tool.test.ts +72 -0
  133. package/src/__tests__/skill-runtime-path.test.ts +2 -3
  134. package/src/__tests__/skills.test.ts +51 -0
  135. package/src/__tests__/slack-notification-approval-card.test.ts +176 -0
  136. package/src/__tests__/slack-reaction-canonical-approval.test.ts +285 -0
  137. package/src/__tests__/subagent-tools.test.ts +266 -0
  138. package/src/__tests__/surface-completion-nudge-hook.test.ts +367 -0
  139. package/src/__tests__/system-storage-cleanup-skill.test.ts +56 -0
  140. package/src/__tests__/task-progress-nudge-hook.test.ts +1 -1
  141. package/src/__tests__/title-generate-hook.test.ts +100 -3
  142. package/src/__tests__/token-estimator-accuracy.benchmark.test.ts +1 -29
  143. package/src/__tests__/token-manager.test.ts +519 -0
  144. package/src/__tests__/tool-approval-seed-content-blocks.test.ts +1 -1
  145. package/src/__tests__/tool-audit-listener.test.ts +7 -7
  146. package/src/__tests__/tool-executor-lifecycle-events.test.ts +6 -3
  147. package/src/__tests__/tool-executor.test.ts +0 -79
  148. package/src/__tests__/trusted-contact-approval-notifier.test.ts +4 -2
  149. package/src/__tests__/trusted-contact-inline-approval-integration.test.ts +220 -3
  150. package/src/__tests__/trusted-contact-multichannel.test.ts +3 -3
  151. package/src/__tests__/trusted-contact-verification.test.ts +8 -10
  152. package/src/__tests__/twilio-routes.test.ts +81 -1
  153. package/src/__tests__/voice-invite-redemption.test.ts +2 -3
  154. package/src/__tests__/weak-open-model.test.ts +30 -0
  155. package/src/__tests__/web-search-catalog-parity.test.ts +6 -25
  156. package/src/__tests__/workspace-greetings.test.ts +152 -0
  157. package/src/__tests__/workspace-migration-105-enable-memory-v3-live-for-new-workspaces.test.ts +149 -0
  158. package/src/__tests__/workspace-migration-108-drop-balanced-economy-profile.test.ts +285 -0
  159. package/src/__tests__/workspace-migration-add-send-diagnostics.test.ts +1 -1
  160. package/src/__tests__/workspace-migration-drop-collect-usage-data.test.ts +118 -0
  161. package/src/__tests__/workspace-migration-drop-send-diagnostics.test.ts +118 -0
  162. package/src/a2a/__tests__/e2e-a2a-channel.test.ts +0 -4
  163. package/src/agent/loop.ts +75 -29
  164. package/src/api/README.md +6 -6
  165. package/src/api/events/tool-result.ts +6 -0
  166. package/src/api/events/workflow-completed.ts +53 -0
  167. package/src/api/events/workflow-leaf-finished.ts +38 -0
  168. package/src/api/events/workflow-leaf-started.ts +35 -0
  169. package/src/api/events/workflow-progress.ts +32 -0
  170. package/src/api/events/workflow-started.ts +31 -0
  171. package/src/api/index.ts +40 -0
  172. package/src/api/responses/conversation-message.ts +28 -4
  173. package/src/api/responses/home.ts +26 -4
  174. package/src/api/responses/workflow-journal.ts +53 -0
  175. package/src/approvals/guardian-card-withdrawal.ts +145 -0
  176. package/src/approvals/guardian-decision-primitive.ts +26 -3
  177. package/src/approvals/guardian-request-resolvers.ts +183 -80
  178. package/src/calls/__tests__/channel-admission-reader.test.ts +132 -0
  179. package/src/calls/__tests__/relay-setup-router.test.ts +350 -0
  180. package/src/calls/call-pointer-messages.ts +10 -4
  181. package/src/calls/channel-admission-reader.ts +104 -0
  182. package/src/calls/guardian-dispatch.ts +17 -45
  183. package/src/calls/media-stream-server.ts +84 -2
  184. package/src/calls/relay-access-wait.ts +1 -1
  185. package/src/calls/relay-server.ts +66 -0
  186. package/src/calls/relay-setup-router.ts +82 -1
  187. package/src/calls/twilio-routes.ts +17 -8
  188. package/src/calls/voice-session-bridge.ts +2 -2
  189. package/src/cli/commands/clients.ts +3 -0
  190. package/src/cli/commands/db/status.ts +37 -3
  191. package/src/cli/commands/{__tests__ → memory/__tests__}/memory-v2-compare-render.test.ts +1 -1
  192. package/src/cli/commands/{__tests__ → memory/__tests__}/memory-v2.test.ts +8 -7
  193. package/src/cli/commands/{__tests__ → memory/__tests__}/memory-v3.test.ts +5 -4
  194. package/src/cli/commands/memory/index.ts +30 -0
  195. package/src/cli/commands/{memory-v2-compare-render.ts → memory/memory-v2-compare-render.ts} +1 -1
  196. package/src/cli/commands/{memory-v2.ts → memory/memory-v2.ts} +6 -15
  197. package/src/cli/commands/{memory-v3.ts → memory/memory-v3.ts} +97 -11
  198. package/src/cli/commands/oauth/status.test.ts +36 -0
  199. package/src/cli/commands/oauth/status.ts +23 -3
  200. package/src/cli/commands/plugins.ts +400 -14
  201. package/src/cli/lib/__tests__/diff-plugin.test.ts +443 -0
  202. package/src/cli/lib/__tests__/inspect-plugin.test.ts +54 -0
  203. package/src/cli/lib/__tests__/install-from-github.test.ts +31 -0
  204. package/src/cli/lib/__tests__/merge-plugin-tree.test.ts +443 -0
  205. package/src/cli/lib/__tests__/plugin-pin-history.test.ts +162 -0
  206. package/src/cli/lib/__tests__/plugin-surfaces.test.ts +111 -0
  207. package/src/cli/lib/__tests__/upgrade-plugin.test.ts +295 -2
  208. package/src/cli/lib/diff-plugin.ts +346 -0
  209. package/src/cli/lib/inspect-plugin.ts +12 -1
  210. package/src/cli/lib/install-from-github.ts +128 -19
  211. package/src/cli/lib/merge-plugin-tree.ts +328 -0
  212. package/src/cli/lib/plugin-fingerprint.ts +14 -0
  213. package/src/cli/lib/plugin-marketplace.ts +11 -0
  214. package/src/cli/lib/plugin-pin-history.ts +257 -0
  215. package/src/cli/lib/plugin-surfaces.ts +104 -0
  216. package/src/cli/lib/upgrade-plugin.ts +298 -10
  217. package/src/cli/program.ts +2 -6
  218. package/src/config/__tests__/sync-gated-profiles.test.ts +368 -0
  219. package/src/config/assistant-feature-flags.ts +22 -7
  220. package/src/config/bundled-skills/contacts/tools/contact-search.ts +0 -1
  221. package/src/config/bundled-skills/document-editor/SKILL.md +3 -3
  222. package/src/config/bundled-skills/document-editor/TOOLS.json +2 -2
  223. package/src/config/bundled-skills/messaging/SKILL.md +6 -4
  224. package/src/config/bundled-skills/messaging/tools/messaging-archive-by-sender.ts +9 -8
  225. package/src/config/bundled-skills/subagent/SKILL.md +4 -0
  226. package/src/config/bundled-skills/subagent/TOOLS.json +4 -0
  227. package/src/config/bundled-skills/system-storage-cleanup/SKILL.md +74 -0
  228. package/src/config/bundled-skills/workflows/SKILL.md +14 -8
  229. package/src/config/bundled-tool-registry.ts +2 -7
  230. package/src/config/call-site-defaults.ts +15 -2
  231. package/src/config/feature-flag-registry.json +46 -31
  232. package/src/config/inference-profile-validation.ts +26 -0
  233. package/src/config/llm-resolver.ts +3 -0
  234. package/src/config/loader.ts +4 -0
  235. package/src/config/memory-v3-gate.ts +11 -0
  236. package/src/config/profile-order.ts +28 -0
  237. package/src/config/schema.ts +8 -6
  238. package/src/config/schemas/__tests__/memory-v3.test.ts +1 -0
  239. package/src/config/schemas/call-site-catalog.ts +7 -0
  240. package/src/config/schemas/channels.ts +11 -0
  241. package/src/config/schemas/elevenlabs.ts +0 -1
  242. package/src/config/schemas/llm.ts +31 -0
  243. package/src/config/schemas/memory-lifecycle.ts +3 -7
  244. package/src/config/schemas/memory-v3.ts +6 -0
  245. package/src/config/schemas/platform.ts +0 -8
  246. package/src/config/schemas/services.ts +18 -0
  247. package/src/config/seed-inference-profiles.ts +109 -44
  248. package/src/config/skills.ts +21 -0
  249. package/src/config/sync-gated-profiles.ts +220 -0
  250. package/src/contacts/contact-store.ts +89 -106
  251. package/src/contacts/contacts-write.ts +5 -22
  252. package/src/contacts/types.ts +0 -1
  253. package/src/context/compactor.ts +88 -54
  254. package/src/context/strip-injections.ts +58 -10
  255. package/src/context/token-estimator.ts +1 -1
  256. package/src/credential-execution/process-manager.ts +55 -14
  257. package/src/credential-execution/prompted-credential.ts +2 -3
  258. package/src/daemon/__tests__/conversation-lifecycle-auto-analyze.test.ts +3 -2
  259. package/src/daemon/__tests__/conversation-tool-setup.test.ts +44 -0
  260. package/src/daemon/config-watcher.ts +0 -4
  261. package/src/daemon/conversation-agent-loop-handlers.ts +2 -0
  262. package/src/daemon/conversation-agent-loop.ts +114 -22
  263. package/src/daemon/conversation-history.ts +1 -1
  264. package/src/daemon/conversation-lifecycle.ts +3 -5
  265. package/src/daemon/conversation-process.ts +13 -5
  266. package/src/daemon/conversation-runtime-assembly.ts +13 -15
  267. package/src/daemon/conversation-slash.ts +2 -23
  268. package/src/daemon/conversation-surfaces.ts +26 -0
  269. package/src/daemon/conversation-tool-setup.ts +53 -19
  270. package/src/daemon/conversation.ts +131 -20
  271. package/src/daemon/disk-pressure-policy.ts +5 -3
  272. package/src/daemon/handlers/__tests__/config-a2a-complete.test.ts +0 -1
  273. package/src/daemon/handlers/__tests__/config-a2a-redeem.test.ts +0 -1
  274. package/src/daemon/handlers/config-a2a.ts +0 -2
  275. package/src/daemon/handlers/config-channels.ts +15 -16
  276. package/src/daemon/handlers/config-slack-channel.ts +22 -3
  277. package/src/daemon/handlers/conversations.ts +107 -0
  278. package/src/daemon/host-browser-proxy.ts +41 -0
  279. package/src/daemon/lifecycle.ts +55 -27
  280. package/src/daemon/memory-v2-startup.test.ts +131 -0
  281. package/src/daemon/memory-v2-startup.ts +100 -1
  282. package/src/daemon/message-provenance.ts +2 -0
  283. package/src/daemon/message-types/contacts.ts +0 -1
  284. package/src/daemon/message-types/conversations.ts +3 -3
  285. package/src/daemon/message-types/sync.ts +0 -1
  286. package/src/daemon/message-types/web-activity.ts +7 -1
  287. package/src/daemon/message-types/workflows.ts +83 -1
  288. package/src/daemon/orphan-reaper.test.ts +0 -19
  289. package/src/daemon/orphan-reaper.ts +2 -24
  290. package/src/daemon/server.ts +0 -10
  291. package/src/daemon/tool-setup-types.ts +4 -0
  292. package/src/daemon/trust-context.ts +1 -1
  293. package/src/daemon/wake-conversation-ops.ts +1 -0
  294. package/src/events/tool-audit-listener.ts +2 -2
  295. package/src/home/feed-source-enrichment.test.ts +151 -0
  296. package/src/home/feed-source-enrichment.ts +176 -0
  297. package/src/home/relationship-state.ts +2 -4
  298. package/src/instrument.ts +18 -6
  299. package/src/ipc/__tests__/binary-result-ipc.test.ts +81 -0
  300. package/src/ipc/__tests__/clients-list-ipc.test.ts +20 -0
  301. package/src/ipc/assistant-server.ts +37 -4
  302. package/src/ipc/gateway-flag-listener.ts +18 -2
  303. package/src/memory/__tests__/auto-analysis-enqueue.test.ts +5 -16
  304. package/src/memory/__tests__/db-logs-attach.test.ts +122 -0
  305. package/src/memory/__tests__/jobs-store-enqueue-gate.test.ts +7 -11
  306. package/src/memory/__tests__/memory-retrospective-enqueue.test.ts +37 -7
  307. package/src/memory/__tests__/memory-retrospective-job.test.ts +229 -401
  308. package/src/memory/__tests__/onboarding-events-store.test.ts +7 -7
  309. package/src/memory/auth-fallback-events-store.ts +2 -2
  310. package/src/memory/auto-analysis-enqueue.ts +3 -5
  311. package/src/memory/bookmark-crud.ts +1 -2
  312. package/src/memory/canonical-guardian-store.ts +39 -1
  313. package/src/memory/conversation-crud.ts +9 -4
  314. package/src/memory/conversation-key-store.ts +17 -2
  315. package/src/memory/conversation-title-service.ts +64 -7
  316. package/src/memory/db-async-query.ts +20 -2
  317. package/src/memory/db-connection.ts +49 -5
  318. package/src/memory/db-init.ts +17 -17
  319. package/src/memory/embedding-backend.ts +38 -1
  320. package/src/memory/embedding-billing-breaker.ts +96 -0
  321. package/src/memory/jobs-store.ts +25 -13
  322. package/src/memory/jobs-worker.ts +54 -1
  323. package/src/memory/lifecycle-events-store.ts +2 -2
  324. package/src/memory/memory-retrospective-constants.ts +4 -4
  325. package/src/memory/memory-retrospective-enqueue.ts +31 -6
  326. package/src/memory/memory-retrospective-job.ts +28 -227
  327. package/src/memory/migrations/129-contact-channels-access-fields.ts +18 -9
  328. package/src/memory/migrations/131-drop-legacy-member-guardian-tables.ts +14 -2
  329. package/src/memory/migrations/289-contact-channels-unique-ext-user.ts +10 -0
  330. package/src/memory/migrations/291-contact-channels-renormalize-addresses.ts +72 -0
  331. package/src/memory/migrations/292-schedule-default-no-reuse-conversation.test.ts +67 -0
  332. package/src/memory/migrations/292-schedule-default-no-reuse-conversation.ts +25 -0
  333. package/src/memory/migrations/293-workflow-journal-leaf-tokens.ts +32 -0
  334. package/src/memory/migrations/294-drop-external-user-id.ts +31 -0
  335. package/src/memory/migrations/295-drop-approval-prompt-ts-tracker.ts +20 -0
  336. package/src/memory/migrations/296-rewrite-balanced-economy-profile-pins.test.ts +110 -0
  337. package/src/memory/migrations/296-rewrite-balanced-economy-profile-pins.ts +68 -0
  338. package/src/memory/migrations/__tests__/131-drop-legacy-member-guardian-tables.test.ts +154 -0
  339. package/src/memory/migrations/__tests__/289-contact-channels-unique-ext-user.test.ts +31 -0
  340. package/src/memory/migrations/__tests__/291-contact-channels-renormalize-addresses.test.ts +341 -0
  341. package/src/memory/migrations/__tests__/run-migrations.test.ts +52 -0
  342. package/src/memory/migrations/index.ts +6 -0
  343. package/src/memory/migrations/run-migrations.ts +41 -0
  344. package/src/memory/migrations/validate-migration-state.ts +1 -1
  345. package/src/memory/onboarding-events-store.ts +3 -3
  346. package/src/memory/schema/contacts.ts +0 -5
  347. package/src/memory/skill-loaded-events-store.test.ts +7 -15
  348. package/src/memory/skill-loaded-events-store.ts +2 -2
  349. package/src/memory/tool-executed-events-store.test.ts +7 -7
  350. package/src/memory/turn-trace-store.test.ts +736 -0
  351. package/src/memory/turn-trace-store.ts +364 -0
  352. package/src/memory/v2/__tests__/consolidation-job.test.ts +8 -0
  353. package/src/memory/v2/__tests__/skill-content.test.ts +30 -0
  354. package/src/memory/v2/consolidation-job.ts +2 -2
  355. package/src/memory/v2/skill-content.ts +25 -7
  356. package/src/memory/v2/skill-store.ts +7 -1
  357. package/src/memory/v3-eval/__tests__/eval-packets.test.ts +248 -0
  358. package/src/memory/v3-eval/eval-packets.ts +546 -0
  359. package/src/messaging/providers/slack/adapter.ts +1 -1
  360. package/src/messaging/providers/slack/api.ts +31 -0
  361. package/src/messaging/providers/slack/send.test.ts +114 -2
  362. package/src/messaging/providers/slack/send.ts +30 -7
  363. package/src/messaging/providers/slack/withdraw.test.ts +200 -0
  364. package/src/messaging/providers/slack/withdraw.ts +161 -0
  365. package/src/notifications/AGENTS.md +2 -0
  366. package/src/notifications/access-request-copy.ts +72 -59
  367. package/src/notifications/adapters/shared.ts +29 -0
  368. package/src/notifications/adapters/slack.ts +58 -103
  369. package/src/notifications/adapters/telegram.ts +2 -20
  370. package/src/notifications/approval-card-data.ts +333 -0
  371. package/src/notifications/broadcaster.ts +16 -3
  372. package/src/notifications/canonical-delivery-recorder.ts +139 -0
  373. package/src/notifications/copy-composer.ts +3 -3
  374. package/src/notifications/decision-engine.ts +4 -2
  375. package/src/notifications/destination-resolver.ts +4 -6
  376. package/src/notifications/guardian-question-mode.ts +10 -0
  377. package/src/notifications/home-feed-side-effect.ts +7 -16
  378. package/src/notifications/notification-utils.ts +19 -20
  379. package/src/notifications/signal.ts +79 -43
  380. package/src/notifications/types.ts +98 -121
  381. package/src/oauth/AGENTS.md +5 -24
  382. package/src/permissions/checker.test.ts +51 -0
  383. package/src/permissions/checker.ts +185 -26
  384. package/src/permissions/ipc-risk-types.ts +24 -0
  385. package/src/permissions/question-prompter.test.ts +27 -0
  386. package/src/permissions/question-prompter.ts +4 -0
  387. package/src/permissions/secret-prompter.ts +15 -1
  388. package/src/platform/client.test.ts +119 -0
  389. package/src/platform/client.ts +66 -0
  390. package/src/platform/consent-cache.test.ts +267 -0
  391. package/src/platform/consent-cache.ts +174 -0
  392. package/src/plugin-api/constants.ts +1 -1
  393. package/src/plugin-api/index.ts +33 -1
  394. package/src/plugin-api/model-profiles.ts +33 -0
  395. package/src/plugin-api/types.ts +50 -2
  396. package/src/plugins/defaults/advisor/__tests__/advisor-gate.test.ts +56 -0
  397. package/src/plugins/defaults/advisor/__tests__/advisor-state-store.test.ts +43 -0
  398. package/src/plugins/defaults/advisor/__tests__/agent-loop-integration.test.ts +137 -0
  399. package/src/plugins/defaults/advisor/__tests__/consult.test.ts +153 -0
  400. package/src/plugins/defaults/advisor/__tests__/hooks.test.ts +138 -0
  401. package/src/plugins/defaults/advisor/__tests__/transcript.test.ts +147 -0
  402. package/src/plugins/defaults/advisor/advisor-gate.ts +29 -0
  403. package/src/plugins/defaults/advisor/advisor-state-store.ts +94 -0
  404. package/src/plugins/defaults/advisor/config.ts +21 -0
  405. package/src/plugins/defaults/advisor/consult.ts +93 -0
  406. package/src/plugins/defaults/advisor/hooks/post-model-call.ts +34 -0
  407. package/src/plugins/defaults/advisor/hooks/pre-model-call.ts +30 -0
  408. package/src/plugins/defaults/advisor/hooks/user-prompt-submit.ts +19 -0
  409. package/src/plugins/defaults/advisor/package.json +14 -0
  410. package/src/plugins/defaults/advisor/steering.ts +67 -0
  411. package/src/plugins/defaults/advisor/tools/advisor.ts +65 -0
  412. package/src/plugins/defaults/advisor/transcript.ts +76 -0
  413. package/src/plugins/defaults/compaction/window-manager.ts +18 -0
  414. package/src/plugins/defaults/index.ts +60 -0
  415. package/src/plugins/defaults/memory-retrieval/hooks/post-compact.ts +22 -9
  416. package/src/plugins/defaults/memory-retrieval/hooks/user-prompt-submit.ts +2 -2
  417. package/src/plugins/defaults/memory-retrieval/injectors.ts +4 -4
  418. package/src/plugins/defaults/memory-retrieval/tail-reinjection-strip.ts +64 -0
  419. package/src/plugins/defaults/memory-retrieval/unified-turn-context.ts +29 -21
  420. package/src/plugins/defaults/memory-v3-shadow/__tests__/carry-integration.test.ts +1 -0
  421. package/src/plugins/defaults/memory-v3-shadow/__tests__/injection.test.ts +1 -0
  422. package/src/plugins/defaults/memory-v3-shadow/__tests__/maintain-job.test.ts +129 -9
  423. package/src/plugins/defaults/memory-v3-shadow/__tests__/orchestrate.test.ts +31 -4
  424. package/src/plugins/defaults/memory-v3-shadow/__tests__/selection-log-store.test.ts +77 -2
  425. package/src/plugins/defaults/memory-v3-shadow/__tests__/shadow-plugin.test.ts +1 -0
  426. package/src/plugins/defaults/memory-v3-shadow/injector.ts +7 -10
  427. package/src/plugins/defaults/memory-v3-shadow/maintain-job.ts +144 -11
  428. package/src/plugins/defaults/memory-v3-shadow/orchestrate.ts +32 -20
  429. package/src/plugins/defaults/memory-v3-shadow/selection-log-store.ts +56 -3
  430. package/src/plugins/defaults/memory-v3-shadow/shadow-plugin.ts +23 -2
  431. package/src/plugins/defaults/surface-completion-nudge/hooks/post-model-call.ts +276 -0
  432. package/src/plugins/defaults/surface-completion-nudge/hooks/stop.ts +22 -0
  433. package/src/plugins/defaults/surface-completion-nudge/nudge-state-store.ts +46 -0
  434. package/src/plugins/defaults/surface-completion-nudge/package.json +14 -0
  435. package/src/plugins/defaults/task-progress-nudge/hooks/post-tool-use.ts +3 -13
  436. package/src/plugins/defaults/title-generate/hooks/stop.ts +56 -21
  437. package/src/prompts/persona-resolver.ts +14 -4
  438. package/src/prompts/templates/system-sections.ts +7 -2
  439. package/src/providers/__tests__/count-tokens-forwarding.test.ts +98 -0
  440. package/src/providers/__tests__/provider-env-vars.test.ts +6 -0
  441. package/src/providers/__tests__/provider-secret-catalog.test.ts +1 -0
  442. package/src/providers/__tests__/retry-callsite.test.ts +176 -0
  443. package/src/providers/anthropic/client.ts +230 -175
  444. package/src/providers/atlascloud/client.ts +85 -0
  445. package/src/providers/call-site-routing.ts +10 -0
  446. package/src/providers/fetch-provider-catalog.ts +85 -0
  447. package/src/providers/gemini/client.ts +43 -0
  448. package/src/providers/inference/adapter-factory.ts +3 -0
  449. package/src/providers/model-catalog.ts +58 -0
  450. package/src/providers/openai/__tests__/chat-completions-provider-reasoning.test.ts +33 -0
  451. package/src/providers/openai/chat-completions-provider.ts +7 -0
  452. package/src/providers/openai/responses-provider.ts +10 -0
  453. package/src/providers/provider-send-message.ts +11 -3
  454. package/src/providers/ratelimit.ts +9 -0
  455. package/src/providers/retry.ts +62 -12
  456. package/src/providers/search-provider-catalog.ts +10 -0
  457. package/src/providers/types.ts +16 -0
  458. package/src/providers/usage-tracking.ts +7 -0
  459. package/src/providers/weak-open-model.ts +22 -0
  460. package/src/runtime/AGENTS.md +0 -1
  461. package/src/runtime/__tests__/agent-wake.test.ts +227 -0
  462. package/src/runtime/__tests__/client-health.test.ts +44 -0
  463. package/src/runtime/access-request-helper.ts +21 -53
  464. package/src/runtime/actor-trust-resolver.ts +59 -63
  465. package/src/runtime/agent-wake.ts +71 -0
  466. package/src/runtime/assistant-event-hub.ts +18 -4
  467. package/src/runtime/auth/__tests__/route-policy.test.ts +12 -0
  468. package/src/runtime/auth/require-bound-guardian.ts +1 -4
  469. package/src/runtime/btw-sidechain.ts +3 -6
  470. package/src/runtime/capabilities.test.ts +120 -0
  471. package/src/runtime/capabilities.ts +197 -0
  472. package/src/runtime/channel-approval-types.ts +22 -45
  473. package/src/runtime/channel-invite-transports/telegram.ts +4 -4
  474. package/src/runtime/channel-retry-sweep.ts +1 -0
  475. package/src/runtime/channel-verification-service.ts +3 -3
  476. package/src/runtime/client-health.ts +26 -0
  477. package/src/runtime/confirmation-request-guardian-bridge.ts +38 -29
  478. package/src/runtime/effective-capabilities.test.ts +128 -0
  479. package/src/runtime/effective-capabilities.ts +84 -0
  480. package/src/runtime/guardian-reply-router.ts +106 -21
  481. package/src/runtime/invite-redemption-service.ts +9 -25
  482. package/src/runtime/migrations/__tests__/vbundle-builder-fd-leak.test.ts +126 -0
  483. package/src/runtime/migrations/vbundle-builder.ts +49 -20
  484. package/src/runtime/pending-interactions.ts +35 -0
  485. package/src/runtime/routes/__tests__/client-routes.test.ts +13 -0
  486. package/src/runtime/routes/__tests__/conversation-management-routes.test.ts +67 -0
  487. package/src/runtime/routes/__tests__/plugins-routes.test.ts +383 -1
  488. package/src/runtime/routes/app-routes.ts +1 -1
  489. package/src/runtime/routes/approval-routes.ts +40 -9
  490. package/src/runtime/routes/approval-strategies/guardian-callback-strategy.ts +2 -2
  491. package/src/runtime/routes/assets/vellum-design-system.css +1959 -0
  492. package/src/runtime/routes/browser-tabs-routes.ts +9 -0
  493. package/src/runtime/routes/btw-routes.ts +1 -27
  494. package/src/runtime/routes/canonical-guardian-expiry-sweep.ts +17 -8
  495. package/src/runtime/routes/client-routes.ts +10 -0
  496. package/src/runtime/routes/contact-routes.ts +31 -8
  497. package/src/runtime/routes/conversation-compaction-routes.ts +1 -1
  498. package/src/runtime/routes/conversation-management-routes.ts +80 -1
  499. package/src/runtime/routes/conversation-query-routes.ts +68 -22
  500. package/src/runtime/routes/conversation-routes.ts +39 -14
  501. package/src/runtime/routes/credential-routes.ts +40 -16
  502. package/src/runtime/routes/empty-state-greeting-cache.ts +1 -2
  503. package/src/runtime/routes/events-routes.ts +1 -3
  504. package/src/runtime/routes/guardian-approval-interception.ts +14 -73
  505. package/src/runtime/routes/guardian-approval-prompt.ts +22 -4
  506. package/src/runtime/routes/home-feed-routes.ts +8 -3
  507. package/src/runtime/routes/identity-routes.ts +1 -296
  508. package/src/runtime/routes/inbound-message-handler.ts +214 -228
  509. package/src/runtime/routes/inbound-stages/acl-enforcement.ts +89 -7
  510. package/src/runtime/routes/inbound-stages/admission-policy.test.ts +154 -0
  511. package/src/runtime/routes/inbound-stages/admission-policy.ts +140 -0
  512. package/src/runtime/routes/inbound-stages/background-dispatch.test.ts +3 -3
  513. package/src/runtime/routes/inbound-stages/background-dispatch.ts +11 -6
  514. package/src/runtime/routes/inbound-stages/escalation-intercept.ts +1 -2
  515. package/src/runtime/routes/inbound-stages/guardian-activation-intercept.ts +1 -2
  516. package/src/runtime/routes/inbound-stages/guardian-reply-intercept.test.ts +7 -7
  517. package/src/runtime/routes/inbound-stages/guardian-reply-intercept.ts +47 -28
  518. package/src/runtime/routes/inbound-stages/reaction-intercept.ts +358 -0
  519. package/src/runtime/routes/index.ts +2 -0
  520. package/src/runtime/routes/integrations/slack/__tests__/channel.test.ts +8 -0
  521. package/src/runtime/routes/integrations/slack/channel.ts +36 -0
  522. package/src/runtime/routes/internal-telemetry-routes.ts +1 -1
  523. package/src/runtime/routes/mcp-auth-routes.ts +233 -41
  524. package/src/runtime/routes/memory-eval-routes.ts +87 -0
  525. package/src/runtime/routes/notification-routes.ts +122 -133
  526. package/src/runtime/routes/platform-routes.ts +2 -2
  527. package/src/runtime/routes/plugins-routes.ts +366 -11
  528. package/src/runtime/routes/schedule-routes.ts +0 -22
  529. package/src/runtime/routes/secret-routes.ts +15 -0
  530. package/src/runtime/routes/surface-action-routes.ts +2 -1
  531. package/src/runtime/routes/tool-call-question-enrichment.test.ts +146 -0
  532. package/src/runtime/routes/tool-call-question-enrichment.ts +66 -0
  533. package/src/runtime/routes/workflow-routes.test.ts +229 -44
  534. package/src/runtime/routes/workflow-routes.ts +131 -29
  535. package/src/runtime/routes/workspace-greetings.ts +55 -0
  536. package/src/runtime/sync/resource-sync-events.ts +1 -11
  537. package/src/runtime/tool-grant-request-helper.ts +18 -16
  538. package/src/runtime/trust-context-resolver.ts +8 -5
  539. package/src/schedule/inference-profile.ts +2 -14
  540. package/src/schedule/schedule-store.ts +1 -1
  541. package/src/schedule/scheduler-types.ts +5 -1
  542. package/src/security/__tests__/provider-key-env-fallback.test.ts +6 -0
  543. package/src/security/secret-patterns.ts +3 -0
  544. package/src/subagent/manager.ts +17 -4
  545. package/src/subagent/types.ts +6 -0
  546. package/src/telemetry/trace-collection-policy.test.ts +28 -0
  547. package/src/telemetry/trace-collection-policy.ts +30 -0
  548. package/src/telemetry/types.ts +89 -0
  549. package/src/telemetry/usage-telemetry-reporter.test.ts +586 -36
  550. package/src/telemetry/usage-telemetry-reporter.ts +148 -41
  551. package/src/tools/AGENTS.md +3 -3
  552. package/src/tools/browser/__tests__/browser-execution-acquire.test.ts +31 -0
  553. package/src/tools/browser/browser-execution.ts +30 -19
  554. package/src/tools/document/document-tool.ts +30 -6
  555. package/src/tools/executor.ts +5 -3
  556. package/src/tools/host-terminal/host-shell.ts +5 -4
  557. package/src/tools/memory/register.ts +2 -2
  558. package/src/tools/network/__tests__/web-fetch-firecrawl.test.ts +360 -0
  559. package/src/tools/network/__tests__/web-search.test.ts +143 -0
  560. package/src/tools/network/web-fetch.ts +372 -1
  561. package/src/tools/network/web-search-error.ts +1 -1
  562. package/src/tools/network/web-search.ts +213 -10
  563. package/src/tools/permission-checker.ts +4 -3
  564. package/src/tools/registry.ts +20 -0
  565. package/src/tools/schedule/create.ts +7 -12
  566. package/src/tools/schedule/update.ts +4 -11
  567. package/src/tools/shared/filesystem/path-policy.ts +39 -13
  568. package/src/tools/side-effects.ts +2 -17
  569. package/src/tools/skills/execute.ts +99 -2
  570. package/src/tools/skills/load.ts +43 -2
  571. package/src/tools/subagent/spawn.ts +64 -15
  572. package/src/tools/terminal/shell.ts +10 -4
  573. package/src/tools/tool-approval-handler.ts +18 -13
  574. package/src/tools/tool-manifest.ts +0 -2
  575. package/src/tools/types.ts +10 -0
  576. package/src/tools/ui-surface/definitions.ts +64 -3
  577. package/src/tools/verification-control-plane-policy.ts +3 -1
  578. package/src/tools/workflows/run-workflow.test.ts +8 -18
  579. package/src/tools/workflows/run-workflow.ts +1 -0
  580. package/src/util/disk-usage.ts +78 -23
  581. package/src/util/logs-db-path.ts +22 -0
  582. package/src/util/platform.ts +10 -3
  583. package/src/watcher/telemetry.ts +2 -2
  584. package/src/workflows/capabilities.ts +2 -3
  585. package/src/workflows/engine.test.ts +175 -1
  586. package/src/workflows/engine.ts +82 -0
  587. package/src/workflows/journal-store.test.ts +70 -0
  588. package/src/workflows/journal-store.ts +18 -3
  589. package/src/workflows/run-manager.test.ts +171 -28
  590. package/src/workflows/run-manager.ts +66 -24
  591. package/src/workspace/migrations/105-enable-memory-v3-live-for-new-workspaces.ts +63 -0
  592. package/src/workspace/migrations/106-drop-collect-usage-data.ts +47 -0
  593. package/src/workspace/migrations/107-drop-send-diagnostics.ts +47 -0
  594. package/src/workspace/migrations/108-drop-balanced-economy-profile.ts +129 -0
  595. package/src/workspace/migrations/registry.ts +8 -0
  596. package/src/__tests__/app-control-no-global-cgevent.test.ts +0 -98
  597. package/src/__tests__/credential-security-e2e.test.ts +0 -362
  598. package/src/__tests__/credential-vault-unit.test.ts +0 -1528
  599. package/src/__tests__/credential-vault.test.ts +0 -1706
  600. package/src/__tests__/identity-intro-cache.test.ts +0 -315
  601. package/src/__tests__/secret-onetime-send.test.ts +0 -182
  602. package/src/cli/commands/__tests__/task.test.ts +0 -914
  603. package/src/cli/commands/task.ts +0 -771
  604. package/src/config/bundled-skills/personal-page/SKILL.md +0 -57
  605. package/src/config/bundled-skills/personal-page/TOOLS.json +0 -27
  606. package/src/config/bundled-skills/personal-page/tools/app-refresh.ts +0 -17
  607. package/src/config/preloaded-apps/personal-page/src/components/About.tsx +0 -22
  608. package/src/config/preloaded-apps/personal-page/src/components/App.tsx +0 -16
  609. package/src/config/preloaded-apps/personal-page/src/components/Features.tsx +0 -77
  610. package/src/config/preloaded-apps/personal-page/src/components/Hero.tsx +0 -57
  611. package/src/config/preloaded-apps/personal-page/src/components/Pending.tsx +0 -28
  612. package/src/config/preloaded-apps/personal-page/src/components/animations.tsx +0 -234
  613. package/src/config/preloaded-apps/personal-page/src/components/icons.tsx +0 -48
  614. package/src/config/preloaded-apps/personal-page/src/components/media.ts +0 -16
  615. package/src/config/preloaded-apps/personal-page/src/index.html +0 -20
  616. package/src/config/preloaded-apps/personal-page/src/main.tsx +0 -7
  617. package/src/config/preloaded-apps/personal-page/src/profile-data.ts +0 -82
  618. package/src/config/preloaded-apps/personal-page/src/styles.css +0 -759
  619. package/src/memory/__tests__/preloaded-apps.test.ts +0 -85
  620. package/src/memory/preloaded-apps.ts +0 -116
  621. package/src/notifications/tool-approval-copy.ts +0 -142
  622. package/src/runtime/routes/approval-prompt-ts-tracker.ts +0 -78
  623. package/src/runtime/routes/identity-intro-cache.ts +0 -172
  624. package/src/tools/credentials/vault.ts +0 -712
@@ -0,0 +1,346 @@
1
+ /**
2
+ * Show the unified diff of local edits to an installed plugin, against the
3
+ * exact commit it was installed at.
4
+ *
5
+ * `plugins inspect` already reports *which* files drifted via the install-time
6
+ * per-file fingerprint (see {@link ./plugin-fingerprint}), but that digest is
7
+ * one-way — it cannot reconstruct the original bytes, so it can't show *what*
8
+ * changed. Drift here is classified the same way `inspect` does — against the
9
+ * fingerprint recorded at install — so the two surfaces always agree and a
10
+ * curated adapter overlay that changed since install never misreads as local
11
+ * drift.
12
+ *
13
+ * Showing *what* changed still needs the baseline *bytes*, which the fingerprint
14
+ * cannot reconstruct. Those are re-derived by re-materializing the recorded
15
+ * commit (from `install-meta.json`) through the *same* pipeline install used
16
+ * (see {@link ./install-from-github.materializePluginTree}): a shallow clone at
17
+ * the immutable SHA plus the curated adapter overlay. The adapter overlay is
18
+ * fetched from the canonical repo's current ref, not the install-time ref (which
19
+ * is not recorded), so a re-materialized file can diverge from what install
20
+ * produced. Each baseline file is therefore verified against the recorded
21
+ * fingerprint digest before it is diffed: on a mismatch the install-time bytes
22
+ * cannot be faithfully reconstructed, so the file is flagged rather than diffed
23
+ * against a fabricated baseline.
24
+ *
25
+ * The baseline is always the recorded install commit, not the marketplace's
26
+ * current pin: this answers "what local changes have been made since install",
27
+ * independent of marketplace movement. Comparing against the latest pin is the
28
+ * separate concern `plugins upgrade --dry-run` already covers.
29
+ *
30
+ * Designed for direct programmatic use with injected dependencies, mirroring
31
+ * the sibling plugin libraries. The CLI command `assistant plugins diff <name>`
32
+ * is a thin wrapper that supplies production deps and formats the result.
33
+ */
34
+
35
+ import { mkdtempSync, readFileSync, rmSync } from "node:fs";
36
+ import { tmpdir } from "node:os";
37
+ import { join } from "node:path";
38
+
39
+ import { createTwoFilesPatch } from "diff";
40
+
41
+ import { getWorkspacePluginsDir } from "../../util/platform.js";
42
+ import {
43
+ DEFAULT_PLUGIN_REF,
44
+ type FetchLike,
45
+ type GitRunner,
46
+ INSTALL_META_FILENAME,
47
+ materializePluginTree,
48
+ type PluginFetchSource,
49
+ PluginNotFoundError,
50
+ type PostinstallRunner,
51
+ readInstallMeta,
52
+ sanitizePluginName,
53
+ } from "./install-from-github.js";
54
+ import { readInstalledPlugin } from "./list-installed-plugins.js";
55
+ import {
56
+ compareFingerprint,
57
+ computeFingerprint,
58
+ type Fingerprint,
59
+ } from "./plugin-fingerprint.js";
60
+ import { PluginNotInstalledError } from "./uninstall-plugin.js";
61
+
62
+ /** How a file drifted from the install-time baseline. */
63
+ export type PluginFileDiffStatus = "modified" | "added" | "removed";
64
+
65
+ /** Unified diff of a single drifted file. */
66
+ export interface PluginFileDiff {
67
+ /** POSIX-relative path within the plugin root. */
68
+ readonly path: string;
69
+ /** Whether the file was edited, newly added, or deleted since install. */
70
+ readonly status: PluginFileDiffStatus;
71
+ /**
72
+ * Unified diff (`--- a/… / +++ b/…`) of the file's bytes. For a binary file
73
+ * this is a short `Binary files differ` marker instead of a line diff, since
74
+ * a line-based patch of non-text content is noise. When {@link
75
+ * PluginFileDiff.reconstructed} is false this is a short explanatory marker
76
+ * rather than a patch, since the install-time bytes are unavailable.
77
+ */
78
+ readonly diff: string;
79
+ /** True when either side was detected as binary (NUL byte present). */
80
+ readonly binary: boolean;
81
+ /**
82
+ * True when the install-time baseline for this file was faithfully recovered
83
+ * (its re-materialized bytes hash-match the digest recorded at install).
84
+ * False when the baseline could not be reconstructed — e.g. the curated
85
+ * adapter overlay the file was built from has changed since install — in
86
+ * which case {@link PluginFileDiff.diff} is a marker, not a real patch.
87
+ * Always true for `added` files, whose baseline is the empty side.
88
+ */
89
+ readonly reconstructed: boolean;
90
+ }
91
+
92
+ /** Resolved diff of an installed plugin against its install-time baseline. */
93
+ export interface PluginDiffResult {
94
+ /** Install name. Matches `assistant plugins install <name>`. */
95
+ readonly name: string;
96
+ /** Absolute path to the installed plugin directory. */
97
+ readonly target: string;
98
+ /** Commit the baseline was re-materialized from (the recorded install SHA). */
99
+ readonly commit: string;
100
+ /** ISO-8601 committer timestamp (UTC) of {@link PluginDiffResult.commit}; `null` when unrecorded. */
101
+ readonly committedAt: string | null;
102
+ /** True when the on-disk tree exactly matches the re-materialized baseline. */
103
+ readonly clean: boolean;
104
+ /** One entry per drifted file, sorted by path. Empty when `clean`. */
105
+ readonly files: readonly PluginFileDiff[];
106
+ }
107
+
108
+ /**
109
+ * The installed copy carries no resolvable commit, so there is no immutable
110
+ * baseline to re-materialize and diff against.
111
+ */
112
+ export class PluginDiffUnavailableError extends Error {
113
+ constructor(
114
+ readonly pluginName: string,
115
+ reason: string,
116
+ ) {
117
+ super(`Plugin "${pluginName}" cannot be diffed: ${reason}.`);
118
+ this.name = "PluginDiffUnavailableError";
119
+ }
120
+ }
121
+
122
+ /** Options that control which plugin to diff. */
123
+ export interface DiffPluginOptions {
124
+ /** Install name (kebab-case directory name). */
125
+ readonly name: string;
126
+ }
127
+
128
+ /** Dependencies injected by the caller. */
129
+ export interface DiffPluginDeps {
130
+ /** HTTP client used to fetch any curated adapter stub. Production callers pass `globalThis.fetch.bind(globalThis)`. */
131
+ readonly fetch: FetchLike;
132
+ /** Override the workspace plugins directory. Falls back to the live workspace. */
133
+ readonly workspacePluginsDir?: string;
134
+ /** Override the git runner used to clone the baseline. Forwarded to {@link materializePluginTree}. */
135
+ readonly runGit?: GitRunner;
136
+ /** Override the postinstall adapter runner. Forwarded to {@link materializePluginTree}. */
137
+ readonly runPostinstall?: PostinstallRunner;
138
+ }
139
+
140
+ /** A NUL byte in the leading bytes is the heuristic git uses to flag a blob as binary. */
141
+ function isBinary(buf: Buffer): boolean {
142
+ const len = Math.min(buf.length, 8000);
143
+ for (let i = 0; i < len; i++) {
144
+ if (buf[i] === 0) return true;
145
+ }
146
+ return false;
147
+ }
148
+
149
+ interface FileContent {
150
+ readonly text: string;
151
+ readonly binary: boolean;
152
+ }
153
+
154
+ function readContent(absPath: string): FileContent {
155
+ const buf = readFileSync(absPath);
156
+ const binary = isBinary(buf);
157
+ return { text: binary ? "" : buf.toString("utf8"), binary };
158
+ }
159
+
160
+ function makeDiff(
161
+ path: string,
162
+ status: PluginFileDiffStatus,
163
+ before: FileContent | null,
164
+ after: FileContent | null,
165
+ reconstructed: boolean,
166
+ ): PluginFileDiff {
167
+ if (!reconstructed) {
168
+ return {
169
+ path,
170
+ status,
171
+ diff: `Baseline unavailable (${status}): the install-time content of this file could not be reconstructed — the curated adapter overlay it was built from has changed since install. Reinstall with 'plugins install <name> --force' to refresh the baseline.`,
172
+ binary: false,
173
+ reconstructed: false,
174
+ };
175
+ }
176
+ const binary = (before?.binary ?? false) || (after?.binary ?? false);
177
+ if (binary) {
178
+ return {
179
+ path,
180
+ status,
181
+ diff: `Binary files differ (${status})`,
182
+ binary: true,
183
+ reconstructed: true,
184
+ };
185
+ }
186
+ // `/dev/null` on the absent side and `a/`–`b/` prefixes mirror `git diff`, so
187
+ // the output is familiar and consumable by tools that parse unified diffs.
188
+ const oldName = status === "added" ? "/dev/null" : `a/${path}`;
189
+ const newName = status === "removed" ? "/dev/null" : `b/${path}`;
190
+ const diff = createTwoFilesPatch(
191
+ oldName,
192
+ newName,
193
+ before?.text ?? "",
194
+ after?.text ?? "",
195
+ undefined,
196
+ undefined,
197
+ { context: 3 },
198
+ );
199
+ return { path, status, diff, binary: false, reconstructed: true };
200
+ }
201
+
202
+ /**
203
+ * Recover the install-time bytes of `path` from the re-materialized baseline
204
+ * tree, but only when they faithfully match what install produced: the
205
+ * re-materialized digest must equal the digest `recorded` at install. A
206
+ * mismatch (or a file the re-materialization did not produce) means the
207
+ * install-time content cannot be reconstructed — typically because the curated
208
+ * adapter overlay the file was built from changed since install — so `null` is
209
+ * returned and the caller flags the file instead of diffing fabricated bytes.
210
+ */
211
+ function baselineContent(
212
+ path: string,
213
+ baselineRoot: string,
214
+ recorded: Fingerprint,
215
+ materialized: Fingerprint,
216
+ ): FileContent | null {
217
+ if (materialized.files[path] !== recorded.files[path]) return null;
218
+ return readContent(join(baselineRoot, path));
219
+ }
220
+
221
+ /**
222
+ * Build a per-file unified diff for the on-disk install, classifying drift
223
+ * against the fingerprint `recorded` at install — the same baseline `inspect`
224
+ * uses, so the two surfaces always agree on which files changed and an adapter
225
+ * overlay that moved since install never reads as drift. The re-materialized
226
+ * tree supplies the baseline *bytes* for the diff, verified per file against
227
+ * `recorded`. The provenance sidecar is excluded on both sides — it never
228
+ * exists in the baseline and must not read as a local addition.
229
+ */
230
+ function buildFileDiffs(
231
+ baselineRoot: string,
232
+ target: string,
233
+ recorded: Fingerprint,
234
+ ): PluginFileDiff[] {
235
+ const comparison = compareFingerprint(target, recorded, [
236
+ INSTALL_META_FILENAME,
237
+ ]);
238
+ const materialized = computeFingerprint(baselineRoot, [
239
+ INSTALL_META_FILENAME,
240
+ ]);
241
+
242
+ const files: PluginFileDiff[] = [];
243
+ for (const path of comparison.modified) {
244
+ const before = baselineContent(path, baselineRoot, recorded, materialized);
245
+ files.push(
246
+ makeDiff(
247
+ path,
248
+ "modified",
249
+ before,
250
+ readContent(join(target, path)),
251
+ before !== null,
252
+ ),
253
+ );
254
+ }
255
+ for (const path of comparison.added) {
256
+ files.push(
257
+ makeDiff(path, "added", null, readContent(join(target, path)), true),
258
+ );
259
+ }
260
+ for (const path of comparison.removed) {
261
+ const before = baselineContent(path, baselineRoot, recorded, materialized);
262
+ files.push(makeDiff(path, "removed", before, null, before !== null));
263
+ }
264
+ files.sort((a, b) => a.path.localeCompare(b.path));
265
+ return files;
266
+ }
267
+
268
+ /**
269
+ * Resolve the unified diff of an installed plugin against its install-time
270
+ * baseline.
271
+ *
272
+ * Throws {@link PluginNotInstalledError} when no copy is installed,
273
+ * {@link PluginDiffUnavailableError} when the install recorded no commit to
274
+ * re-materialize, {@link PluginNotFoundError} when the recorded commit can no
275
+ * longer be fetched (e.g. the source repo or commit was removed), and
276
+ * propagates {@link materializePluginTree}'s errors (e.g. source unavailable)
277
+ * when the baseline clone itself fails.
278
+ */
279
+ export async function diffPlugin(
280
+ opts: DiffPluginOptions,
281
+ deps: DiffPluginDeps,
282
+ ): Promise<PluginDiffResult> {
283
+ const name = sanitizePluginName(opts.name);
284
+ const dir = deps.workspacePluginsDir ?? getWorkspacePluginsDir();
285
+ const target = join(dir, name);
286
+
287
+ if (!readInstalledPlugin(name, { workspacePluginsDir: dir })) {
288
+ throw new PluginNotInstalledError(name, target);
289
+ }
290
+
291
+ const meta = readInstallMeta(target);
292
+ const commit = meta?.commit ?? null;
293
+ if (!meta || !commit) {
294
+ throw new PluginDiffUnavailableError(
295
+ name,
296
+ `no install commit was recorded (an older or manually-copied install); reinstall with 'assistant plugins install ${name} --force' to record provenance`,
297
+ );
298
+ }
299
+ // Drift is classified against the install-time fingerprint (as `inspect`
300
+ // does), so without it there is no trustworthy baseline to diff against.
301
+ const recorded = meta.fingerprint;
302
+ if (!recorded) {
303
+ throw new PluginDiffUnavailableError(
304
+ name,
305
+ `no install-time fingerprint was recorded (an older or manually-copied install); reinstall with 'assistant plugins install ${name} --force' to record provenance`,
306
+ );
307
+ }
308
+
309
+ const source: PluginFetchSource = {
310
+ owner: meta.source.owner,
311
+ repo: meta.source.repo,
312
+ rootPath: meta.source.path ?? "",
313
+ ref: commit,
314
+ };
315
+
316
+ const baselineRoot = mkdtempSync(join(tmpdir(), `plugin-diff-${name}-`));
317
+ try {
318
+ const materialized = await materializePluginTree(
319
+ { source, name, stubRef: DEFAULT_PLUGIN_REF, destDir: baselineRoot },
320
+ {
321
+ fetch: deps.fetch,
322
+ runGit: deps.runGit,
323
+ runPostinstall: deps.runPostinstall,
324
+ },
325
+ );
326
+ if (materialized.fileCount === 0) {
327
+ throw new PluginNotFoundError(
328
+ name,
329
+ commit,
330
+ `${source.owner}/${source.repo}`,
331
+ );
332
+ }
333
+
334
+ const files = buildFileDiffs(baselineRoot, target, recorded);
335
+ return {
336
+ name,
337
+ target,
338
+ commit,
339
+ committedAt: meta.committedAt ?? materialized.committedAt ?? null,
340
+ clean: files.length === 0,
341
+ files,
342
+ };
343
+ } finally {
344
+ rmSync(baselineRoot, { recursive: true, force: true });
345
+ }
346
+ }
@@ -36,6 +36,10 @@ import {
36
36
  fetchMarketplaceEntries,
37
37
  type MarketplaceEntry,
38
38
  } from "./plugin-marketplace.js";
39
+ import {
40
+ detectPluginSurfaces,
41
+ type PluginSurfaces,
42
+ } from "./plugin-surfaces.js";
39
43
 
40
44
  /** Full commit SHA (40 hex SHA-1 or 64 hex SHA-256). */
41
45
  const FULL_SHA_RE = /^(?:[0-9a-f]{40}|[0-9a-f]{64})$/i;
@@ -131,6 +135,12 @@ export interface PluginInspection {
131
135
  readonly remote: PluginRemoteInfo | null;
132
136
  /** Marketplace fetch error message, when the catalog could not be read. */
133
137
  readonly remoteError: string | null;
138
+ /**
139
+ * Surfaces the installed copy contributes (skills, hooks, tools), read from
140
+ * its on-disk tree. `null` when the plugin is not installed — there is no
141
+ * tree to inspect, and the marketplace metadata does not enumerate surfaces.
142
+ */
143
+ readonly surfaces: PluginSurfaces | null;
134
144
  }
135
145
 
136
146
  /** Neither an installed copy nor a marketplace entry claims the name. */
@@ -266,6 +276,7 @@ export async function inspectPlugin(
266
276
  });
267
277
  const installed = entry !== null;
268
278
  const local = entry ? readLocal(entry, readInstallMeta(entry.target)) : null;
279
+ const surfaces = entry ? detectPluginSurfaces(entry.target) : null;
269
280
 
270
281
  let remote: PluginRemoteInfo | null = null;
271
282
  let remoteError: string | null = null;
@@ -294,7 +305,7 @@ export async function inspectPlugin(
294
305
  }
295
306
 
296
307
  const status = classify(installed, local, remote, remoteError);
297
- return { name, installed, status, local, remote, remoteError };
308
+ return { name, installed, status, local, remote, remoteError, surfaces };
298
309
  }
299
310
 
300
311
  function classify(
@@ -112,6 +112,16 @@ export interface InstallPluginOptions {
112
112
  readonly force?: boolean;
113
113
  /** Git ref (branch, tag, SHA) to fetch from. Defaults to {@link DEFAULT_PLUGIN_REF}. */
114
114
  readonly ref?: string;
115
+ /**
116
+ * Materialize this exact plugin commit SHA instead of the pin the marketplace
117
+ * manifest records, while still resolving the plugin's owner/repo/path (and
118
+ * the curated adapter stub) from the manifest at {@link InstallPluginOptions.ref}.
119
+ * This is the CLI-only escape hatch for installing an unreviewed revision; the
120
+ * adapter stub therefore comes from the manifest's `ref`, not the override's
121
+ * era, so an adapted plugin may not reproduce a historical version faithfully.
122
+ * Unset for normal installs, which take the reviewed pin from the manifest.
123
+ */
124
+ readonly commitOverride?: string;
115
125
  }
116
126
 
117
127
  /** Dependencies injected by the caller. */
@@ -221,7 +231,7 @@ function isTransientUpstreamStatus(res: Response): boolean {
221
231
  }
222
232
 
223
233
  /** Resolved GitHub coordinates a plugin name is fetched from. */
224
- interface PluginFetchSource {
234
+ export interface PluginFetchSource {
225
235
  readonly owner: string;
226
236
  readonly repo: string;
227
237
  /** Repo-relative directory holding the plugin root; `""` = repo root. */
@@ -345,7 +355,13 @@ export async function installPlugin(
345
355
  "plugins/marketplace.json",
346
356
  );
347
357
  }
348
- const ref = source.ref;
358
+ // A commit override installs a specific plugin revision while still taking
359
+ // owner/repo/path (and the adapter stub, via `marketplaceRef`) from the
360
+ // manifest; otherwise the reviewed pin from the manifest is materialized.
361
+ const effectiveSource: PluginFetchSource = opts.commitOverride
362
+ ? { ...source, ref: opts.commitOverride }
363
+ : source;
364
+ const ref = effectiveSource.ref;
349
365
 
350
366
  const pluginsDir = deps.workspacePluginsDir ?? getWorkspacePluginsDir();
351
367
  const target = join(pluginsDir, name);
@@ -376,22 +392,18 @@ export async function installPlugin(
376
392
  let commit: string | null = null;
377
393
  let committedAt: string | null = null;
378
394
  try {
379
- const cloned = await copyExternalViaGit(
380
- source,
381
- stagingDir,
382
- deps.runGit ?? defaultGitRunner,
395
+ const materialized = await materializePluginTree(
396
+ {
397
+ source: effectiveSource,
398
+ name,
399
+ stubRef: marketplaceRef,
400
+ destDir: stagingDir,
401
+ },
402
+ deps,
383
403
  );
384
- fileCount = cloned.fileCount;
385
- commit = cloned.commit;
386
- committedAt = cloned.committedAt;
387
- // An external clone is often a foreign-ecosystem plugin (e.g. a Claude
388
- // Code plugin) that the Vellum loader can't run as-is. When we curate an
389
- // adapter stub for it, overlay the stub and run its transform so the
390
- // materialized tree is a valid Vellum plugin. Raw clones (no stub) are
391
- // left untouched.
392
- if (fileCount > 0) {
393
- await applyAdapterStub(name, marketplaceRef, stagingDir, deps);
394
- }
404
+ fileCount = materialized.fileCount;
405
+ commit = materialized.commit;
406
+ committedAt = materialized.committedAt;
395
407
  } catch (err) {
396
408
  rmSync(stagingDir, { recursive: true, force: true });
397
409
  throw err;
@@ -399,9 +411,55 @@ export async function installPlugin(
399
411
 
400
412
  if (fileCount === 0) {
401
413
  rmSync(stagingDir, { recursive: true, force: true });
402
- throw new PluginNotFoundError(name, ref, sourceLabel(source));
414
+ throw new PluginNotFoundError(name, ref, sourceLabel(effectiveSource));
403
415
  }
404
416
 
417
+ finalizeStagedInstall(stagingDir, {
418
+ name,
419
+ source: effectiveSource,
420
+ ref,
421
+ commit,
422
+ committedAt,
423
+ pluginsDir,
424
+ });
425
+
426
+ return { name, target, fileCount, ref, commit, committedAt };
427
+ }
428
+
429
+ /** Inputs for {@link finalizeStagedInstall}. */
430
+ export interface FinalizeStagedInstallParams {
431
+ readonly name: string;
432
+ /** Source coordinates recorded in the provenance sidecar. */
433
+ readonly source: PluginFetchSource;
434
+ /** Ref recorded in the sidecar (the resolved commit SHA for marketplace installs). */
435
+ readonly ref: string;
436
+ readonly commit: string | null;
437
+ /** ISO-8601 committer timestamp of {@link FinalizeStagedInstallParams.commit} (UTC); null when unknown. */
438
+ readonly committedAt: string | null;
439
+ /** Served plugins directory; the staging dir is swapped into `<pluginsDir>/<name>`. */
440
+ readonly pluginsDir: string;
441
+ }
442
+
443
+ /**
444
+ * Fingerprint a fully-populated `stagingDir`, write its provenance sidecar, and
445
+ * atomically swap it into `<pluginsDir>/<name>`. Returns the final install path
446
+ * and the fingerprint that was recorded.
447
+ *
448
+ * Shared by {@link installPlugin} (fresh materialization) and the merge-based
449
+ * `plugins upgrade --strategy` path so both record identical provenance and use
450
+ * the same atomic rm+rename swap.
451
+ */
452
+ export function finalizeStagedInstall(
453
+ stagingDir: string,
454
+ {
455
+ name,
456
+ source,
457
+ ref,
458
+ commit,
459
+ committedAt,
460
+ pluginsDir,
461
+ }: FinalizeStagedInstallParams,
462
+ ): { target: string; fingerprint: Fingerprint } {
405
463
  // Hash the materialized tree before the sidecar is written (so the sidecar
406
464
  // never hashes itself) — the baseline `plugins inspect` uses to detect later
407
465
  // local edits. The per-file fingerprint answers "which files changed"; the
@@ -428,13 +486,14 @@ export async function installPlugin(
428
486
  // rm and the rename — and at that point the staging dir is fully populated.
429
487
  // Ensure the served `plugins/` directory exists: staging now lives outside
430
488
  // it, so the target's parent is no longer created as a side effect.
489
+ const target = join(pluginsDir, name);
431
490
  mkdirSync(pluginsDir, { recursive: true });
432
491
  if (existsSync(target)) {
433
492
  rmSync(target, { recursive: true, force: true });
434
493
  }
435
494
  renameSync(stagingDir, target);
436
495
 
437
- return { name, target, fileCount, ref, commit, committedAt };
496
+ return { target, fingerprint };
438
497
  }
439
498
 
440
499
  /** Cap on any single git invocation; a shallow fetch is well under this. */
@@ -523,6 +582,56 @@ export interface InstallMeta {
523
582
  readonly fingerprint: Fingerprint | null;
524
583
  }
525
584
 
585
+ /** Outcome of materializing an external plugin tree into a directory. */
586
+ export interface MaterializedTree {
587
+ /** Number of regular files written into the destination. */
588
+ readonly fileCount: number;
589
+ /** Commit SHA the source was cloned at; `null` when it could not be read. */
590
+ readonly commit: string | null;
591
+ /** ISO-8601 committer timestamp of {@link MaterializedTree.commit}; `null` when unread. */
592
+ readonly committedAt: string | null;
593
+ }
594
+
595
+ /**
596
+ * Produce the exact plugin tree an install stages, into `destDir`: clone the
597
+ * source at `source.ref`, then overlay the curated adapter stub (when one
598
+ * exists) so a foreign-ecosystem clone is translated into Vellum shape.
599
+ *
600
+ * `installPlugin` calls this and then fingerprints, records provenance, and
601
+ * swaps the result into the workspace. `diffPlugin` (see {@link ./diff-plugin})
602
+ * calls it with the *recorded install commit* to reconstruct the install-time
603
+ * baseline. Routing both through one path guarantees a re-materialized commit
604
+ * is byte-identical to what the original install produced, so an install-time
605
+ * adapter transform never reads as local drift when diffing.
606
+ */
607
+ export async function materializePluginTree(
608
+ opts: {
609
+ /** Source coordinates; `source.ref` selects the commit to clone. */
610
+ readonly source: PluginFetchSource;
611
+ /** Install name, used to locate the curated adapter stub. */
612
+ readonly name: string;
613
+ /** Ref the curated adapter stub is fetched at (the canonical repo ref). */
614
+ readonly stubRef: string;
615
+ /** Directory the tree is written into. */
616
+ readonly destDir: string;
617
+ },
618
+ deps: InstallPluginDeps,
619
+ ): Promise<MaterializedTree> {
620
+ const cloned = await copyExternalViaGit(
621
+ opts.source,
622
+ opts.destDir,
623
+ deps.runGit ?? defaultGitRunner,
624
+ );
625
+ // An external clone is often a foreign-ecosystem plugin (e.g. a Claude Code
626
+ // plugin) that the Vellum loader can't run as-is. When we curate an adapter
627
+ // stub for it, overlay the stub and run its transform so the materialized
628
+ // tree is a valid Vellum plugin. Raw clones (no stub) are left untouched.
629
+ if (cloned.fileCount > 0) {
630
+ await applyAdapterStub(opts.name, opts.stubRef, opts.destDir, deps);
631
+ }
632
+ return cloned;
633
+ }
634
+
526
635
  /**
527
636
  * Materialize an external plugin by shallow-cloning its repo at the pinned ref.
528
637
  *