@vellumai/assistant 0.9.0 → 0.10.0-staging.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (572) hide show
  1. package/ARCHITECTURE.md +18 -34
  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 +976 -63
  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__/always-loaded-tools-guard.test.ts +2 -3
  28. package/src/__tests__/app-compiler.test.ts +15 -1
  29. package/src/__tests__/app-dir-path-guard.test.ts +0 -1
  30. package/src/__tests__/assistant-feature-flag-guard.test.ts +1 -4
  31. package/src/__tests__/assistant-feature-flag-guardrails.test.ts +0 -2
  32. package/src/__tests__/auth-fallback-events-store.test.ts +6 -14
  33. package/src/__tests__/avatar-identity-sync.test.ts +2 -27
  34. package/src/__tests__/btw-routes.test.ts +6 -8
  35. package/src/__tests__/call-pointer-messages.test.ts +28 -0
  36. package/src/__tests__/cancel-clears-processing.test.ts +89 -0
  37. package/src/__tests__/channel-approval-routes.test.ts +0 -4
  38. package/src/__tests__/channel-inbound-disk-pressure.test.ts +5 -15
  39. package/src/__tests__/checker.test.ts +0 -3
  40. package/src/__tests__/cli-memory-v2-reembed-skills.test.ts +3 -4
  41. package/src/__tests__/compactor-image-manifest-trust.test.ts +21 -1
  42. package/src/__tests__/compactor-summary-call-truncation.test.ts +223 -0
  43. package/src/__tests__/config-loader-backfill.test.ts +268 -27
  44. package/src/__tests__/config-schema.test.ts +35 -0
  45. package/src/__tests__/config-watcher.test.ts +0 -18
  46. package/src/__tests__/confirmation-request-guardian-bridge.test.ts +2 -2
  47. package/src/__tests__/contact-store-user-file.test.ts +0 -6
  48. package/src/__tests__/contacts-tools.test.ts +29 -0
  49. package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +22 -0
  50. package/src/__tests__/conversation-agent-loop-overflow.test.ts +1 -0
  51. package/src/__tests__/conversation-agent-loop.test.ts +58 -0
  52. package/src/__tests__/conversation-attention-telegram.test.ts +0 -1
  53. package/src/__tests__/conversation-lifecycle.test.ts +7 -9
  54. package/src/__tests__/conversation-load-history-repair.test.ts +101 -0
  55. package/src/__tests__/conversation-routes-guardian-reply.test.ts +15 -12
  56. package/src/__tests__/conversation-surfaces-activation-emit.test.ts +6 -3
  57. package/src/__tests__/conversation-title-service.test.ts +62 -0
  58. package/src/__tests__/credential-broker.test.ts +449 -1
  59. package/src/__tests__/credential-execution-shell-lockdown.test.ts +18 -11
  60. package/src/__tests__/credential-execution-tools.test.ts +0 -1
  61. package/src/__tests__/credential-prompt-route.test.ts +4 -4
  62. package/src/__tests__/credential-routes.test.ts +360 -0
  63. package/src/__tests__/credential-security-invariants.test.ts +4 -13
  64. package/src/__tests__/disk-pressure-policy.test.ts +12 -0
  65. package/src/__tests__/disk-usage.test.ts +65 -0
  66. package/src/__tests__/dynamic-page-surface.test.ts +152 -1
  67. package/src/__tests__/fixtures/credential-security-fixtures.ts +2 -33
  68. package/src/__tests__/gateway-flag-listener.test.ts +110 -1
  69. package/src/__tests__/gateway-only-guard.test.ts +3 -7
  70. package/src/__tests__/guardian-binding-drift-heal.test.ts +1 -1
  71. package/src/__tests__/guardian-card-withdrawal.test.ts +403 -0
  72. package/src/__tests__/guardian-decision-primitive-canonical.test.ts +5 -3
  73. package/src/__tests__/guardian-grant-minting.test.ts +3 -35
  74. package/src/__tests__/guardian-routing-invariants.test.ts +64 -26
  75. package/src/__tests__/guardian-routing-state.test.ts +0 -1
  76. package/src/__tests__/headless-browser-mode.test.ts +10 -0
  77. package/src/__tests__/headless-browser-navigate.test.ts +8 -3
  78. package/src/__tests__/helpers/create-guardian-binding.ts +0 -1
  79. package/src/__tests__/host-browser-proxy.test.ts +87 -0
  80. package/src/__tests__/identity-routes.test.ts +0 -189
  81. package/src/__tests__/inbound-invite-redemption.test.ts +4 -4
  82. package/src/__tests__/injector-v3-suppression.test.ts +27 -20
  83. package/src/__tests__/internal-telemetry-routes.test.ts +6 -14
  84. package/src/__tests__/invite-redemption-service.test.ts +4 -7
  85. package/src/__tests__/llm-callsite-catalog.test.ts +5 -6
  86. package/src/__tests__/llm-catalog-parity.test.ts +30 -23
  87. package/src/__tests__/llm-resolver.test.ts +70 -24
  88. package/src/__tests__/llm-schema.test.ts +1 -0
  89. package/src/__tests__/managed-profile-guard.test.ts +163 -4
  90. package/src/__tests__/mcp-health-check.test.ts +6 -7
  91. package/src/__tests__/media-stream-server-integration.test.ts +317 -13
  92. package/src/__tests__/oauth-provider-seed-logos.test.ts +4 -6
  93. package/src/__tests__/onboarding-persona-write.test.ts +1 -1
  94. package/src/__tests__/path-policy.test.ts +34 -0
  95. package/src/__tests__/persona-resolver.test.ts +49 -14
  96. package/src/__tests__/plugin-api-model-profiles.test.ts +178 -0
  97. package/src/__tests__/plugin-api-provider.test.ts +24 -0
  98. package/src/__tests__/plugin-tool-contribution.test.ts +6 -3
  99. package/src/__tests__/post-compaction-reinjection-idempotency.test.ts +214 -0
  100. package/src/__tests__/provider-send-message-override-profile.test.ts +76 -0
  101. package/src/__tests__/reaction-persistence.test.ts +150 -29
  102. package/src/__tests__/registry.test.ts +2 -7
  103. package/src/__tests__/relay-server.test.ts +285 -0
  104. package/src/__tests__/runtime-attachment-metadata.test.ts +0 -1
  105. package/src/__tests__/schedule-routes-workflow-validation.test.ts +1 -10
  106. package/src/__tests__/schedule-routes.test.ts +0 -30
  107. package/src/__tests__/schedule-tools.test.ts +2 -18
  108. package/src/__tests__/scheduler-reuse-conversation.test.ts +8 -5
  109. package/src/__tests__/skill-execute-input.test.ts +51 -1
  110. package/src/__tests__/skill-runtime-path.test.ts +2 -3
  111. package/src/__tests__/skills.test.ts +51 -0
  112. package/src/__tests__/slack-notification-approval-card.test.ts +176 -0
  113. package/src/__tests__/slack-reaction-canonical-approval.test.ts +285 -0
  114. package/src/__tests__/subagent-tools.test.ts +266 -0
  115. package/src/__tests__/surface-completion-nudge-hook.test.ts +367 -0
  116. package/src/__tests__/task-progress-nudge-hook.test.ts +1 -1
  117. package/src/__tests__/title-generate-hook.test.ts +100 -3
  118. package/src/__tests__/token-estimator-accuracy.benchmark.test.ts +1 -29
  119. package/src/__tests__/token-manager.test.ts +519 -0
  120. package/src/__tests__/tool-approval-seed-content-blocks.test.ts +1 -1
  121. package/src/__tests__/tool-audit-listener.test.ts +7 -7
  122. package/src/__tests__/tool-executor-lifecycle-events.test.ts +6 -3
  123. package/src/__tests__/tool-executor.test.ts +0 -79
  124. package/src/__tests__/trusted-contact-approval-notifier.test.ts +4 -2
  125. package/src/__tests__/trusted-contact-inline-approval-integration.test.ts +220 -3
  126. package/src/__tests__/trusted-contact-multichannel.test.ts +3 -3
  127. package/src/__tests__/trusted-contact-verification.test.ts +8 -10
  128. package/src/__tests__/twilio-routes.test.ts +81 -1
  129. package/src/__tests__/voice-invite-redemption.test.ts +2 -3
  130. package/src/__tests__/weak-open-model.test.ts +30 -0
  131. package/src/__tests__/web-search-catalog-parity.test.ts +6 -25
  132. package/src/__tests__/workspace-greetings.test.ts +152 -0
  133. package/src/__tests__/workspace-migration-105-enable-memory-v3-live-for-new-workspaces.test.ts +149 -0
  134. package/src/__tests__/workspace-migration-108-drop-balanced-economy-profile.test.ts +285 -0
  135. package/src/__tests__/workspace-migration-add-send-diagnostics.test.ts +1 -1
  136. package/src/__tests__/workspace-migration-drop-collect-usage-data.test.ts +118 -0
  137. package/src/__tests__/workspace-migration-drop-send-diagnostics.test.ts +118 -0
  138. package/src/a2a/__tests__/e2e-a2a-channel.test.ts +0 -4
  139. package/src/agent/loop.ts +49 -29
  140. package/src/api/README.md +6 -6
  141. package/src/api/events/tool-result.ts +6 -0
  142. package/src/api/events/workflow-completed.ts +53 -0
  143. package/src/api/events/workflow-leaf-finished.ts +38 -0
  144. package/src/api/events/workflow-leaf-started.ts +35 -0
  145. package/src/api/events/workflow-progress.ts +32 -0
  146. package/src/api/events/workflow-started.ts +31 -0
  147. package/src/api/index.ts +40 -0
  148. package/src/api/responses/conversation-message.ts +28 -4
  149. package/src/api/responses/home.ts +26 -4
  150. package/src/api/responses/workflow-journal.ts +53 -0
  151. package/src/approvals/guardian-card-withdrawal.ts +145 -0
  152. package/src/approvals/guardian-decision-primitive.ts +26 -3
  153. package/src/approvals/guardian-request-resolvers.ts +183 -80
  154. package/src/calls/__tests__/channel-admission-reader.test.ts +132 -0
  155. package/src/calls/__tests__/relay-setup-router.test.ts +350 -0
  156. package/src/calls/call-pointer-messages.ts +10 -4
  157. package/src/calls/channel-admission-reader.ts +104 -0
  158. package/src/calls/guardian-dispatch.ts +17 -45
  159. package/src/calls/media-stream-server.ts +84 -2
  160. package/src/calls/relay-access-wait.ts +1 -1
  161. package/src/calls/relay-server.ts +66 -0
  162. package/src/calls/relay-setup-router.ts +82 -1
  163. package/src/calls/twilio-routes.ts +17 -8
  164. package/src/calls/voice-session-bridge.ts +2 -2
  165. package/src/cli/commands/clients.ts +3 -0
  166. package/src/cli/commands/{__tests__ → memory/__tests__}/memory-v2-compare-render.test.ts +1 -1
  167. package/src/cli/commands/{__tests__ → memory/__tests__}/memory-v2.test.ts +8 -7
  168. package/src/cli/commands/{__tests__ → memory/__tests__}/memory-v3.test.ts +5 -4
  169. package/src/cli/commands/memory/index.ts +30 -0
  170. package/src/cli/commands/{memory-v2-compare-render.ts → memory/memory-v2-compare-render.ts} +1 -1
  171. package/src/cli/commands/{memory-v2.ts → memory/memory-v2.ts} +6 -15
  172. package/src/cli/commands/{memory-v3.ts → memory/memory-v3.ts} +97 -11
  173. package/src/cli/commands/oauth/status.test.ts +36 -0
  174. package/src/cli/commands/oauth/status.ts +23 -3
  175. package/src/cli/commands/plugins.ts +197 -4
  176. package/src/cli/lib/__tests__/diff-plugin.test.ts +443 -0
  177. package/src/cli/lib/__tests__/inspect-plugin.test.ts +54 -0
  178. package/src/cli/lib/__tests__/merge-plugin-tree.test.ts +443 -0
  179. package/src/cli/lib/__tests__/plugin-surfaces.test.ts +111 -0
  180. package/src/cli/lib/__tests__/upgrade-plugin.test.ts +295 -2
  181. package/src/cli/lib/diff-plugin.ts +346 -0
  182. package/src/cli/lib/inspect-plugin.ts +12 -1
  183. package/src/cli/lib/install-from-github.ts +105 -17
  184. package/src/cli/lib/merge-plugin-tree.ts +328 -0
  185. package/src/cli/lib/plugin-fingerprint.ts +14 -0
  186. package/src/cli/lib/plugin-surfaces.ts +104 -0
  187. package/src/cli/lib/upgrade-plugin.ts +298 -10
  188. package/src/cli/program.ts +2 -6
  189. package/src/config/__tests__/sync-gated-profiles.test.ts +368 -0
  190. package/src/config/assistant-feature-flags.ts +22 -7
  191. package/src/config/bundled-skills/contacts/tools/contact-search.ts +0 -1
  192. package/src/config/bundled-skills/messaging/SKILL.md +6 -4
  193. package/src/config/bundled-skills/messaging/tools/messaging-archive-by-sender.ts +9 -8
  194. package/src/config/bundled-skills/subagent/SKILL.md +4 -0
  195. package/src/config/bundled-skills/subagent/TOOLS.json +4 -0
  196. package/src/config/bundled-skills/workflows/SKILL.md +14 -8
  197. package/src/config/bundled-tool-registry.ts +2 -7
  198. package/src/config/call-site-defaults.ts +15 -2
  199. package/src/config/feature-flag-registry.json +46 -31
  200. package/src/config/inference-profile-validation.ts +26 -0
  201. package/src/config/llm-resolver.ts +3 -0
  202. package/src/config/loader.ts +4 -0
  203. package/src/config/memory-v3-gate.ts +11 -0
  204. package/src/config/profile-order.ts +28 -0
  205. package/src/config/schema.ts +8 -6
  206. package/src/config/schemas/__tests__/memory-v3.test.ts +1 -0
  207. package/src/config/schemas/call-site-catalog.ts +7 -0
  208. package/src/config/schemas/channels.ts +11 -0
  209. package/src/config/schemas/elevenlabs.ts +0 -1
  210. package/src/config/schemas/llm.ts +31 -0
  211. package/src/config/schemas/memory-lifecycle.ts +3 -7
  212. package/src/config/schemas/memory-v3.ts +6 -0
  213. package/src/config/schemas/platform.ts +0 -8
  214. package/src/config/schemas/services.ts +18 -0
  215. package/src/config/seed-inference-profiles.ts +109 -44
  216. package/src/config/skills.ts +21 -0
  217. package/src/config/sync-gated-profiles.ts +220 -0
  218. package/src/contacts/contact-store.ts +89 -106
  219. package/src/contacts/contacts-write.ts +5 -22
  220. package/src/contacts/types.ts +0 -1
  221. package/src/context/compactor.ts +88 -54
  222. package/src/context/strip-injections.ts +58 -10
  223. package/src/context/token-estimator.ts +1 -1
  224. package/src/credential-execution/process-manager.ts +55 -14
  225. package/src/credential-execution/prompted-credential.ts +2 -3
  226. package/src/daemon/__tests__/conversation-lifecycle-auto-analyze.test.ts +3 -2
  227. package/src/daemon/config-watcher.ts +0 -4
  228. package/src/daemon/conversation-agent-loop-handlers.ts +2 -0
  229. package/src/daemon/conversation-agent-loop.ts +114 -22
  230. package/src/daemon/conversation-history.ts +1 -1
  231. package/src/daemon/conversation-lifecycle.ts +3 -5
  232. package/src/daemon/conversation-process.ts +13 -5
  233. package/src/daemon/conversation-runtime-assembly.ts +13 -15
  234. package/src/daemon/conversation-slash.ts +2 -23
  235. package/src/daemon/conversation-surfaces.ts +26 -0
  236. package/src/daemon/conversation-tool-setup.ts +27 -14
  237. package/src/daemon/conversation.ts +66 -14
  238. package/src/daemon/disk-pressure-policy.ts +5 -3
  239. package/src/daemon/handlers/__tests__/config-a2a-complete.test.ts +0 -1
  240. package/src/daemon/handlers/__tests__/config-a2a-redeem.test.ts +0 -1
  241. package/src/daemon/handlers/config-a2a.ts +0 -2
  242. package/src/daemon/handlers/config-channels.ts +15 -16
  243. package/src/daemon/handlers/config-slack-channel.ts +22 -3
  244. package/src/daemon/handlers/conversations.ts +107 -0
  245. package/src/daemon/host-browser-proxy.ts +41 -0
  246. package/src/daemon/lifecycle.ts +55 -27
  247. package/src/daemon/message-provenance.ts +2 -0
  248. package/src/daemon/message-types/contacts.ts +0 -1
  249. package/src/daemon/message-types/conversations.ts +3 -3
  250. package/src/daemon/message-types/sync.ts +0 -1
  251. package/src/daemon/message-types/web-activity.ts +7 -1
  252. package/src/daemon/message-types/workflows.ts +83 -1
  253. package/src/daemon/orphan-reaper.test.ts +0 -19
  254. package/src/daemon/orphan-reaper.ts +2 -24
  255. package/src/daemon/server.ts +0 -10
  256. package/src/daemon/tool-setup-types.ts +4 -0
  257. package/src/daemon/trust-context.ts +1 -1
  258. package/src/events/tool-audit-listener.ts +2 -2
  259. package/src/home/feed-source-enrichment.test.ts +151 -0
  260. package/src/home/feed-source-enrichment.ts +176 -0
  261. package/src/home/relationship-state.ts +2 -4
  262. package/src/instrument.ts +18 -6
  263. package/src/ipc/__tests__/binary-result-ipc.test.ts +81 -0
  264. package/src/ipc/__tests__/clients-list-ipc.test.ts +20 -0
  265. package/src/ipc/assistant-server.ts +37 -4
  266. package/src/ipc/gateway-flag-listener.ts +18 -2
  267. package/src/memory/__tests__/auto-analysis-enqueue.test.ts +5 -16
  268. package/src/memory/__tests__/jobs-store-enqueue-gate.test.ts +7 -11
  269. package/src/memory/__tests__/memory-retrospective-enqueue.test.ts +37 -7
  270. package/src/memory/__tests__/memory-retrospective-job.test.ts +229 -401
  271. package/src/memory/__tests__/onboarding-events-store.test.ts +7 -7
  272. package/src/memory/auth-fallback-events-store.ts +2 -2
  273. package/src/memory/auto-analysis-enqueue.ts +3 -5
  274. package/src/memory/bookmark-crud.ts +1 -2
  275. package/src/memory/canonical-guardian-store.ts +39 -1
  276. package/src/memory/conversation-crud.ts +9 -4
  277. package/src/memory/conversation-key-store.ts +17 -2
  278. package/src/memory/conversation-title-service.ts +64 -7
  279. package/src/memory/db-init.ts +17 -17
  280. package/src/memory/embedding-backend.ts +38 -1
  281. package/src/memory/embedding-billing-breaker.ts +96 -0
  282. package/src/memory/jobs-store.ts +25 -13
  283. package/src/memory/jobs-worker.ts +54 -1
  284. package/src/memory/lifecycle-events-store.ts +2 -2
  285. package/src/memory/memory-retrospective-constants.ts +4 -4
  286. package/src/memory/memory-retrospective-enqueue.ts +31 -6
  287. package/src/memory/memory-retrospective-job.ts +28 -227
  288. package/src/memory/migrations/129-contact-channels-access-fields.ts +18 -9
  289. package/src/memory/migrations/131-drop-legacy-member-guardian-tables.ts +14 -2
  290. package/src/memory/migrations/289-contact-channels-unique-ext-user.ts +10 -0
  291. package/src/memory/migrations/291-contact-channels-renormalize-addresses.ts +72 -0
  292. package/src/memory/migrations/292-schedule-default-no-reuse-conversation.test.ts +67 -0
  293. package/src/memory/migrations/292-schedule-default-no-reuse-conversation.ts +25 -0
  294. package/src/memory/migrations/293-workflow-journal-leaf-tokens.ts +32 -0
  295. package/src/memory/migrations/294-drop-external-user-id.ts +31 -0
  296. package/src/memory/migrations/295-drop-approval-prompt-ts-tracker.ts +20 -0
  297. package/src/memory/migrations/296-rewrite-balanced-economy-profile-pins.test.ts +110 -0
  298. package/src/memory/migrations/296-rewrite-balanced-economy-profile-pins.ts +68 -0
  299. package/src/memory/migrations/__tests__/131-drop-legacy-member-guardian-tables.test.ts +154 -0
  300. package/src/memory/migrations/__tests__/289-contact-channels-unique-ext-user.test.ts +31 -0
  301. package/src/memory/migrations/__tests__/291-contact-channels-renormalize-addresses.test.ts +341 -0
  302. package/src/memory/migrations/__tests__/run-migrations.test.ts +52 -0
  303. package/src/memory/migrations/index.ts +6 -0
  304. package/src/memory/migrations/run-migrations.ts +41 -0
  305. package/src/memory/migrations/validate-migration-state.ts +1 -1
  306. package/src/memory/onboarding-events-store.ts +3 -3
  307. package/src/memory/schema/contacts.ts +0 -5
  308. package/src/memory/skill-loaded-events-store.test.ts +7 -15
  309. package/src/memory/skill-loaded-events-store.ts +2 -2
  310. package/src/memory/tool-executed-events-store.test.ts +7 -7
  311. package/src/memory/turn-trace-store.test.ts +736 -0
  312. package/src/memory/turn-trace-store.ts +364 -0
  313. package/src/memory/v2/__tests__/consolidation-job.test.ts +8 -0
  314. package/src/memory/v2/__tests__/skill-content.test.ts +30 -0
  315. package/src/memory/v2/consolidation-job.ts +2 -2
  316. package/src/memory/v2/skill-content.ts +25 -7
  317. package/src/memory/v2/skill-store.ts +7 -1
  318. package/src/memory/v3-eval/__tests__/eval-packets.test.ts +248 -0
  319. package/src/memory/v3-eval/eval-packets.ts +546 -0
  320. package/src/messaging/providers/slack/adapter.ts +1 -1
  321. package/src/messaging/providers/slack/api.ts +31 -0
  322. package/src/messaging/providers/slack/send.test.ts +114 -2
  323. package/src/messaging/providers/slack/send.ts +30 -7
  324. package/src/messaging/providers/slack/withdraw.test.ts +200 -0
  325. package/src/messaging/providers/slack/withdraw.ts +161 -0
  326. package/src/notifications/AGENTS.md +2 -0
  327. package/src/notifications/access-request-copy.ts +72 -59
  328. package/src/notifications/adapters/shared.ts +29 -0
  329. package/src/notifications/adapters/slack.ts +58 -103
  330. package/src/notifications/adapters/telegram.ts +2 -20
  331. package/src/notifications/approval-card-data.ts +333 -0
  332. package/src/notifications/broadcaster.ts +16 -3
  333. package/src/notifications/canonical-delivery-recorder.ts +139 -0
  334. package/src/notifications/copy-composer.ts +3 -3
  335. package/src/notifications/decision-engine.ts +4 -2
  336. package/src/notifications/destination-resolver.ts +4 -6
  337. package/src/notifications/guardian-question-mode.ts +10 -0
  338. package/src/notifications/home-feed-side-effect.ts +7 -16
  339. package/src/notifications/notification-utils.ts +19 -20
  340. package/src/notifications/signal.ts +79 -43
  341. package/src/notifications/types.ts +98 -121
  342. package/src/oauth/AGENTS.md +5 -24
  343. package/src/permissions/checker.test.ts +51 -0
  344. package/src/permissions/checker.ts +185 -26
  345. package/src/permissions/ipc-risk-types.ts +24 -0
  346. package/src/permissions/question-prompter.test.ts +27 -0
  347. package/src/permissions/question-prompter.ts +4 -0
  348. package/src/platform/client.test.ts +119 -0
  349. package/src/platform/client.ts +66 -0
  350. package/src/platform/consent-cache.test.ts +267 -0
  351. package/src/platform/consent-cache.ts +174 -0
  352. package/src/plugin-api/constants.ts +1 -1
  353. package/src/plugin-api/index.ts +33 -1
  354. package/src/plugin-api/model-profiles.ts +33 -0
  355. package/src/plugin-api/types.ts +50 -2
  356. package/src/plugins/defaults/advisor/__tests__/advisor-gate.test.ts +56 -0
  357. package/src/plugins/defaults/advisor/__tests__/advisor-state-store.test.ts +43 -0
  358. package/src/plugins/defaults/advisor/__tests__/agent-loop-integration.test.ts +137 -0
  359. package/src/plugins/defaults/advisor/__tests__/consult.test.ts +153 -0
  360. package/src/plugins/defaults/advisor/__tests__/hooks.test.ts +138 -0
  361. package/src/plugins/defaults/advisor/__tests__/transcript.test.ts +147 -0
  362. package/src/plugins/defaults/advisor/advisor-gate.ts +29 -0
  363. package/src/plugins/defaults/advisor/advisor-state-store.ts +94 -0
  364. package/src/plugins/defaults/advisor/config.ts +21 -0
  365. package/src/plugins/defaults/advisor/consult.ts +93 -0
  366. package/src/plugins/defaults/advisor/hooks/post-model-call.ts +34 -0
  367. package/src/plugins/defaults/advisor/hooks/pre-model-call.ts +30 -0
  368. package/src/plugins/defaults/advisor/hooks/user-prompt-submit.ts +19 -0
  369. package/src/plugins/defaults/advisor/package.json +14 -0
  370. package/src/plugins/defaults/advisor/steering.ts +67 -0
  371. package/src/plugins/defaults/advisor/tools/advisor.ts +65 -0
  372. package/src/plugins/defaults/advisor/transcript.ts +76 -0
  373. package/src/plugins/defaults/index.ts +60 -0
  374. package/src/plugins/defaults/memory-retrieval/hooks/post-compact.ts +22 -9
  375. package/src/plugins/defaults/memory-retrieval/hooks/user-prompt-submit.ts +2 -2
  376. package/src/plugins/defaults/memory-retrieval/tail-reinjection-strip.ts +64 -0
  377. package/src/plugins/defaults/memory-retrieval/unified-turn-context.ts +29 -21
  378. package/src/plugins/defaults/memory-v3-shadow/__tests__/carry-integration.test.ts +1 -0
  379. package/src/plugins/defaults/memory-v3-shadow/__tests__/injection.test.ts +1 -0
  380. package/src/plugins/defaults/memory-v3-shadow/__tests__/maintain-job.test.ts +129 -9
  381. package/src/plugins/defaults/memory-v3-shadow/__tests__/orchestrate.test.ts +31 -4
  382. package/src/plugins/defaults/memory-v3-shadow/__tests__/selection-log-store.test.ts +77 -2
  383. package/src/plugins/defaults/memory-v3-shadow/__tests__/shadow-plugin.test.ts +1 -0
  384. package/src/plugins/defaults/memory-v3-shadow/injector.ts +7 -10
  385. package/src/plugins/defaults/memory-v3-shadow/maintain-job.ts +144 -11
  386. package/src/plugins/defaults/memory-v3-shadow/orchestrate.ts +32 -20
  387. package/src/plugins/defaults/memory-v3-shadow/selection-log-store.ts +56 -3
  388. package/src/plugins/defaults/memory-v3-shadow/shadow-plugin.ts +23 -2
  389. package/src/plugins/defaults/surface-completion-nudge/hooks/post-model-call.ts +276 -0
  390. package/src/plugins/defaults/surface-completion-nudge/hooks/stop.ts +22 -0
  391. package/src/plugins/defaults/surface-completion-nudge/nudge-state-store.ts +46 -0
  392. package/src/plugins/defaults/surface-completion-nudge/package.json +14 -0
  393. package/src/plugins/defaults/task-progress-nudge/hooks/post-tool-use.ts +3 -13
  394. package/src/plugins/defaults/title-generate/hooks/stop.ts +56 -21
  395. package/src/prompts/persona-resolver.ts +14 -4
  396. package/src/prompts/templates/system-sections.ts +7 -2
  397. package/src/providers/__tests__/provider-env-vars.test.ts +6 -0
  398. package/src/providers/__tests__/provider-secret-catalog.test.ts +1 -0
  399. package/src/providers/__tests__/retry-callsite.test.ts +176 -0
  400. package/src/providers/atlascloud/client.ts +85 -0
  401. package/src/providers/fetch-provider-catalog.ts +85 -0
  402. package/src/providers/inference/adapter-factory.ts +3 -0
  403. package/src/providers/model-catalog.ts +58 -0
  404. package/src/providers/openai/__tests__/chat-completions-provider-reasoning.test.ts +33 -0
  405. package/src/providers/openai/chat-completions-provider.ts +7 -0
  406. package/src/providers/openai/responses-provider.ts +10 -0
  407. package/src/providers/provider-send-message.ts +11 -3
  408. package/src/providers/retry.ts +53 -12
  409. package/src/providers/search-provider-catalog.ts +10 -0
  410. package/src/providers/weak-open-model.ts +22 -0
  411. package/src/runtime/AGENTS.md +0 -1
  412. package/src/runtime/__tests__/agent-wake.test.ts +181 -0
  413. package/src/runtime/__tests__/client-health.test.ts +44 -0
  414. package/src/runtime/access-request-helper.ts +21 -53
  415. package/src/runtime/actor-trust-resolver.ts +59 -63
  416. package/src/runtime/agent-wake.ts +52 -0
  417. package/src/runtime/assistant-event-hub.ts +18 -4
  418. package/src/runtime/auth/__tests__/route-policy.test.ts +12 -0
  419. package/src/runtime/auth/require-bound-guardian.ts +1 -4
  420. package/src/runtime/btw-sidechain.ts +3 -6
  421. package/src/runtime/capabilities.test.ts +120 -0
  422. package/src/runtime/capabilities.ts +197 -0
  423. package/src/runtime/channel-approval-types.ts +22 -45
  424. package/src/runtime/channel-invite-transports/telegram.ts +4 -4
  425. package/src/runtime/channel-retry-sweep.ts +1 -0
  426. package/src/runtime/channel-verification-service.ts +3 -3
  427. package/src/runtime/client-health.ts +26 -0
  428. package/src/runtime/confirmation-request-guardian-bridge.ts +38 -29
  429. package/src/runtime/effective-capabilities.test.ts +128 -0
  430. package/src/runtime/effective-capabilities.ts +84 -0
  431. package/src/runtime/guardian-reply-router.ts +106 -21
  432. package/src/runtime/invite-redemption-service.ts +9 -25
  433. package/src/runtime/migrations/__tests__/vbundle-builder-fd-leak.test.ts +123 -0
  434. package/src/runtime/migrations/vbundle-builder.ts +49 -20
  435. package/src/runtime/pending-interactions.ts +15 -0
  436. package/src/runtime/routes/__tests__/client-routes.test.ts +13 -0
  437. package/src/runtime/routes/__tests__/conversation-management-routes.test.ts +67 -0
  438. package/src/runtime/routes/__tests__/plugins-routes.test.ts +240 -1
  439. package/src/runtime/routes/app-routes.ts +1 -1
  440. package/src/runtime/routes/approval-strategies/guardian-callback-strategy.ts +2 -2
  441. package/src/runtime/routes/assets/vellum-design-system.css +1959 -0
  442. package/src/runtime/routes/browser-tabs-routes.ts +9 -0
  443. package/src/runtime/routes/btw-routes.ts +1 -27
  444. package/src/runtime/routes/canonical-guardian-expiry-sweep.ts +17 -8
  445. package/src/runtime/routes/client-routes.ts +10 -0
  446. package/src/runtime/routes/contact-routes.ts +31 -8
  447. package/src/runtime/routes/conversation-compaction-routes.ts +1 -1
  448. package/src/runtime/routes/conversation-management-routes.ts +80 -1
  449. package/src/runtime/routes/conversation-query-routes.ts +68 -22
  450. package/src/runtime/routes/conversation-routes.ts +39 -14
  451. package/src/runtime/routes/credential-routes.ts +40 -16
  452. package/src/runtime/routes/empty-state-greeting-cache.ts +1 -2
  453. package/src/runtime/routes/events-routes.ts +1 -3
  454. package/src/runtime/routes/guardian-approval-interception.ts +14 -73
  455. package/src/runtime/routes/guardian-approval-prompt.ts +22 -4
  456. package/src/runtime/routes/home-feed-routes.ts +8 -3
  457. package/src/runtime/routes/identity-routes.ts +1 -296
  458. package/src/runtime/routes/inbound-message-handler.ts +214 -228
  459. package/src/runtime/routes/inbound-stages/acl-enforcement.ts +89 -7
  460. package/src/runtime/routes/inbound-stages/admission-policy.test.ts +154 -0
  461. package/src/runtime/routes/inbound-stages/admission-policy.ts +140 -0
  462. package/src/runtime/routes/inbound-stages/background-dispatch.test.ts +3 -3
  463. package/src/runtime/routes/inbound-stages/background-dispatch.ts +11 -6
  464. package/src/runtime/routes/inbound-stages/escalation-intercept.ts +1 -2
  465. package/src/runtime/routes/inbound-stages/guardian-activation-intercept.ts +1 -2
  466. package/src/runtime/routes/inbound-stages/guardian-reply-intercept.test.ts +7 -7
  467. package/src/runtime/routes/inbound-stages/guardian-reply-intercept.ts +47 -28
  468. package/src/runtime/routes/inbound-stages/reaction-intercept.ts +358 -0
  469. package/src/runtime/routes/index.ts +2 -0
  470. package/src/runtime/routes/integrations/slack/__tests__/channel.test.ts +8 -0
  471. package/src/runtime/routes/integrations/slack/channel.ts +36 -0
  472. package/src/runtime/routes/internal-telemetry-routes.ts +1 -1
  473. package/src/runtime/routes/mcp-auth-routes.ts +233 -41
  474. package/src/runtime/routes/memory-eval-routes.ts +87 -0
  475. package/src/runtime/routes/notification-routes.ts +122 -133
  476. package/src/runtime/routes/platform-routes.ts +2 -2
  477. package/src/runtime/routes/plugins-routes.ts +202 -3
  478. package/src/runtime/routes/schedule-routes.ts +0 -22
  479. package/src/runtime/routes/secret-routes.ts +10 -0
  480. package/src/runtime/routes/surface-action-routes.ts +2 -1
  481. package/src/runtime/routes/tool-call-question-enrichment.test.ts +146 -0
  482. package/src/runtime/routes/tool-call-question-enrichment.ts +66 -0
  483. package/src/runtime/routes/workflow-routes.test.ts +229 -44
  484. package/src/runtime/routes/workflow-routes.ts +131 -29
  485. package/src/runtime/routes/workspace-greetings.ts +55 -0
  486. package/src/runtime/sync/resource-sync-events.ts +1 -11
  487. package/src/runtime/tool-grant-request-helper.ts +18 -16
  488. package/src/runtime/trust-context-resolver.ts +8 -5
  489. package/src/schedule/inference-profile.ts +2 -14
  490. package/src/schedule/schedule-store.ts +1 -1
  491. package/src/schedule/scheduler-types.ts +5 -1
  492. package/src/security/__tests__/provider-key-env-fallback.test.ts +6 -0
  493. package/src/security/secret-patterns.ts +3 -0
  494. package/src/subagent/manager.ts +17 -4
  495. package/src/subagent/types.ts +6 -0
  496. package/src/telemetry/trace-collection-policy.test.ts +28 -0
  497. package/src/telemetry/trace-collection-policy.ts +30 -0
  498. package/src/telemetry/types.ts +89 -0
  499. package/src/telemetry/usage-telemetry-reporter.test.ts +586 -36
  500. package/src/telemetry/usage-telemetry-reporter.ts +148 -41
  501. package/src/tools/AGENTS.md +3 -3
  502. package/src/tools/browser/__tests__/browser-execution-acquire.test.ts +31 -0
  503. package/src/tools/browser/browser-execution.ts +30 -19
  504. package/src/tools/document/document-tool.ts +2 -3
  505. package/src/tools/executor.ts +5 -3
  506. package/src/tools/host-terminal/host-shell.ts +5 -4
  507. package/src/tools/memory/register.ts +2 -2
  508. package/src/tools/network/__tests__/web-fetch-firecrawl.test.ts +360 -0
  509. package/src/tools/network/__tests__/web-search.test.ts +143 -0
  510. package/src/tools/network/web-fetch.ts +372 -1
  511. package/src/tools/network/web-search-error.ts +1 -1
  512. package/src/tools/network/web-search.ts +213 -10
  513. package/src/tools/permission-checker.ts +4 -3
  514. package/src/tools/registry.ts +20 -0
  515. package/src/tools/schedule/create.ts +7 -12
  516. package/src/tools/schedule/update.ts +4 -11
  517. package/src/tools/shared/filesystem/path-policy.ts +39 -13
  518. package/src/tools/side-effects.ts +2 -17
  519. package/src/tools/skills/execute.ts +33 -0
  520. package/src/tools/subagent/spawn.ts +61 -12
  521. package/src/tools/terminal/shell.ts +10 -4
  522. package/src/tools/tool-approval-handler.ts +18 -13
  523. package/src/tools/tool-manifest.ts +0 -2
  524. package/src/tools/types.ts +9 -0
  525. package/src/tools/ui-surface/definitions.ts +64 -3
  526. package/src/tools/verification-control-plane-policy.ts +3 -1
  527. package/src/tools/workflows/run-workflow.test.ts +8 -18
  528. package/src/tools/workflows/run-workflow.ts +1 -0
  529. package/src/util/disk-usage.ts +78 -23
  530. package/src/util/platform.ts +10 -3
  531. package/src/watcher/telemetry.ts +2 -2
  532. package/src/workflows/capabilities.ts +2 -3
  533. package/src/workflows/engine.test.ts +175 -1
  534. package/src/workflows/engine.ts +82 -0
  535. package/src/workflows/journal-store.test.ts +70 -0
  536. package/src/workflows/journal-store.ts +18 -3
  537. package/src/workflows/run-manager.test.ts +171 -28
  538. package/src/workflows/run-manager.ts +66 -24
  539. package/src/workspace/migrations/105-enable-memory-v3-live-for-new-workspaces.ts +63 -0
  540. package/src/workspace/migrations/106-drop-collect-usage-data.ts +47 -0
  541. package/src/workspace/migrations/107-drop-send-diagnostics.ts +47 -0
  542. package/src/workspace/migrations/108-drop-balanced-economy-profile.ts +129 -0
  543. package/src/workspace/migrations/registry.ts +8 -0
  544. package/src/__tests__/app-control-no-global-cgevent.test.ts +0 -98
  545. package/src/__tests__/credential-security-e2e.test.ts +0 -362
  546. package/src/__tests__/credential-vault-unit.test.ts +0 -1528
  547. package/src/__tests__/credential-vault.test.ts +0 -1706
  548. package/src/__tests__/identity-intro-cache.test.ts +0 -315
  549. package/src/__tests__/secret-onetime-send.test.ts +0 -182
  550. package/src/cli/commands/__tests__/task.test.ts +0 -914
  551. package/src/cli/commands/task.ts +0 -771
  552. package/src/config/bundled-skills/personal-page/SKILL.md +0 -57
  553. package/src/config/bundled-skills/personal-page/TOOLS.json +0 -27
  554. package/src/config/bundled-skills/personal-page/tools/app-refresh.ts +0 -17
  555. package/src/config/preloaded-apps/personal-page/src/components/About.tsx +0 -22
  556. package/src/config/preloaded-apps/personal-page/src/components/App.tsx +0 -16
  557. package/src/config/preloaded-apps/personal-page/src/components/Features.tsx +0 -77
  558. package/src/config/preloaded-apps/personal-page/src/components/Hero.tsx +0 -57
  559. package/src/config/preloaded-apps/personal-page/src/components/Pending.tsx +0 -28
  560. package/src/config/preloaded-apps/personal-page/src/components/animations.tsx +0 -234
  561. package/src/config/preloaded-apps/personal-page/src/components/icons.tsx +0 -48
  562. package/src/config/preloaded-apps/personal-page/src/components/media.ts +0 -16
  563. package/src/config/preloaded-apps/personal-page/src/index.html +0 -20
  564. package/src/config/preloaded-apps/personal-page/src/main.tsx +0 -7
  565. package/src/config/preloaded-apps/personal-page/src/profile-data.ts +0 -82
  566. package/src/config/preloaded-apps/personal-page/src/styles.css +0 -759
  567. package/src/memory/__tests__/preloaded-apps.test.ts +0 -85
  568. package/src/memory/preloaded-apps.ts +0 -116
  569. package/src/notifications/tool-approval-copy.ts +0 -142
  570. package/src/runtime/routes/approval-prompt-ts-tracker.ts +0 -78
  571. package/src/runtime/routes/identity-intro-cache.ts +0 -172
  572. package/src/tools/credentials/vault.ts +0 -712
