@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
@@ -25,6 +25,10 @@
25
25
 
26
26
  import { z } from "zod";
27
27
 
28
+ import {
29
+ diffPlugin,
30
+ PluginDiffUnavailableError,
31
+ } from "../../cli/lib/diff-plugin.js";
28
32
  import {
29
33
  inspectPlugin,
30
34
  PluginInspectNotFoundError,
@@ -46,6 +50,12 @@ import {
46
50
  getPluginDetails,
47
51
  PluginDetailsNotFoundError,
48
52
  } from "../../cli/lib/plugin-details.js";
53
+ import {
54
+ DEFAULT_PIN_HISTORY_LIMIT,
55
+ listPinHistory,
56
+ PluginPinHistoryError,
57
+ resolvePinToMarketplaceCommit,
58
+ } from "../../cli/lib/plugin-pin-history.js";
49
59
  import {
50
60
  assertValidSearchPattern,
51
61
  filterPluginCatalog,
@@ -58,7 +68,9 @@ import {
58
68
  uninstallPlugin,
59
69
  } from "../../cli/lib/uninstall-plugin.js";
60
70
  import {
71
+ PluginMergeBaselineError,
61
72
  PluginNotUpgradableError,
73
+ type PluginUpgradeStrategy,
62
74
  upgradePlugin,
63
75
  } from "../../cli/lib/upgrade-plugin.js";
64
76
  import { ACTOR_PRINCIPALS } from "../auth/route-policy.js";
@@ -67,6 +79,7 @@ import {
67
79
  ConflictError,
68
80
  InternalError,
69
81
  NotFoundError,
82
+ RouteError,
70
83
  ServiceUnavailableError,
71
84
  } from "./errors.js";
72
85
  import type { RouteDefinition, RouteHandlerArgs } from "./types.js";
@@ -230,6 +243,12 @@ const pluginInstallRequestSchema = z.object({
230
243
  .boolean()
231
244
  .optional()
232
245
  .describe("Overwrite an existing install in place. Defaults to false."),
246
+ pin: z
247
+ .string()
248
+ .optional()
249
+ .describe(
250
+ "Install a specific reviewed marketplace pin (full commit SHA) instead of the current one. The pin is validated against the plugin's marketplace pin history (`GET /v1/plugins/:name/versions`) and installed from the marketplace revision that introduced it; an unreviewed SHA is rejected. Use this to roll a plugin back to an older reviewed version.",
251
+ ),
233
252
  });
234
253
 
235
254
  const pluginInstallResponseSchema = z.object({
@@ -244,6 +263,32 @@ const pluginInstallResponseSchema = z.object({
244
263
  ref: z.string().describe("Git ref the plugin was fetched from."),
245
264
  });
246
265
 
266
+ const pluginPinHistoryEntrySchema = z.object({
267
+ pin: z
268
+ .string()
269
+ .describe("Plugin commit SHA pinned at this point in marketplace history."),
270
+ marketplaceCommit: z
271
+ .string()
272
+ .describe(
273
+ "Marketplace-manifest commit to install this pin from (the newest commit carrying it).",
274
+ ),
275
+ promotedAt: z
276
+ .string()
277
+ .nullable()
278
+ .describe(
279
+ "ISO-8601 committer date (UTC) of the marketplace commit that promoted this pin; null when unreadable.",
280
+ ),
281
+ current: z
282
+ .boolean()
283
+ .describe("True for the pin currently active on the default branch."),
284
+ });
285
+
286
+ const pluginVersionsResponseSchema = z
287
+ .array(pluginPinHistoryEntrySchema)
288
+ .describe(
289
+ "Distinct marketplace pins a plugin has been promoted to, newest first; the first entry is the current pin. Empty when the plugin has no resolvable history.",
290
+ );
291
+
247
292
  const fingerprintComparisonSchema = z
248
293
  .object({
249
294
  modified: z
@@ -366,6 +411,26 @@ const pluginRemoteInfoSchema = z
366
411
  })
367
412
  .describe("The marketplace's current pin and advertised metadata.");
368
413
 
414
+ const pluginSurfacesSchema = z
415
+ .object({
416
+ skills: z
417
+ .array(z.string())
418
+ .describe("Skill ids shipped at `skills/<id>/SKILL.md`."),
419
+ hooks: z
420
+ .array(z.string())
421
+ .describe(
422
+ "Lifecycle hook names from `hooks/<name>.{ts,js}` (e.g. `pre-model-call`).",
423
+ ),
424
+ tools: z
425
+ .array(z.string())
426
+ .describe(
427
+ "Registered tool names from `tools/<name>.{ts,js}` (filenames derived to tool names, e.g. `create-issue` \u2192 `create_issue`).",
428
+ ),
429
+ })
430
+ .describe(
431
+ "Surfaces the installed copy contributes, read from its on-disk tree.",
432
+ );
433
+
369
434
  const pluginInspectResponseSchema = z.object({
370
435
  name: z
371
436
  .string()
@@ -401,6 +466,11 @@ const pluginInspectResponseSchema = z.object({
401
466
  .describe(
402
467
  "Marketplace fetch error message, when the catalog could not be read.",
403
468
  ),
469
+ surfaces: pluginSurfacesSchema
470
+ .nullable()
471
+ .describe(
472
+ "Surfaces the installed copy contributes (skills, hooks, tools); null when the plugin is not installed.",
473
+ ),
404
474
  });
405
475
 
406
476
  const pluginUpgradeRequestSchema = z.object({
@@ -410,6 +480,12 @@ const pluginUpgradeRequestSchema = z.object({
410
480
  .describe(
411
481
  "Report what would change without modifying the install. Defaults to false.",
412
482
  ),
483
+ strategy: z
484
+ .enum(["ours", "theirs", "overwrite", "assistant"])
485
+ .optional()
486
+ .describe(
487
+ "How to reconcile local edits with the pin. `overwrite` (default) discards local edits and re-installs the pin; `ours`/`theirs` three-way merge, resolving conflicting hunks toward the local edit or the pin respectively; `assistant` is not yet supported.",
488
+ ),
413
489
  });
414
490
 
415
491
  const pluginUpgradeResponseSchema = z.object({
@@ -452,6 +528,19 @@ const pluginUpgradeResponseSchema = z.object({
452
528
  "Files materialized by the upgrade; null for a no-op or dry run.",
453
529
  ),
454
530
  dryRun: z.boolean().describe("Whether this was a dry run (no changes made)."),
531
+ strategy: z
532
+ .enum(["ours", "theirs", "overwrite", "assistant"])
533
+ .describe("Conflict-resolution strategy the upgrade applied."),
534
+ conflicts: z
535
+ .array(z.string())
536
+ .describe(
537
+ "Paths left for the assistant to resolve under the `assistant` strategy: text files carry git conflict markers, modify/delete divergences keep the surviving content. Empty for other strategies.",
538
+ ),
539
+ binaryConflicts: z
540
+ .array(z.string())
541
+ .describe(
542
+ "Binary files that conflicted under the `assistant` strategy; the local copy was kept since markers cannot be written into binary content. Empty for other strategies.",
543
+ ),
455
544
  provenanceWasUnknown: z
456
545
  .boolean()
457
546
  .describe(
@@ -459,6 +548,62 @@ const pluginUpgradeResponseSchema = z.object({
459
548
  ),
460
549
  });
461
550
 
551
+ const pluginFileDiffSchema = z
552
+ .object({
553
+ path: z.string().describe("POSIX-relative path within the plugin root."),
554
+ status: z
555
+ .enum(["modified", "added", "removed"])
556
+ .describe(
557
+ "Whether the file was edited, newly added, or deleted since install.",
558
+ ),
559
+ diff: z
560
+ .string()
561
+ .describe(
562
+ "Unified diff (`--- a/… / +++ b/…`) of the file. A short `Binary files differ` marker for binary files, or a `Baseline unavailable` marker when `reconstructed` is false.",
563
+ ),
564
+ binary: z
565
+ .boolean()
566
+ .describe(
567
+ "True when either side was detected as binary (NUL byte present).",
568
+ ),
569
+ reconstructed: z
570
+ .boolean()
571
+ .describe(
572
+ "True when the install-time baseline for this file was faithfully recovered (re-materialized bytes hash-match the install fingerprint). False when it could not be reconstructed (e.g. the curated adapter overlay changed since install), in which case `diff` is a marker, not a patch. Always true for added files.",
573
+ ),
574
+ })
575
+ .describe("Unified diff of a single drifted file.");
576
+
577
+ const pluginDiffResponseSchema = z.object({
578
+ name: z
579
+ .string()
580
+ .describe("Install name. Matches `assistant plugins install <name>`."),
581
+ target: z
582
+ .string()
583
+ .describe("Absolute path to the installed plugin directory on the host."),
584
+ commit: z
585
+ .string()
586
+ .describe(
587
+ "Commit the baseline was re-materialized from (the recorded install SHA).",
588
+ ),
589
+ committedAt: z
590
+ .string()
591
+ .nullable()
592
+ .describe(
593
+ "ISO-8601 committer timestamp (UTC) of `commit`; null when not recorded.",
594
+ ),
595
+ clean: z
596
+ .boolean()
597
+ .describe(
598
+ "True when the on-disk tree exactly matches the re-materialized baseline.",
599
+ ),
600
+ files: z
601
+ .array(pluginFileDiffSchema)
602
+ .describe(
603
+ "One entry per drifted file, sorted by path. Empty when `clean`.",
604
+ ),
605
+ });
606
+
462
607
  // ---------------------------------------------------------------------------
463
608
  // Helpers
464
609
  // ---------------------------------------------------------------------------
@@ -665,23 +810,52 @@ async function handleGetPluginDetails({
665
810
  // Handler — install
666
811
  // ---------------------------------------------------------------------------
667
812
 
813
+ /**
814
+ * Resolve the marketplace ref an install should read from. With no `pin`, that
815
+ * is the default reviewed branch. With a `pin`, it is the marketplace commit
816
+ * that introduced that pin — but only when the pin appears in the plugin's
817
+ * reviewed history; an unreviewed SHA is rejected as a bad request so the route
818
+ * never installs an unvetted revision.
819
+ */
820
+ async function resolveInstallMarketplaceRef(
821
+ name: string,
822
+ pin: string | undefined,
823
+ ): Promise<string> {
824
+ if (!pin) return DEFAULT_PLUGIN_REF;
825
+ const entry = await resolvePinToMarketplaceCommit(name, pin, {
826
+ fetch: globalThis.fetch.bind(globalThis),
827
+ });
828
+ if (!entry) {
829
+ throw new BadRequestError(
830
+ `"${pin}" is not a reviewed marketplace pin for "${name}". ` +
831
+ `Use GET /v1/plugins/${name}/versions to list installable pins.`,
832
+ );
833
+ }
834
+ return entry.marketplaceCommit;
835
+ }
836
+
668
837
  async function handleInstallPlugin({ body = {} }: RouteHandlerArgs) {
669
838
  const name = typeof body.name === "string" ? body.name : "";
670
839
  if (!name) {
671
840
  throw new BadRequestError("`name` is required");
672
841
  }
673
842
  const force = typeof body.force === "boolean" ? body.force : undefined;
843
+ const pin = typeof body.pin === "string" ? body.pin : undefined;
674
844
 
675
- // The ref is pinned to the curated `DEFAULT_PLUGIN_REF` rather than taken
676
- // from the request: a caller-supplied ref would let any `settings.write`
677
- // principal install from an unreviewed revision (a PR branch, fork ref,
678
- // ...) whose marketplace manifest could carry attacker code that the loader
679
- // then dynamically imports. Installs over HTTP therefore only ever resolve
680
- // against the reviewed catalog on the default ref. Operators who need
681
- // another ref use the local CLI's `assistant plugins install --ref`.
845
+ // The marketplace ref is never taken raw from the request: a caller-supplied
846
+ // ref would let any `settings.write` principal install from an unreviewed
847
+ // revision (a PR branch, fork ref, ...) whose manifest could carry attacker
848
+ // code the loader then dynamically imports. Installs over HTTP therefore
849
+ // resolve only against reviewed history. A `pin` is honored by mapping it —
850
+ // server-side — to the marketplace commit that introduced it, but only if it
851
+ // appears in the plugin's reviewed pin history; an unreviewed SHA is refused.
852
+ // The default install reads the current catalog on `DEFAULT_PLUGIN_REF`.
853
+ // Operators who need an unreviewed revision use the local CLI's
854
+ // `assistant plugins install --pin <sha> --allow-unreviewed`.
682
855
  try {
856
+ const marketplaceRef = await resolveInstallMarketplaceRef(name, pin);
683
857
  const result = await installPlugin(
684
- { name, ref: DEFAULT_PLUGIN_REF, force },
858
+ { name, ref: marketplaceRef, force },
685
859
  { fetch: globalThis.fetch.bind(globalThis) },
686
860
  );
687
861
  return {
@@ -692,6 +866,11 @@ async function handleInstallPlugin({ body = {} }: RouteHandlerArgs) {
692
866
  ref: result.ref,
693
867
  };
694
868
  } catch (err) {
869
+ // Pin resolution already maps unreviewed/bad-pin cases to a RouteError;
870
+ // re-throw those verbatim rather than masking them as a 500.
871
+ if (err instanceof RouteError) {
872
+ throw err;
873
+ }
695
874
  if (err instanceof InvalidPluginNameError) {
696
875
  throw new BadRequestError(err.message);
697
876
  }
@@ -706,6 +885,10 @@ async function handleInstallPlugin({ body = {} }: RouteHandlerArgs) {
706
885
  if (err instanceof PluginSourceUnavailableError) {
707
886
  throw new ServiceUnavailableError(err.message);
708
887
  }
888
+ // The pin-history read hits GitHub too; treat its failures as retryable.
889
+ if (err instanceof PluginPinHistoryError) {
890
+ throw new ServiceUnavailableError(err.message);
891
+ }
709
892
  throw new InternalError(
710
893
  err instanceof Error ? err.message : "plugin install failed",
711
894
  );
@@ -740,6 +923,84 @@ async function handleInspectPlugin({ pathParams = {} }: RouteHandlerArgs) {
740
923
  }
741
924
  }
742
925
 
926
+ // ---------------------------------------------------------------------------
927
+ // Handler — versions (marketplace pin history)
928
+ // ---------------------------------------------------------------------------
929
+
930
+ async function handlePluginVersions({
931
+ pathParams = {},
932
+ queryParams = {},
933
+ }: RouteHandlerArgs) {
934
+ const rawName = pathParams.name ?? "";
935
+
936
+ let limit: number | undefined;
937
+ if (queryParams.limit !== undefined) {
938
+ const parsed = Number.parseInt(queryParams.limit, 10);
939
+ if (!Number.isInteger(parsed) || parsed < 1) {
940
+ throw new BadRequestError("`limit` must be a positive integer.");
941
+ }
942
+ limit = parsed;
943
+ }
944
+
945
+ try {
946
+ return await listPinHistory(
947
+ rawName,
948
+ { fetch: globalThis.fetch.bind(globalThis) },
949
+ limit !== undefined ? { limit } : {},
950
+ );
951
+ } catch (err) {
952
+ if (err instanceof InvalidPluginNameError) {
953
+ throw new BadRequestError(err.message);
954
+ }
955
+ // The history read hits GitHub; its failures are retryable upstream errors.
956
+ if (err instanceof PluginPinHistoryError) {
957
+ throw new ServiceUnavailableError(err.message);
958
+ }
959
+ throw new InternalError(
960
+ err instanceof Error ? err.message : "plugin versions failed",
961
+ );
962
+ }
963
+ }
964
+
965
+ // ---------------------------------------------------------------------------
966
+ // Handler — diff
967
+ // ---------------------------------------------------------------------------
968
+
969
+ async function handleDiffPlugin({ pathParams = {} }: RouteHandlerArgs) {
970
+ const rawName = pathParams.name ?? "";
971
+
972
+ try {
973
+ return await diffPlugin(
974
+ { name: rawName },
975
+ { fetch: globalThis.fetch.bind(globalThis) },
976
+ );
977
+ } catch (err) {
978
+ if (err instanceof InvalidPluginNameError) {
979
+ throw new BadRequestError(err.message);
980
+ }
981
+ if (
982
+ err instanceof PluginNotInstalledError ||
983
+ err instanceof PluginNotFoundError
984
+ ) {
985
+ throw new NotFoundError(err.message);
986
+ }
987
+ // The install exists but recorded no commit to re-materialize a baseline
988
+ // from — a permanent state the caller cannot resolve by retrying. 409
989
+ // marks the request as well-formed but not actionable in the current state.
990
+ if (err instanceof PluginDiffUnavailableError) {
991
+ throw new ConflictError(err.message);
992
+ }
993
+ // A rate-limited or temporarily-down source (the plugin repo) is a
994
+ // retryable outage, not a conflict — 503.
995
+ if (err instanceof PluginSourceUnavailableError) {
996
+ throw new ServiceUnavailableError(err.message);
997
+ }
998
+ throw new InternalError(
999
+ err instanceof Error ? err.message : "plugin diff failed",
1000
+ );
1001
+ }
1002
+ }
1003
+
743
1004
  // ---------------------------------------------------------------------------
744
1005
  // Handler — upgrade
745
1006
  // ---------------------------------------------------------------------------
@@ -750,6 +1011,10 @@ async function handleUpgradePlugin({
750
1011
  }: RouteHandlerArgs) {
751
1012
  const rawName = pathParams.name ?? "";
752
1013
  const dryRun = typeof body.dryRun === "boolean" ? body.dryRun : undefined;
1014
+ const strategy =
1015
+ typeof body.strategy === "string"
1016
+ ? (body.strategy as PluginUpgradeStrategy)
1017
+ : undefined;
753
1018
 
754
1019
  // Like install, the upgrade target ref is the curated marketplace pin
755
1020
  // (resolved inside `upgradePlugin` via `inspectPlugin`), never a
@@ -757,7 +1022,7 @@ async function handleUpgradePlugin({
757
1022
  // upgrade at an unreviewed revision.
758
1023
  try {
759
1024
  const result = await upgradePlugin(
760
- { name: rawName, dryRun },
1025
+ { name: rawName, dryRun, strategy },
761
1026
  { fetch: globalThis.fetch.bind(globalThis) },
762
1027
  );
763
1028
  return {
@@ -770,6 +1035,9 @@ async function handleUpgradePlugin({
770
1035
  target: result.target,
771
1036
  fileCount: result.fileCount,
772
1037
  dryRun: result.dryRun,
1038
+ strategy: result.strategy,
1039
+ conflicts: result.conflicts,
1040
+ binaryConflicts: result.binaryConflicts,
773
1041
  provenanceWasUnknown: result.provenanceWasUnknown,
774
1042
  };
775
1043
  } catch (err) {
@@ -782,6 +1050,12 @@ async function handleUpgradePlugin({
782
1050
  if (err instanceof PluginNotFoundError) {
783
1051
  throw new NotFoundError(err.message);
784
1052
  }
1053
+ // A merge strategy was requested but the install-time baseline can't be
1054
+ // reconstructed — a well-formed request that isn't actionable in the
1055
+ // current state (the caller can retry with `overwrite` or reinstall).
1056
+ if (err instanceof PluginMergeBaselineError) {
1057
+ throw new ConflictError(err.message);
1058
+ }
785
1059
  // The install exists but has no marketplace entry to advance to — a
786
1060
  // permanent state the caller cannot resolve by retrying. 409 marks the
787
1061
  // request as well-formed but not actionable in the current state.
@@ -992,6 +1266,87 @@ export const ROUTES: RouteDefinition[] = [
992
1266
  },
993
1267
  handler: handleInspectPlugin,
994
1268
  },
1269
+ {
1270
+ operationId: "plugins_versions",
1271
+ endpoint: "plugins/:name/versions",
1272
+ method: "GET",
1273
+ policy: {
1274
+ requiredScopes: ["settings.read"],
1275
+ allowedPrincipalTypes: ACTOR_PRINCIPALS,
1276
+ },
1277
+ summary: "List a plugin's reviewed marketplace pins",
1278
+ description: `Report the distinct marketplace pins a plugin has been promoted to over time, newest first (the first entry is the current pin), capped at \`limit\` (default ${DEFAULT_PIN_HISTORY_LIMIT}). The curated \`marketplace.json\` stores only the current pin, so this is reconstructed from the manifest's own commit history on the default branch — every entry is therefore a reviewed, known-good revision. Pair with \`POST /v1/plugins/install\`'s \`pin\` field to roll a plugin back to an older reviewed version. An unknown name (never in the manifest) returns an empty array, not 404.`,
1279
+ tags: ["plugins"],
1280
+ pathParams: [
1281
+ {
1282
+ name: "name",
1283
+ type: "string",
1284
+ description:
1285
+ "Install name. Must match the kebab-case name accepted by `assistant plugins install`.",
1286
+ },
1287
+ ],
1288
+ queryParams: [
1289
+ {
1290
+ name: "limit",
1291
+ type: "string",
1292
+ required: false,
1293
+ description: `Maximum number of pins to return (positive integer; default ${DEFAULT_PIN_HISTORY_LIMIT}).`,
1294
+ },
1295
+ ],
1296
+ responseBody: pluginVersionsResponseSchema,
1297
+ additionalResponses: {
1298
+ "400": {
1299
+ description:
1300
+ "The plugin name failed sanitization, or `limit` was not a positive integer.",
1301
+ },
1302
+ "503": {
1303
+ description:
1304
+ "The marketplace pin history could not be read from GitHub (rate-limited or upstream error); retryable.",
1305
+ },
1306
+ },
1307
+ handler: handlePluginVersions,
1308
+ },
1309
+ {
1310
+ operationId: "plugins_diff",
1311
+ endpoint: "plugins/:name/diff",
1312
+ method: "POST",
1313
+ policy: {
1314
+ requiredScopes: ["settings.read"],
1315
+ allowedPrincipalTypes: ACTOR_PRINCIPALS,
1316
+ },
1317
+ summary: "Diff a plugin against its install commit",
1318
+ description:
1319
+ "Show a unified diff of local edits to an installed plugin against the exact commit it was installed at (recorded in its `install-meta.json`). Computing the diff re-materializes the baseline through the install pipeline — a network clone plus a temp-dir write — so this is a POST: it is not a safe, cacheable GET even though it leaves persistent state untouched (requires only `settings.read`). Drift is classified against the install-time fingerprint so an adapter overlay that moved since install never reads as a local change. Returns the baseline `commit`, a `clean` flag, and a `files` array of `{ path, status, diff, binary, reconstructed }` for each modified/added/removed file. The baseline is the install commit, not the marketplace pin — comparing against the current pin is `POST /v1/plugins/:name/upgrade` with `dryRun`. A name with no installed copy returns 404; an install that recorded no commit or fingerprint returns 409; an unreachable source returns 503. Mirrors the CLI's `assistant plugins diff <name>`.",
1320
+ tags: ["plugins"],
1321
+ pathParams: [
1322
+ {
1323
+ name: "name",
1324
+ type: "string",
1325
+ description:
1326
+ "Install name. Must match the kebab-case name accepted by `assistant plugins install`.",
1327
+ },
1328
+ ],
1329
+ responseBody: pluginDiffResponseSchema,
1330
+ additionalResponses: {
1331
+ "400": {
1332
+ description:
1333
+ "The plugin name failed sanitization (e.g. contained slashes, dots, or uppercase letters).",
1334
+ },
1335
+ "404": {
1336
+ description:
1337
+ "No copy of the plugin is installed, or its recorded commit resolves to nothing.",
1338
+ },
1339
+ "409": {
1340
+ description:
1341
+ "The install recorded no commit or no fingerprint to re-materialize and verify a baseline from.",
1342
+ },
1343
+ "503": {
1344
+ description:
1345
+ "The plugin source (GitHub) was temporarily unavailable; the diff is retryable.",
1346
+ },
1347
+ },
1348
+ handler: handleDiffPlugin,
1349
+ },
995
1350
  {
996
1351
  operationId: "plugins_upgrade",
997
1352
  endpoint: "plugins/:name/upgrade",
@@ -1002,7 +1357,7 @@ export const ROUTES: RouteDefinition[] = [
1002
1357
  },
1003
1358
  summary: "Upgrade a plugin to the marketplace pin",
1004
1359
  description:
1005
- 'Move an installed plugin to the marketplace\'s current pinned commit, re-materializing it under `<workspaceDir>/plugins/<name>/`. Always resolves against the curated marketplace pin (no caller-supplied ref), mirroring `plugins install`\'s curation boundary. A no-op (`outcome: "already-up-to-date"`) when the installed commit already equals the pin; pass `dryRun` to preview the move (`outcome: "would-upgrade"`) without touching the install. Installs lacking provenance are re-pinned to the current SHA. The assistant must be restarted to load the upgraded code. This does not gate on local edits — callers should consult `GET /v1/plugins/:name/inspect` (`local.localChanges`) first and confirm before overwriting. Mirrors the CLI\'s `assistant plugins upgrade <name>`.',
1360
+ 'Move an installed plugin to the marketplace\'s current pinned commit, re-materializing it under `<workspaceDir>/plugins/<name>/`. Always resolves against the curated marketplace pin (no caller-supplied ref), mirroring `plugins install`\'s curation boundary. A no-op (`outcome: "already-up-to-date"`) when the installed commit already equals the pin; pass `dryRun` to preview the move (`outcome: "would-upgrade"`) without touching the install. Installs lacking provenance are re-pinned to the current SHA. The assistant must be restarted to load the upgraded code. `strategy` controls how local edits are reconciled: `overwrite` (default) discards them and re-installs the pin wholesale; `ours`/`theirs`/`assistant` perform a three-way merge against the re-materialized install commit, carrying non-conflicting edits from both sides forward and resolving conflicting hunks toward the local edit (`ours`) or the pin (`theirs`), or writing git conflict markers into the file and reporting them in `conflicts`/`binaryConflicts` for the assistant to resolve (`assistant`). A merge strategy whose install-time baseline cannot be reconstructed returns 409. Mirrors the CLI\'s `assistant plugins upgrade <name> [--strategy <s>]`.',
1006
1361
  tags: ["plugins"],
1007
1362
  pathParams: [
1008
1363
  {
@@ -1025,7 +1380,7 @@ export const ROUTES: RouteDefinition[] = [
1025
1380
  },
1026
1381
  "409": {
1027
1382
  description:
1028
- "The install exists but has no marketplace entry to advance to.",
1383
+ "The install exists but has no marketplace entry to advance to, or a merge strategy was requested whose install-time baseline cannot be reconstructed.",
1029
1384
  },
1030
1385
  "503": {
1031
1386
  description:
@@ -7,8 +7,6 @@
7
7
 
8
8
  import { z } from "zod";
9
9
 
10
- import { isAssistantFeatureFlagEnabled } from "../../config/assistant-feature-flags.js";
11
- import { getConfig } from "../../config/loader.js";
12
10
  import { getOrCreateConversation } from "../../daemon/conversation-store.js";
13
11
  import { INTERNAL_GUARDIAN_TRUST_CONTEXT } from "../../daemon/trust-context.js";
14
12
  import { bootstrapConversation } from "../../memory/conversation-bootstrap.js";
@@ -299,7 +297,6 @@ function handleCreateSchedule(body: Record<string, unknown>) {
299
297
  }
300
298
 
301
299
  if (mode === "workflow") {
302
- assertWorkflowsEnabled();
303
300
  const workflowName =
304
301
  typeof body.workflowName === "string" ? body.workflowName.trim() : "";
305
302
  if (!workflowName) {
@@ -351,17 +348,6 @@ function handleCreateSchedule(body: Record<string, unknown>) {
351
348
  return handleListSchedules({});
352
349
  }
353
350
 
354
- /**
355
- * Reject workflow-mode schedule operations when the `workflows` feature flag
356
- * is off. Scheduled workflow runs would fail at trigger time (the run manager
357
- * hard-fails with WorkflowsDisabledError), so we fail fast at the route.
358
- */
359
- function assertWorkflowsEnabled(): void {
360
- if (!isAssistantFeatureFlagEnabled("workflows", getConfig())) {
361
- throw new BadRequestError("Workflows are not enabled.");
362
- }
363
- }
364
-
365
351
  function handleToggleSchedule(id: string, body: Record<string, unknown>) {
366
352
  const enabled = body.enabled;
367
353
  if (typeof enabled !== "boolean") {
@@ -427,11 +413,6 @@ function handleUpdateSchedule(id: string, body: Record<string, unknown>) {
427
413
  );
428
414
  }
429
415
 
430
- // Switching a schedule into workflow mode requires the workflows flag.
431
- if (body.mode === "workflow") {
432
- assertWorkflowsEnabled();
433
- }
434
-
435
416
  // Mirror the create-side validation: a schedule whose RESULTING mode is
436
417
  // `workflow` must carry a non-empty `workflowName`. Without this, a PATCH can
437
418
  // leave a workflow-mode schedule nameless — the scheduler then hits the
@@ -946,9 +927,6 @@ async function handleRunScheduleNow(id: string) {
946
927
  // `schedule.message` (usually empty — workflow-mode create no longer requires
947
928
  // a message), running a no-op normal turn instead of the workflow.
948
929
  if (schedule.mode === "workflow") {
949
- // Fail fast at the route when the flag is off (the run manager would also
950
- // hard-fail with WorkflowsDisabledError); matches create/update handling.
951
- assertWorkflowsEnabled();
952
930
  if (!schedule.workflowName) {
953
931
  throw new BadRequestError("Workflow schedule has no workflowName");
954
932
  }
@@ -22,10 +22,12 @@ import {
22
22
  invalidateConfigCache,
23
23
  } from "../../config/loader.js";
24
24
  import type { CesClient } from "../../credential-execution/client.js";
25
+ import { maybeReseedCapabilitiesAfterManagedCredential } from "../../daemon/memory-v2-startup.js";
25
26
  import { setSentryOrganizationId, setSentryUserId } from "../../instrument.js";
26
27
  import { clearEmbeddingBackendCache } from "../../memory/embedding-backend.js";
27
28
  import { syncManualTokenConnection } from "../../oauth/manual-token-connection.js";
28
29
  import { validateAnthropicApiKey } from "../../providers/anthropic/client.js";
30
+ import { validateAtlasCloudApiKey } from "../../providers/atlascloud/client.js";
29
31
  import { validateGeminiApiKey } from "../../providers/gemini/client.js";
30
32
  import { validateMinimaxApiKey } from "../../providers/minimax/client.js";
31
33
  import { validateOpenAIApiKey } from "../../providers/openai/client.js";
@@ -206,6 +208,15 @@ async function handleAddSecret({ body }: RouteHandlerArgs) {
206
208
  );
207
209
  return { success: false, error: validation.reason };
208
210
  }
211
+ } else if (name === "atlascloud") {
212
+ const validation = await validateAtlasCloudApiKey(value);
213
+ if (!validation.valid) {
214
+ log.warn(
215
+ { provider: name, reason: validation.reason },
216
+ "API key validation failed",
217
+ );
218
+ return { success: false, error: validation.reason };
219
+ }
209
220
  }
210
221
 
211
222
  const stored = await setSecureKeyAsync(
@@ -286,6 +297,10 @@ async function handleAddSecret({ body }: RouteHandlerArgs) {
286
297
  }
287
298
  if (isManagedProxyCredential(service, field)) {
288
299
  await refreshProvidersAfterSecretChange();
300
+ // Close the first-boot race where the startup capability seed ran before
301
+ // the managed embedding credential was provisioned, leaving skill/CLI
302
+ // pages unseeded until restart. Detached — must not block the response.
303
+ void maybeReseedCapabilitiesAfterManagedCredential(getConfig());
289
304
  if (service === "vellum" && field === "assistant_api_key") {
290
305
  const generation = ++apiKeyGeneration;
291
306
  const deps = getSecretsDeps();
@@ -13,6 +13,7 @@ import type { ChannelId } from "../../channels/types.js";
13
13
  import { isHttpAuthDisabled } from "../../config/env.js";
14
14
  import { findGuardianForChannel } from "../../contacts/contact-store.js";
15
15
  import { getLogger } from "../../util/logger.js";
16
+ import type { TrustClass } from "../actor-trust-resolver.js";
16
17
  import { DAEMON_INTERNAL_ASSISTANT_ID } from "../assistant-scope.js";
17
18
  import { ACTOR_PRINCIPALS } from "../auth/route-policy.js";
18
19
  import { processGuardianDecision } from "../guardian-action-service.js";
@@ -47,7 +48,7 @@ const log = getLogger("surface-action-routes");
47
48
  function applyTrustContext(
48
49
  conversation: {
49
50
  setTrustContext?(ctx: {
50
- trustClass: "guardian" | "trusted_contact" | "unknown";
51
+ trustClass: TrustClass;
51
52
  sourceChannel: ChannelId;
52
53
  }): void;
53
54
  },