@@ -27,7 +27,10 @@ type ManagedProfileTemplate = Omit<
27
27
  ProfileEntry,
28
28
  "provider" | "model" | "provider_connection"
29
29
  > & {
30
- intent: ModelIntent;
30
+ // Exactly one of `intent` or `model` must be set. `intent` resolves the
31
+ // model from the catalog at seed time; `model` pins an explicit model id.
32
+ intent?: ModelIntent;
33
+ model?: string;
31
34
  provider: NonNullable<ProfileEntry["provider"]>;
32
35
  connectionName: string;
33
36
  };
@@ -38,17 +41,20 @@ type ManagedProfileTemplate = Omit<
38
41
  * (`preserveProfileNames`) take precedence when present.
39
42
  */
40
43
  const MANAGED_PROFILE_TEMPLATES: Record<string, ManagedProfileTemplate> = {
44
+ // Served by MiniMax M3 on Fireworks via managed platform inference: a strong
45
+ // open model at a lower price point than the managed Anthropic route.
41
46
  balanced: {
42
47
  intent: "balanced",
43
- provider: "anthropic",
44
- connectionName: "anthropic-managed",
48
+ provider: "fireworks",
49
+ connectionName: "fireworks-managed",
45
50
  source: "managed",
46
51
  label: "Balanced",
47
52
  description: "Good balance of quality, cost, and speed",
48
- maxTokens: 16000,
53
+ maxTokens: 32000,
49
54
  effort: "high",
50
55
  thinking: { enabled: true, streamThinking: true },
51
56
  contextWindow: { maxInputTokens: DEFAULT_CONTEXT_WINDOW_MAX_INPUT_TOKENS },
57
+ topP: 0.95,
52
58
  },
53
59
  "quality-optimized": {
54
60
  intent: "quality-optimized",
@@ -61,6 +67,9 @@ const MANAGED_PROFILE_TEMPLATES: Record<string, ManagedProfileTemplate> = {
61
67
  effort: "high",
62
68
  thinking: { enabled: true, streamThinking: true },
63
69
  contextWindow: { maxInputTokens: DEFAULT_CONTEXT_WINDOW_MAX_INPUT_TOKENS },
70
+ // This is the advisor's own (strongest) profile: when it's also the chat
71
+ // profile there's nothing stronger to consult, so the advisor defaults off.
72
+ advisorEnabled: false,
64
73
  },
65
74
  "cost-optimized": {
66
75
  intent: "latency-optimized",
@@ -74,20 +83,6 @@ const MANAGED_PROFILE_TEMPLATES: Record<string, ManagedProfileTemplate> = {
74
83
  thinking: { enabled: false, streamThinking: false },
75
84
  contextWindow: { maxInputTokens: DEFAULT_CONTEXT_WINDOW_MAX_INPUT_TOKENS },
76
85
  },
77
- // Open-weight economy option: MiniMax M3 served by Fireworks via managed
78
- // platform inference.
79
- "balanced-economy": {
80
- intent: "balanced",
81
- provider: "fireworks",
82
- connectionName: "fireworks-managed",
83
- source: "managed",
84
- label: "Balanced Economy",
85
- description: "Strong open model (MiniMax M3) at a lower price point",
86
- maxTokens: 32000,
87
- effort: "high",
88
- thinking: { enabled: true, streamThinking: true },
89
- contextWindow: { maxInputTokens: DEFAULT_CONTEXT_WINDOW_MAX_INPUT_TOKENS },
90
- },
91
86
  };
92
87
 
93
88
  /**
@@ -144,8 +139,37 @@ const USER_PROFILE_TEMPLATES: Record<string, ManagedProfileTemplate> = {
144
139
  */
145
140
  export const AUTO_PROFILE_KEY = "auto";
146
141
 
142
+ export const OS_BETA_PROFILE_KEY = "os-beta";
143
+ export const OS_BETA_FEATURE_FLAG_KEY = "os-beta";
144
+
145
+ /**
146
+ * Flag-gated managed profile. NOT in MANAGED_PROFILE_TEMPLATES, so the
147
+ * unconditional boot seed never creates it. Reconciled in/out by
148
+ * the flag-gated profile reconcile based on the `os-beta` feature flag.
149
+ * Balanced-parity defaults; GLM 5.2 pinned explicitly via `model`.
150
+ */
151
+ export const OS_BETA_PROFILE_TEMPLATE: ManagedProfileTemplate = {
152
+ model: "accounts/fireworks/models/glm-5p2",
153
+ provider: "fireworks",
154
+ connectionName: "fireworks-managed",
155
+ source: "managed",
156
+ label: "OS Beta",
157
+ description: "Open-source frontier model (GLM 5.2), in beta",
158
+ maxTokens: 32000,
159
+ effort: "high",
160
+ thinking: { enabled: true, streamThinking: true },
161
+ contextWindow: { maxInputTokens: DEFAULT_CONTEXT_WINDOW_MAX_INPUT_TOKENS },
162
+ };
163
+
164
+ // Membership here marks a name as managed. The route layer applies managed
165
+ // restrictions (blocking model/provider edits and deletion) only to entries
166
+ // whose on-disk `source` is `managed`, so a user-owned profile sharing one of
167
+ // these names is not locked. `OS_BETA_PROFILE_KEY` is flag-gated: it is
168
+ // materialized by the flag-gated profile reconcile, which refuses to touch a
169
+ // same-named user profile.
147
170
  export const MANAGED_PROFILE_NAMES = new Set([
148
171
  ...Object.keys(MANAGED_PROFILE_TEMPLATES),
172
+ OS_BETA_PROFILE_KEY,
149
173
  AUTO_PROFILE_KEY,
150
174
  ]);
151
175
 
@@ -167,10 +191,17 @@ export type SeedInferenceProfilesOptions = {
167
191
  *
168
192
  * Runs on every daemon startup. Two responsibilities:
169
193
  *
170
- * 1. **Managed profiles** (`balanced`, `quality-optimized`, `cost-optimized`):
171
- * overwritten on every boot so Vellum can push model/config updates to
172
- * customers. Each carries `provider_connection: "anthropic-managed"`.
173
- * Platform overlays (`preserveProfileNames`) take precedence.
194
+ * 1. **Managed profiles** (`balanced`, `quality-optimized`,
195
+ * `cost-optimized`): reconciled from the code templates on every boot
196
+ * on-platform and off-platform alike — so Vellum can push model/config
197
+ * updates to customers in a release without a workspace migration. The
198
+ * templates own all profile content; `label`, `status`, `advisorEnabled`,
199
+ * and `topP` are user overrides that survive reseeds. Of those, only
200
+ * `label`, `status`, and `topP` are editable through the managed PUT route
201
+ * allowlist; `advisorEnabled` is edited via the generic config path but is
202
+ * still preserved here so it survives reboots.
203
+ * Platform overlays (`preserveProfileNames`) take precedence for the boot
204
+ * they are supplied.
174
205
  *
175
206
  * 2. **User profiles** (`custom-balanced`, `custom-quality-optimized`,
176
207
  * `custom-cost-optimized`): materialized once at hatch time for
@@ -208,24 +239,31 @@ export function seedInferenceProfiles(
208
239
  // subsequent boot.
209
240
  const isByokMode = !isPlatform;
210
241
 
211
- // 1. Managed profiles. Off-platform: overwrite on every boot so Vellum can
212
- // push model/config updates in new releases. On-platform: insert only if
213
- // absent the platform controls profiles through overlays, and the
214
- // overlay fragment is authoritative even when it omits fields the local
215
- // template carries (e.g. an overlay supplying only provider/model/label
216
- // must not get its maxTokens/thinking polluted from the template). The
217
- // legacy migration-052 backfill that seeds label-less Anthropic
218
- // defaults is healed by workspace migration 082
219
- // (`backfill-managed-profile-labels`) rather than the seeder, so
220
- // this skip path stays simple.
242
+ // 1. Managed profiles. Reconciled from the code templates on every boot
243
+ // on-platform and off-platform alike so Vellum can push model/config
244
+ // updates in new releases just by editing `MANAGED_PROFILE_TEMPLATES` /
245
+ // `model-intents.ts` and shipping a release, with no workspace migration.
246
+ // The templates are the single source of truth for profile *content*
247
+ // (model, maxTokens, effort, thinking, description, provider/connection).
248
+ //
249
+ // Platform overlays (`preserveProfileNames`) still take precedence for the
250
+ // boot they are supplied: a profile named in the overlay is skipped here so
251
+ // the overlay fragment lands verbatim and is never polluted by template
252
+ // fields it omits. The overlay is a one-time hatch input (archived after
253
+ // its first merge), so on subsequent boots the templates reconcile content
254
+ // as usual.
221
255
  //
222
- // Two user-editable fields survive the overwrite: `label` (display
223
- // rename) and `status` (active/disabled toggle). The PUT route
224
- // `/v1/config/llm/profiles/:name` lets users patch these on managed
225
- // profiles without duplicating; we have to honor those edits across
226
- // reseeds or they'd silently revert on every boot. Carry by
227
- // key-presence rather than truthiness so an explicit `null` (user
228
- // cleared the label) survives too.
256
+ // A whitelist of user-editable fields survives the reconcile: `label`
257
+ // (display rename), `status` (active/disabled toggle), `advisorEnabled`
258
+ // (per-profile advisor toggle), and `topP` (sampling override) the only
259
+ // fields a user may override. The managed PUT route
260
+ // `/v1/config/llm/profiles/:name` lets users patch `label`, `status`, and
261
+ // `topP` on managed profiles without duplicating (its editable allowlist,
262
+ // `MANAGED_PROFILE_EDITABLE_KEYS`, deliberately excludes `advisorEnabled`,
263
+ // which is set through the generic config path). We honor every one of
264
+ // these overrides across reseeds or they'd silently revert on every boot.
265
+ // Carry by key-presence rather than truthiness so an explicit `null` (user
266
+ // cleared the field) survives too.
229
267
  //
230
268
  // BYOK seed defaults (off-platform only):
231
269
  // • label: " (Managed)" suffix disambiguates managed profile labels
@@ -249,7 +287,6 @@ export function seedInferenceProfiles(
249
287
 
250
288
  for (const [name, template] of Object.entries(MANAGED_PROFILE_TEMPLATES)) {
251
289
  if (preservedProfileNames.has(name)) continue;
252
- if (isPlatform && readObject(profiles[name]) !== null) continue;
253
290
 
254
291
  const previous = readObject(profiles[name]);
255
292
  const effectiveTemplate: ManagedProfileTemplate = isByokMode
@@ -281,6 +318,18 @@ export function seedInferenceProfiles(
281
318
  : previous.label;
282
319
  }
283
320
  if ("status" in previous) next.status = previous.status;
321
+ // The per-profile advisor toggle is a user override — preserve it across
322
+ // reseeds so a user's choice survives reboots (the template value only
323
+ // seeds the initial default, e.g. off for quality-optimized).
324
+ if ("advisorEnabled" in previous) {
325
+ next.advisorEnabled = previous.advisorEnabled;
326
+ }
327
+ // `topP` is user-editable on managed profiles (see the managed-profile
328
+ // editable allowlist on the PUT route) — preserve a user override across
329
+ // reseeds, including an explicit `null` clear, or it would silently revert
330
+ // to the template value on every boot. Carry by key-presence (not
331
+ // truthiness) so `null` survives too.
332
+ if ("topP" in previous) next.topP = previous.topP;
284
333
  }
285
334
  profiles[name] = next as ProfileEntry;
286
335
  }
@@ -364,6 +413,17 @@ export function seedInferenceProfiles(
364
413
  }
365
414
  }
366
415
 
416
+ // Advisor profile: default to the strongest managed profile when unset, so
417
+ // the advisor consults `quality-optimized` out of the box. Guarded on
418
+ // existence so it never names a missing profile (superRefine rejects that);
419
+ // off-platform/BYOK installs can repoint it at one of their own profiles.
420
+ if (
421
+ readString(llm.advisorProfile) === undefined &&
422
+ readObject(profiles["quality-optimized"]) !== null
423
+ ) {
424
+ llm.advisorProfile = "quality-optimized";
425
+ }
426
+
367
427
  // Profile ordering — ensure all seeded profiles appear in the order array.
368
428
  // "auto" is prepended so it appears first in the picker.
369
429
  const profileOrder = Array.isArray(llm.profileOrder)
@@ -405,21 +465,26 @@ export function seedInferenceProfiles(
405
465
  saveRawConfig(config);
406
466
  }
407
467
 
408
- function materializeProfile(
468
+ export function materializeProfile(
409
469
  template: ManagedProfileTemplate,
410
470
  provider: NonNullable<ProfileEntry["provider"]>,
411
471
  connectionName: string,
412
472
  ): ProfileEntry {
413
- const { intent, provider: _p, connectionName: _c, ...rest } = template;
473
+ const { intent, model, provider: _p, connectionName: _c, ...rest } = template;
474
+ const resolvedModel =
475
+ model ?? (intent ? resolveModelIntent(provider, intent) : undefined);
476
+ if (!resolvedModel) {
477
+ throw new Error("ManagedProfileTemplate requires `intent` or `model`");
478
+ }
414
479
  return {
415
480
  ...rest,
416
481
  provider,
417
482
  provider_connection: connectionName,
418
- model: resolveModelIntent(provider, intent),
483
+ model: resolvedModel,
419
484
  };
420
485
  }
421
486
 
422
- function readObject(value: unknown): Record<string, unknown> | null {
487
+ export function readObject(value: unknown): Record<string, unknown> | null {
423
488
  return value !== null && typeof value === "object" && !Array.isArray(value)
424
489
  ? (value as Record<string, unknown>)
425
490
  : null;
@@ -47,6 +47,7 @@ const VellumMetadataSchema = z
47
47
  "activation-hints": z.array(z.string()).optional(),
48
48
  "avoid-when": z.array(z.string()).optional(),
49
49
  category: z.string().optional(),
50
+ "always-candidate": z.boolean().optional(),
50
51
  })
51
52
  .passthrough();
52
53
 
@@ -109,6 +110,13 @@ export interface SkillSummary {
109
110
  avoidWhen?: string[];
110
111
  /** Category slug declared in frontmatter, used as a fallback when the skill is not in the Vellum catalog. */
111
112
  category?: string;
113
+ /**
114
+ * When true, this skill is pinned into the memory-v3 selector's stable-prefix
115
+ * candidate pool every turn (so the selector can choose it even when no
116
+ * retrieval lane surfaces it). For cross-cutting capabilities whose relevance
117
+ * the model must judge, not embedding similarity.
118
+ */
119
+ alwaysCandidate?: boolean;
112
120
  /** Parsed inline command expansion descriptors (`!\`command\``) found in the skill body. */
113
121
  inlineCommandExpansions?: InlineCommandExpansion[];
114
122
  }
@@ -227,6 +235,7 @@ interface ParsedFrontmatter {
227
235
  activationHints?: string[];
228
236
  avoidWhen?: string[];
229
237
  category?: string;
238
+ alwaysCandidate?: boolean;
230
239
  inlineCommandExpansions?: InlineCommandExpansion[];
231
240
  }
232
241
 
@@ -342,6 +351,11 @@ function parseFrontmatter(
342
351
  ? vellum.category.trim()
343
352
  : undefined;
344
353
 
354
+ const alwaysCandidate =
355
+ typeof vellum?.["always-candidate"] === "boolean"
356
+ ? vellum["always-candidate"]
357
+ : undefined;
358
+
345
359
  const strippedBody = stripCommentLines(body);
346
360
 
347
361
  // Parse inline command expansions from the body (after frontmatter/comment stripping)
@@ -366,6 +380,7 @@ function parseFrontmatter(
366
380
  activationHints,
367
381
  avoidWhen,
368
382
  category,
383
+ alwaysCandidate,
369
384
  inlineCommandExpansions,
370
385
  };
371
386
  }
@@ -523,6 +538,7 @@ function readSkillFromDirectory(
523
538
  activationHints: parsed.activationHints,
524
539
  avoidWhen: parsed.avoidWhen,
525
540
  category: parsed.category,
541
+ alwaysCandidate: parsed.alwaysCandidate,
526
542
  inlineCommandExpansions: parsed.inlineCommandExpansions,
527
543
  };
528
544
  } catch (err) {
@@ -576,6 +592,7 @@ function readBundledSkillFromDirectory(
576
592
  activationHints: parsed.activationHints,
577
593
  avoidWhen: parsed.avoidWhen,
578
594
  category: parsed.category,
595
+ alwaysCandidate: parsed.alwaysCandidate,
579
596
  inlineCommandExpansions: parsed.inlineCommandExpansions,
580
597
  };
581
598
  } catch (err) {
@@ -637,6 +654,7 @@ function loadBundledSkills(): SkillSummary[] {
637
654
  activationHints: skill.activationHints,
638
655
  avoidWhen: skill.avoidWhen,
639
656
  category: skill.category,
657
+ alwaysCandidate: skill.alwaysCandidate,
640
658
  inlineCommandExpansions: skill.inlineCommandExpansions,
641
659
  });
642
660
  }
@@ -764,6 +782,7 @@ function skillSummaryFromDefinition(
764
782
  activationHints: skill.activationHints,
765
783
  avoidWhen: skill.avoidWhen,
766
784
  category: skill.category,
785
+ alwaysCandidate: skill.alwaysCandidate,
767
786
  inlineCommandExpansions: skill.inlineCommandExpansions,
768
787
  };
769
788
  }
@@ -818,6 +837,7 @@ export function loadSkillCatalog(
818
837
  activationHints: parsed.activationHints,
819
838
  avoidWhen: parsed.avoidWhen,
820
839
  category: parsed.category,
840
+ alwaysCandidate: parsed.alwaysCandidate,
821
841
  inlineCommandExpansions: parsed.inlineCommandExpansions,
822
842
  });
823
843
  } catch (err) {
@@ -954,6 +974,7 @@ export function loadSkillCatalog(
954
974
  activationHints: parsed.activationHints,
955
975
  avoidWhen: parsed.avoidWhen,
956
976
  category: parsed.category,
977
+ alwaysCandidate: parsed.alwaysCandidate,
957
978
  inlineCommandExpansions: parsed.inlineCommandExpansions,
958
979
  };
959
980
 
@@ -0,0 +1,220 @@
1
+ import { isDeepStrictEqual } from "node:util";
2
+
3
+ import { getLogger } from "../util/logger.js";
4
+ import { isAssistantFeatureFlagEnabled } from "./assistant-feature-flags.js";
5
+ import {
6
+ getConfigReadOnly,
7
+ invalidateConfigCache,
8
+ loadRawConfig,
9
+ saveRawConfig,
10
+ } from "./loader.js";
11
+ import type { ProfileEntry } from "./schemas/llm.js";
12
+ import {
13
+ materializeProfile,
14
+ OS_BETA_FEATURE_FLAG_KEY,
15
+ OS_BETA_PROFILE_KEY,
16
+ OS_BETA_PROFILE_TEMPLATE,
17
+ readObject,
18
+ } from "./seed-inference-profiles.js";
19
+
20
+ const log = getLogger("sync-gated-profiles");
21
+
22
+ /**
23
+ * Reconcile flag-gated managed profiles against the current feature-flag state.
24
+ *
25
+ * `seedInferenceProfiles()` runs synchronously at boot before feature flags are
26
+ * available, so the OS Beta profile (GLM 5.2 / fireworks-managed) is materialized
27
+ * here once flags have loaded. When the `os-beta` flag is on, the managed profile
28
+ * is created (ordered right after `balanced`); when it is off, a previously
29
+ * managed entry is removed with `profileOrder` / `activeProfile` / `advisorProfile`
30
+ * fallbacks. The reconcile is idempotent and never touches a user-owned profile of
31
+ * the same name.
32
+ *
33
+ * Returns whether the on-disk config changed.
34
+ */
35
+ export function reconcileFlagGatedProfiles(): boolean {
36
+ const config = loadRawConfig();
37
+
38
+ if (config.llm == null || typeof config.llm !== "object") {
39
+ config.llm = {};
40
+ }
41
+ const llm = config.llm as Record<string, unknown>;
42
+
43
+ if (llm.profiles == null || typeof llm.profiles !== "object") {
44
+ llm.profiles = {};
45
+ }
46
+ const profiles = llm.profiles as Record<string, Record<string, unknown>>;
47
+
48
+ const profileOrder = Array.isArray(llm.profileOrder)
49
+ ? (llm.profileOrder as string[])
50
+ : [];
51
+ llm.profileOrder = profileOrder;
52
+
53
+ // The resolver reads flag state from the gateway-populated override cache and
54
+ // ignores the config argument; pass the read-only config for signature parity
55
+ // without mutating disk before the reconcile decision is made.
56
+ const enabled = isAssistantFeatureFlagEnabled(
57
+ OS_BETA_FEATURE_FLAG_KEY,
58
+ getConfigReadOnly(),
59
+ );
60
+
61
+ const isPlatform =
62
+ process.env.IS_PLATFORM === "true" || process.env.IS_PLATFORM === "1";
63
+ const isByokMode = !isPlatform;
64
+
65
+ const previous = readObject(profiles[OS_BETA_PROFILE_KEY]);
66
+
67
+ // Never clobber a user-owned profile that happens to be named `os-beta`. The
68
+ // entry is ours to manage only when it is absent or already managed; a
69
+ // user-sourced entry of the same name is left untouched on every path.
70
+ const isOursToManage = previous == null || previous.source === "managed";
71
+ if (!isOursToManage) {
72
+ return false;
73
+ }
74
+
75
+ const changed = enabled
76
+ ? enableProfile(profiles, profileOrder, previous, isByokMode)
77
+ : disableProfile(llm, profiles, profileOrder, previous);
78
+
79
+ if (changed) {
80
+ saveRawConfig(config);
81
+ invalidateConfigCache();
82
+ log.info(
83
+ { profile: OS_BETA_PROFILE_KEY, enabled },
84
+ "Reconciled flag-gated profile",
85
+ );
86
+ }
87
+ return changed;
88
+ }
89
+
90
+ function enableProfile(
91
+ profiles: Record<string, Record<string, unknown>>,
92
+ profileOrder: string[],
93
+ previous: Record<string, unknown> | null,
94
+ isByokMode: boolean,
95
+ ): boolean {
96
+ const effectiveTemplate = isByokMode
97
+ ? {
98
+ ...OS_BETA_PROFILE_TEMPLATE,
99
+ label: `${OS_BETA_PROFILE_TEMPLATE.label} (Managed)`,
100
+ }
101
+ : OS_BETA_PROFILE_TEMPLATE;
102
+ const next = materializeProfile(
103
+ effectiveTemplate,
104
+ OS_BETA_PROFILE_TEMPLATE.provider,
105
+ OS_BETA_PROFILE_TEMPLATE.connectionName,
106
+ ) as Record<string, unknown>;
107
+
108
+ // BYOK installs seed managed profiles disabled: the platform-auth
109
+ // `fireworks-managed` connection backing this profile isn't usable until the
110
+ // user enables it, so a fresh OS Beta entry starts disabled to avoid offering
111
+ // an unusable route. A user's own status override (preserved below) wins on
112
+ // later reconciles.
113
+ if (isByokMode && !previous) {
114
+ next.status = "disabled";
115
+ }
116
+
117
+ if (previous) {
118
+ // The only fields a user may override on a managed profile. Carry `label`
119
+ // by key-presence so an explicit null (user cleared it) survives too.
120
+ if ("label" in previous) next.label = previous.label;
121
+ if ("status" in previous) next.status = previous.status;
122
+ if ("advisorEnabled" in previous) {
123
+ next.advisorEnabled = previous.advisorEnabled;
124
+ }
125
+ }
126
+
127
+ let changed = false;
128
+ if (!previous || !isDeepStrictEqual(previous, next)) {
129
+ profiles[OS_BETA_PROFILE_KEY] = next as ProfileEntry;
130
+ changed = true;
131
+ }
132
+
133
+ if (!profileOrder.includes(OS_BETA_PROFILE_KEY)) {
134
+ const balancedIndex = profileOrder.indexOf("balanced");
135
+ if (balancedIndex >= 0) {
136
+ profileOrder.splice(balancedIndex + 1, 0, OS_BETA_PROFILE_KEY);
137
+ } else {
138
+ profileOrder.push(OS_BETA_PROFILE_KEY);
139
+ }
140
+ changed = true;
141
+ }
142
+
143
+ return changed;
144
+ }
145
+
146
+ // `MixSchema = z.array(MixArmSchema).min(2)` in schemas/llm.ts: mixes require
147
+ // >= 2 arms. A mix that drops below this is invalid and cannot be kept.
148
+ const MIX_MIN_ARMS = 2;
149
+
150
+ function disableProfile(
151
+ llm: Record<string, unknown>,
152
+ profiles: Record<string, Record<string, unknown>>,
153
+ profileOrder: string[],
154
+ previous: Record<string, unknown> | null,
155
+ ): boolean {
156
+ if (!previous) return false;
157
+
158
+ delete profiles[OS_BETA_PROFILE_KEY];
159
+
160
+ // The removal closure: every name here is absent from `profiles` once the
161
+ // closure settles, so the written config can never reference one. A mix that
162
+ // loses arms below the >= 2 minimum is itself invalid, so it joins the set
163
+ // and the loop runs to a fixpoint to resolve any references that cascade.
164
+ const removed = new Set<string>([OS_BETA_PROFILE_KEY]);
165
+ let cascading = true;
166
+ while (cascading) {
167
+ cascading = false;
168
+ for (const [name, profile] of Object.entries(profiles)) {
169
+ if (removed.has(name)) continue;
170
+ if (!Array.isArray(profile.mix)) continue;
171
+ const arms = profile.mix as unknown[];
172
+ const kept = arms.filter((arm) => {
173
+ const armProfile = readObject(arm)?.profile;
174
+ return typeof armProfile !== "string" || !removed.has(armProfile);
175
+ });
176
+ if (kept.length === arms.length) continue;
177
+ if (kept.length >= MIX_MIN_ARMS) {
178
+ profile.mix = kept;
179
+ } else {
180
+ delete profiles[name];
181
+ removed.add(name);
182
+ }
183
+ cascading = true;
184
+ }
185
+ }
186
+
187
+ llm.profileOrder = profileOrder.filter((name) => !removed.has(name));
188
+
189
+ if (typeof llm.activeProfile === "string" && removed.has(llm.activeProfile)) {
190
+ llm.activeProfile = "balanced";
191
+ }
192
+ if (
193
+ typeof llm.advisorProfile === "string" &&
194
+ removed.has(llm.advisorProfile)
195
+ ) {
196
+ if (readObject(profiles["quality-optimized"]) !== null) {
197
+ llm.advisorProfile = "quality-optimized";
198
+ } else {
199
+ delete llm.advisorProfile;
200
+ }
201
+ }
202
+
203
+ // Clear any call-site `profile` reference to a removed profile; other override
204
+ // fields on the entry stay intact (an empty override object is valid).
205
+ const callSites = readObject(llm.callSites);
206
+ if (callSites) {
207
+ for (const entry of Object.values(callSites)) {
208
+ const site = readObject(entry);
209
+ if (
210
+ site &&
211
+ typeof site.profile === "string" &&
212
+ removed.has(site.profile)
213
+ ) {
214
+ delete site.profile;
215
+ }
216
+ }
217
+ }
218
+
219
+ return true;
220
+ }