@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
@@ -41,7 +41,11 @@ function resetTables(): void {
41
41
 
42
42
  async function addImageMessage(
43
43
  conversationId: string,
44
- provenanceTrustClass: "guardian" | "trusted_contact" | "unknown",
44
+ provenanceTrustClass:
45
+ | "guardian"
46
+ | "trusted_contact"
47
+ | "unverified_contact"
48
+ | "unknown",
45
49
  filename: string,
46
50
  ): Promise<void> {
47
51
  const inserted = await addMessage(
@@ -109,4 +113,20 @@ describe("collectImageManifest trust filtering", () => {
109
113
  expect(filenames).toContain("contact.png");
110
114
  expect(filenames).not.toContain("guardian-secret.png");
111
115
  });
116
+
117
+ test("unverified_contact actor manifest excludes guardian images but keeps unverified-provenance images", async () => {
118
+ // GIVEN a conversation with a guardian image and an unverified-contact image
119
+ const conv = createConversation();
120
+ await addImageMessage(conv.id, "guardian", "guardian-secret.png");
121
+ await addImageMessage(conv.id, "unverified_contact", "unverified.png");
122
+
123
+ // WHEN the manifest is built for an unverified_contact actor
124
+ const manifest = collectImageManifest(conv.id, "unverified_contact");
125
+
126
+ // THEN the unverified-provenance image is listed and the guardian image
127
+ // is excluded — unverified_contact is treated as untrusted downstream.
128
+ const filenames = manifest.map((e) => e.filename);
129
+ expect(filenames).toContain("unverified.png");
130
+ expect(filenames).not.toContain("guardian-secret.png");
131
+ });
112
132
  });
@@ -0,0 +1,223 @@
1
+ /**
2
+ * The compaction summary call bounds its own input to the context window.
3
+ *
4
+ * With no tool pair to anchor an emergency split, an overflow recovery routes
5
+ * the full conversation straight into `runAssistantDrivenCompaction`. If that
6
+ * history exceeds the window, the summary call must front-truncate its own
7
+ * request or it overflows in turn and recovery stalls. Below the window the
8
+ * request is sent untouched so its prefix stays byte-aligned with the agent's
9
+ * warm cache (the budget-path cache reuse must not regress).
10
+ */
11
+ import { describe, expect, mock, test } from "bun:test";
12
+
13
+ function makeLoggerStub(): Record<string, unknown> {
14
+ const stub: Record<string, unknown> = {};
15
+ for (const m of [
16
+ "info",
17
+ "warn",
18
+ "error",
19
+ "debug",
20
+ "trace",
21
+ "fatal",
22
+ "silent",
23
+ "child",
24
+ ]) {
25
+ stub[m] = m === "child" ? () => makeLoggerStub() : () => {};
26
+ }
27
+ return stub;
28
+ }
29
+
30
+ mock.module("../util/logger.js", () => ({
31
+ getLogger: () => makeLoggerStub(),
32
+ }));
33
+
34
+ mock.module("../memory/conversation-crud.js", () => ({
35
+ getMessages: () => [],
36
+ }));
37
+
38
+ mock.module("../memory/attachments-store.js", () => ({
39
+ getAttachmentMetadataForMessage: () => [],
40
+ getAttachmentContent: () => null,
41
+ }));
42
+
43
+ mock.module("../memory/llm-request-log-store.js", () => ({
44
+ recordRequestLog: () => {},
45
+ }));
46
+
47
+ import { runAssistantDrivenCompaction } from "../context/compactor.js";
48
+ import { estimatePromptTokens } from "../context/token-estimator.js";
49
+ import type { Message, Provider } from "../providers/types.js";
50
+
51
+ const TRUNCATION_MARKER = "summary covers only the visible portion";
52
+
53
+ function turnTimestamp(turn: number): string {
54
+ const hour = String(10 + Math.floor(turn / 60)).padStart(2, "0");
55
+ const minute = String(turn % 60).padStart(2, "0");
56
+ return `2026-05-21 (Thursday) ${hour}:${minute}:00 -05:00 (America/Chicago)`;
57
+ }
58
+
59
+ function userTurn(turn: number, body: string): Message {
60
+ return {
61
+ role: "user",
62
+ content: [
63
+ {
64
+ type: "text",
65
+ text: `<turn_context>\ncurrent_time: ${turnTimestamp(
66
+ turn,
67
+ )}\n</turn_context>\n[U${turn}] ${body}`,
68
+ },
69
+ ],
70
+ };
71
+ }
72
+
73
+ function assistantTurn(turn: number, body: string): Message {
74
+ return {
75
+ role: "assistant",
76
+ content: [{ type: "text", text: `[A${turn}] ${body}` }],
77
+ };
78
+ }
79
+
80
+ const SUMMARY = "Earlier turns summarized in the assistant's own voice.";
81
+
82
+ function compactionResponse(tailTurn: number, preview: string): string {
83
+ return `<compaction_result>
84
+ <summary>
85
+ ${SUMMARY}
86
+ </summary>
87
+ <key_state>
88
+ - Nothing critical pending.
89
+ </key_state>
90
+ <tail_start timestamp="${turnTimestamp(tailTurn)}" preview="${preview}" />
91
+ </compaction_result>`;
92
+ }
93
+
94
+ function makeCapturingProvider(response: string): {
95
+ provider: Provider;
96
+ lastRequest: () => Message[] | null;
97
+ } {
98
+ let captured: Message[] | null = null;
99
+ const provider: Provider = {
100
+ name: "mock-provider",
101
+ sendMessage: async (messages: Message[]) => {
102
+ captured = messages;
103
+ return {
104
+ content: [{ type: "text", text: response }],
105
+ model: "mock-model",
106
+ usage: { inputTokens: 100, outputTokens: 50 },
107
+ stopReason: "end_turn",
108
+ };
109
+ },
110
+ };
111
+ return { provider, lastRequest: () => captured };
112
+ }
113
+
114
+ function estimate(messages: Message[]): number {
115
+ return estimatePromptTokens(messages, "system", {
116
+ providerName: "mock-provider",
117
+ });
118
+ }
119
+
120
+ describe("runAssistantDrivenCompaction — summary call self-truncation", () => {
121
+ test("front-truncates the outbound request when the full history exceeds the context window", async () => {
122
+ // GIVEN a tool-pair-free history whose estimate exceeds the window, so an
123
+ // overflow recovery would route it straight into the summary call.
124
+ const messages: Message[] = [];
125
+ for (let i = 0; i < 40; i++) {
126
+ messages.push(
127
+ userTurn(
128
+ i,
129
+ "Heavy user turn body that carries real weight. ".repeat(8),
130
+ ),
131
+ );
132
+ messages.push(
133
+ assistantTurn(
134
+ i,
135
+ "Heavy assistant reply that also carries weight. ".repeat(8),
136
+ ),
137
+ );
138
+ }
139
+
140
+ const maxInputTokens = 10_000;
141
+ // Budget mirrors compactor.compactionPrefixBudget: window minus the
142
+ // instruction reserve (800) and the 15% output reserve.
143
+ const prefixBudget =
144
+ maxInputTokens - 800 - Math.floor(maxInputTokens * 0.15);
145
+ expect(estimate(messages)).toBeGreaterThan(prefixBudget);
146
+
147
+ const { provider, lastRequest } = makeCapturingProvider(
148
+ compactionResponse(38, "Heavy user turn body"),
149
+ );
150
+
151
+ // WHEN the summary call runs against that oversized history.
152
+ const result = await runAssistantDrivenCompaction({
153
+ conversationId: "conv-test",
154
+ messages,
155
+ provider,
156
+ systemPrompt: "system",
157
+ compaction: { enabled: true, autoThreshold: 0.7 },
158
+ maxInputTokens,
159
+ force: true,
160
+ previousEstimatedInputTokens: 90_000,
161
+ });
162
+
163
+ expect(result.compacted).toBe(true);
164
+
165
+ // THEN the request actually sent fits within the context window.
166
+ const sent = lastRequest();
167
+ expect(sent).not.toBeNull();
168
+ expect(estimate(sent ?? [])).toBeLessThan(maxInputTokens);
169
+
170
+ // AND it opens with a marker noting the dropped leading messages so the
171
+ // model knows the summary covers only the visible portion.
172
+ const firstBlock = sent?.[0]?.content[0];
173
+ const firstText = firstBlock && "text" in firstBlock ? firstBlock.text : "";
174
+ expect(firstText).toContain(TRUNCATION_MARKER);
175
+ });
176
+
177
+ test("sends the full history untouched when it already fits below the budget", async () => {
178
+ // GIVEN a small history whose estimate is well under the window — the
179
+ // common budget-triggered compaction.
180
+ const messages: Message[] = [];
181
+ for (let i = 0; i < 8; i++) {
182
+ messages.push(userTurn(i, "short user turn body"));
183
+ messages.push(assistantTurn(i, "short assistant reply body"));
184
+ }
185
+
186
+ const maxInputTokens = 200_000;
187
+ const prefixBudget =
188
+ maxInputTokens - 800 - Math.floor(maxInputTokens * 0.15);
189
+ expect(estimate(messages)).toBeLessThan(prefixBudget);
190
+
191
+ const { provider, lastRequest } = makeCapturingProvider(
192
+ compactionResponse(6, "short user turn body"),
193
+ );
194
+
195
+ // WHEN the summary call runs against the below-budget history.
196
+ const result = await runAssistantDrivenCompaction({
197
+ conversationId: "conv-test",
198
+ messages,
199
+ provider,
200
+ systemPrompt: "system",
201
+ compaction: { enabled: true, autoThreshold: 0.7 },
202
+ maxInputTokens,
203
+ force: true,
204
+ previousEstimatedInputTokens: 90_000,
205
+ });
206
+
207
+ expect(result.compacted).toBe(true);
208
+
209
+ // THEN no truncation marker is prepended and the request carries every
210
+ // history message plus the single instruction, with the leading message
211
+ // byte-identical to the original — keeping the prefix cache warm.
212
+ const sent = lastRequest();
213
+ expect(sent).not.toBeNull();
214
+ expect(sent?.length).toBe(messages.length + 1);
215
+ const firstBlock = sent?.[0]?.content[0];
216
+ const firstText = firstBlock && "text" in firstBlock ? firstBlock.text : "";
217
+ expect(firstText).not.toContain(TRUNCATION_MARKER);
218
+ const originalFirst = messages[0]?.content[0];
219
+ expect(firstText).toBe(
220
+ originalFirst && "text" in originalFirst ? originalFirst.text : "",
221
+ );
222
+ });
223
+ });
@@ -75,7 +75,12 @@ import {
75
75
  loadConfig,
76
76
  mergeDefaultWorkspaceConfig,
77
77
  } from "../config/loader.js";
78
- import { seedInferenceProfiles } from "../config/seed-inference-profiles.js";
78
+ import {
79
+ MANAGED_PROFILE_NAMES,
80
+ materializeProfile,
81
+ OS_BETA_PROFILE_TEMPLATE,
82
+ seedInferenceProfiles,
83
+ } from "../config/seed-inference-profiles.js";
79
84
  import type { DrizzleDb } from "../memory/db-connection.js";
80
85
  import { migrateCreateProviderConnections } from "../memory/migrations/243-provider-connections.js";
81
86
  import { migrateProviderConnectionStatusLabel } from "../memory/migrations/244-provider-connection-status-label.js";
@@ -417,6 +422,24 @@ describe("loadConfig startup behavior", () => {
417
422
  expect(raw.memory?.jobs?.workerConcurrency).toBe(4);
418
423
  });
419
424
 
425
+ test("strips daemon.reapOrphanedSubprocesses from existing user configs", () => {
426
+ // `daemon.reapOrphanedSubprocesses` is a deprecated opt-in flag: the
427
+ // orphan-subprocess reaper runs by default whenever the daemon is PID 1 on
428
+ // Linux. Existing configs that have it written to disk should load cleanly
429
+ // with the field silently stripped.
430
+ writeConfig({
431
+ provider: "anthropic",
432
+ daemon: { reapOrphanedSubprocesses: true, standaloneRecording: false },
433
+ });
434
+
435
+ loadConfig();
436
+
437
+ const raw = JSON.parse(readFileSync(CONFIG_PATH, "utf-8"));
438
+ expect(raw.daemon?.reapOrphanedSubprocesses).toBeUndefined();
439
+ // Sibling fields under daemon are preserved
440
+ expect(raw.daemon?.standaloneRecording).toBe(false);
441
+ });
442
+
420
443
  test("still writes a default config on first launch when file is absent", () => {
421
444
  // Discoverability: when no config.json exists, write one populated with
422
445
  // all schema defaults so users can see and edit available options.
@@ -462,9 +485,11 @@ describe("loadConfig startup behavior", () => {
462
485
  "anthropic-personal",
463
486
  );
464
487
  // Managed profiles exist as well.
465
- expect(config.llm.profiles.balanced?.model).toBe("claude-sonnet-4-6");
488
+ expect(config.llm.profiles.balanced?.model).toBe(
489
+ "accounts/fireworks/models/minimax-m3",
490
+ );
466
491
  expect(config.llm.profiles.balanced?.provider_connection).toBe(
467
- "anthropic-managed",
492
+ "fireworks-managed",
468
493
  );
469
494
 
470
495
  const raw = JSON.parse(readFileSync(CONFIG_PATH, "utf-8"));
@@ -473,7 +498,9 @@ describe("loadConfig startup behavior", () => {
473
498
  model: "claude-opus-4-7",
474
499
  });
475
500
  expect(raw.llm.activeProfile).toBe("custom-balanced");
476
- expect(raw.llm.profiles.balanced.model).toBe("claude-sonnet-4-6");
501
+ expect(raw.llm.profiles.balanced.model).toBe(
502
+ "accounts/fireworks/models/minimax-m3",
503
+ );
477
504
  });
478
505
 
479
506
  test("on-platform hatch seeds only managed profiles", () => {
@@ -501,9 +528,11 @@ describe("loadConfig startup behavior", () => {
501
528
  const config = loadConfig();
502
529
 
503
530
  expect(config.llm.activeProfile).toBe("balanced");
504
- expect(config.llm.profiles.balanced?.model).toBe("claude-sonnet-4-6");
531
+ expect(config.llm.profiles.balanced?.model).toBe(
532
+ "accounts/fireworks/models/minimax-m3",
533
+ );
505
534
  expect(config.llm.profiles.balanced?.provider_connection).toBe(
506
- "anthropic-managed",
535
+ "fireworks-managed",
507
536
  );
508
537
  // No user profiles created on platform.
509
538
  expect(config.llm.profiles["custom-balanced"]).toBeUndefined();
@@ -545,10 +574,10 @@ describe("loadConfig startup behavior", () => {
545
574
  expect(raw.llm.profiles["custom-balanced"].provider_connection).toBe(
546
575
  "anthropic-personal",
547
576
  );
548
- // Managed balanced profile is seeded for anthropic-managed.
549
- expect(raw.llm.profiles.balanced.provider).toBe("anthropic");
577
+ // Managed balanced profile is seeded for fireworks-managed (MiniMax M3).
578
+ expect(raw.llm.profiles.balanced.provider).toBe("fireworks");
550
579
  expect(raw.llm.profiles.balanced.provider_connection).toBe(
551
- "anthropic-managed",
580
+ "fireworks-managed",
552
581
  );
553
582
  });
554
583
 
@@ -582,9 +611,9 @@ describe("loadConfig startup behavior", () => {
582
611
  const raw = JSON.parse(readFileSync(CONFIG_PATH, "utf-8"));
583
612
  // On-platform: no user profiles created, active resets to managed balanced.
584
613
  expect(raw.llm.activeProfile).toBe("balanced");
585
- expect(raw.llm.profiles.balanced.provider).toBe("anthropic");
614
+ expect(raw.llm.profiles.balanced.provider).toBe("fireworks");
586
615
  expect(raw.llm.profiles.balanced.provider_connection).toBe(
587
- "anthropic-managed",
616
+ "fireworks-managed",
588
617
  );
589
618
  // The old custom-balanced is preserved on disk but no longer active.
590
619
  expect(raw.llm.profiles["custom-balanced"].provider).toBe("openai");
@@ -636,10 +665,10 @@ describe("loadConfig startup behavior", () => {
636
665
  "gpt-5.4-nano",
637
666
  );
638
667
 
639
- // Managed profiles are also seeded (balanced uses Anthropic).
640
- expect(raw.llm.profiles.balanced.provider).toBe("anthropic");
668
+ // Managed profiles are also seeded (balanced uses Fireworks/MiniMax M3).
669
+ expect(raw.llm.profiles.balanced.provider).toBe("fireworks");
641
670
  expect(raw.llm.profiles.balanced.provider_connection).toBe(
642
- "anthropic-managed",
671
+ "fireworks-managed",
643
672
  );
644
673
  expect(raw.llm.profiles.balanced.source).toBe("managed");
645
674
  expect(raw.llm.profiles["quality-optimized"].provider).toBe("anthropic");
@@ -667,13 +696,84 @@ describe("loadConfig startup behavior", () => {
667
696
  mergeDefaultConfigAndSeedInferenceProfiles();
668
697
  const raw = JSON.parse(readFileSync(CONFIG_PATH, "utf-8"));
669
698
 
670
- expect(raw.llm.profiles.balanced.model).toBe("claude-sonnet-4-6");
699
+ expect(raw.llm.profiles.balanced.model).toBe(
700
+ "accounts/fireworks/models/minimax-m3",
701
+ );
702
+ expect(raw.llm.profiles.balanced.provider_connection).toBe(
703
+ "fireworks-managed",
704
+ );
705
+ expect(raw.llm.activeProfile).toBe("balanced");
706
+ });
707
+
708
+ test("on-platform managed profiles reconcile to the code template on every boot", () => {
709
+ // Headline behavior: on-platform installs now reconcile managed profile
710
+ // content from the code template on every boot (same as off-platform), so
711
+ // model/config updates ship in a release without a workspace migration.
712
+ process.env.IS_PLATFORM = "true";
713
+
714
+ writeConfig({
715
+ llm: {
716
+ profiles: {
717
+ balanced: {
718
+ source: "managed",
719
+ provider: "anthropic",
720
+ model: "old-model-from-previous-release",
721
+ maxTokens: 1,
722
+ provider_connection: "anthropic-managed",
723
+ },
724
+ },
725
+ activeProfile: "balanced",
726
+ },
727
+ });
728
+
729
+ // Non-hatch boot (no overlay). Content is refreshed from the template.
730
+ mergeDefaultConfigAndSeedInferenceProfiles();
731
+ const raw = JSON.parse(readFileSync(CONFIG_PATH, "utf-8"));
732
+
733
+ expect(raw.llm.profiles.balanced.model).toBe(
734
+ "accounts/fireworks/models/minimax-m3",
735
+ );
736
+ expect(raw.llm.profiles.balanced.maxTokens).toBe(32000);
737
+ expect(raw.llm.profiles.balanced.topP).toBe(0.95);
671
738
  expect(raw.llm.profiles.balanced.provider_connection).toBe(
672
- "anthropic-managed",
739
+ "fireworks-managed",
673
740
  );
674
741
  expect(raw.llm.activeProfile).toBe("balanced");
675
742
  });
676
743
 
744
+ test("on-platform reseed preserves user-edited label and status on managed profiles", () => {
745
+ // The only two fields a user may override on a managed profile — label and
746
+ // status — survive the on-platform reconcile, exactly as off-platform.
747
+ process.env.IS_PLATFORM = "true";
748
+
749
+ writeConfig({
750
+ llm: {
751
+ profiles: {
752
+ balanced: {
753
+ source: "managed",
754
+ provider: "anthropic",
755
+ model: "old-model-from-previous-release",
756
+ provider_connection: "anthropic-managed",
757
+ label: "My Default",
758
+ status: "disabled",
759
+ },
760
+ },
761
+ activeProfile: "balanced",
762
+ },
763
+ });
764
+
765
+ mergeDefaultConfigAndSeedInferenceProfiles();
766
+ const raw = JSON.parse(readFileSync(CONFIG_PATH, "utf-8"));
767
+
768
+ // Content refreshes from the template...
769
+ expect(raw.llm.profiles.balanced.model).toBe(
770
+ "accounts/fireworks/models/minimax-m3",
771
+ );
772
+ // ...but the user's label and status overrides are preserved.
773
+ expect(raw.llm.profiles.balanced.label).toBe("My Default");
774
+ expect(raw.llm.profiles.balanced.status).toBe("disabled");
775
+ });
776
+
677
777
  test("off-platform reseed preserves user-edited label on managed profiles (Codex P1 on PR #30362)", () => {
678
778
  // Simulate a user who renamed the managed "balanced" profile via
679
779
  // PUT /v1/config/llm/profiles/balanced { label: "My Default" }.
@@ -696,7 +796,9 @@ describe("loadConfig startup behavior", () => {
696
796
  const raw = JSON.parse(readFileSync(CONFIG_PATH, "utf-8"));
697
797
 
698
798
  // Model still gets the new template value (provider-controlled).
699
- expect(raw.llm.profiles.balanced.model).toBe("claude-sonnet-4-6");
799
+ expect(raw.llm.profiles.balanced.model).toBe(
800
+ "accounts/fireworks/models/minimax-m3",
801
+ );
700
802
  // But the user's label override is preserved across the reseed.
701
803
  expect(raw.llm.profiles.balanced.label).toBe("My Default");
702
804
  });
@@ -724,7 +826,51 @@ describe("loadConfig startup behavior", () => {
724
826
 
725
827
  expect(raw.llm.profiles.balanced.status).toBe("disabled");
726
828
  // Model still refreshes — only label/status are user-owned.
727
- expect(raw.llm.profiles.balanced.model).toBe("claude-sonnet-4-6");
829
+ expect(raw.llm.profiles.balanced.model).toBe(
830
+ "accounts/fireworks/models/minimax-m3",
831
+ );
832
+ });
833
+
834
+ test("reseed preserves user-edited topP on managed profiles", () => {
835
+ // Simulate a user who overrode topP on the managed "balanced" profile via
836
+ // PUT /v1/config/llm/profiles/balanced { topP: 0.5 }. The override must
837
+ // survive the reconcile instead of reverting to the template's 0.95.
838
+ writeConfig({
839
+ llm: {
840
+ profiles: {
841
+ balanced: {
842
+ source: "managed",
843
+ provider: "anthropic",
844
+ model: "old-model-from-previous-release",
845
+ provider_connection: "anthropic-managed",
846
+ topP: 0.5,
847
+ },
848
+ },
849
+ activeProfile: "balanced",
850
+ },
851
+ });
852
+
853
+ mergeDefaultConfigAndSeedInferenceProfiles();
854
+ const raw = JSON.parse(readFileSync(CONFIG_PATH, "utf-8"));
855
+
856
+ // The user's topP override is preserved across the reseed (not reverted to
857
+ // the template default of 0.95).
858
+ expect(raw.llm.profiles.balanced.topP).toBe(0.5);
859
+ // Model still refreshes — topP is user-owned, the rest is template-owned.
860
+ expect(raw.llm.profiles.balanced.model).toBe(
861
+ "accounts/fireworks/models/minimax-m3",
862
+ );
863
+ });
864
+
865
+ test("reseed seeds the template topP on a fresh managed balanced profile", () => {
866
+ // No previous on-disk entry → the balanced profile materializes with the
867
+ // template's topP default of 0.95.
868
+ writeConfig({ llm: {} });
869
+
870
+ mergeDefaultConfigAndSeedInferenceProfiles();
871
+ const raw = JSON.parse(readFileSync(CONFIG_PATH, "utf-8"));
872
+
873
+ expect(raw.llm.profiles.balanced.topP).toBe(0.95);
728
874
  });
729
875
 
730
876
  test("off-platform reseed preserves an explicit null label (user cleared it)", () => {
@@ -765,12 +911,20 @@ describe("loadConfig startup behavior", () => {
765
911
  const raw = JSON.parse(readFileSync(CONFIG_PATH, "utf-8"));
766
912
 
767
913
  expect(raw.llm.profiles.balanced.label).toBe("Balanced (Managed)");
768
- expect(raw.llm.profiles.balanced.model).toBe("claude-sonnet-4-6");
914
+ expect(raw.llm.profiles.balanced.model).toBe(
915
+ "accounts/fireworks/models/minimax-m3",
916
+ );
769
917
  // Status is unset by default — must not appear as `undefined`.
770
918
  expect("status" in raw.llm.profiles.balanced).toBe(false);
771
919
  });
772
920
 
773
- test("platform-provided profile fragments are not polluted by managed seeds", () => {
921
+ test("platform overlay fragment wins its hatch boot, then content reconciles to the code template (label preserved)", () => {
922
+ // The overlay is authoritative for the boot it is supplied: its `balanced`
923
+ // fragment lands verbatim and is never polluted by template fields it omits
924
+ // (no maxTokens/thinking leak in). On the next boot — overlay archived —
925
+ // managed profile *content* reconciles to the code template, since the
926
+ // templates are the single source of truth for content. Only the
927
+ // user/overlay-set `label` survives the reconcile.
774
928
  process.env.IS_PLATFORM = "true";
775
929
 
776
930
  writeConfig({
@@ -824,6 +978,7 @@ describe("loadConfig startup behavior", () => {
824
978
  const config = loadConfig();
825
979
  const mainAgentConfig = resolveCallSiteConfig("mainAgent", config.llm);
826
980
 
981
+ // Hatch boot: overlay fragment is preserved verbatim (preserveProfileNames).
827
982
  expect(config.llm.activeProfile).toBe("balanced");
828
983
  expect(config.llm.profiles.balanced).toEqual({
829
984
  source: "managed",
@@ -844,16 +999,26 @@ describe("loadConfig startup behavior", () => {
844
999
  expect(raw.llm.profiles.balanced.maxTokens).toBeUndefined();
845
1000
  expect(raw.llm.profiles.balanced.thinking).toBeUndefined();
846
1001
 
1002
+ // Next boot, no overlay: content reconciles to the fireworks-managed code
1003
+ // template; only the overlay-set label is carried across.
847
1004
  mergeDefaultConfigAndSeedInferenceProfiles();
848
1005
 
849
1006
  const afterRestart = JSON.parse(readFileSync(CONFIG_PATH, "utf-8"));
850
1007
  expect(afterRestart.llm.activeProfile).toBe("balanced");
851
- expect(afterRestart.llm.profiles.balanced).toEqual({
852
- source: "managed",
853
- provider: "openai",
854
- model: "gpt-5.4",
855
- label: "Platform Balanced",
1008
+ expect(afterRestart.llm.profiles.balanced.provider).toBe("fireworks");
1009
+ expect(afterRestart.llm.profiles.balanced.provider_connection).toBe(
1010
+ "fireworks-managed",
1011
+ );
1012
+ expect(afterRestart.llm.profiles.balanced.model).toBe(
1013
+ "accounts/fireworks/models/minimax-m3",
1014
+ );
1015
+ expect(afterRestart.llm.profiles.balanced.maxTokens).toBe(32000);
1016
+ expect(afterRestart.llm.profiles.balanced.thinking).toEqual({
1017
+ enabled: true,
1018
+ streamThinking: true,
856
1019
  });
1020
+ // The user/overlay-set label is the one field that survives the reconcile.
1021
+ expect(afterRestart.llm.profiles.balanced.label).toBe("Platform Balanced");
857
1022
  });
858
1023
 
859
1024
  test("quarantines corrupt config before merging hatch overlay", () => {
@@ -892,7 +1057,9 @@ describe("loadConfig startup behavior", () => {
892
1057
  // Off-platform hatch: user profiles are active.
893
1058
  expect(raw.llm.activeProfile).toBe("custom-balanced");
894
1059
  expect(raw.llm.profiles["custom-balanced"].provider).toBe("anthropic");
895
- expect(raw.llm.profiles.balanced.model).toBe("claude-sonnet-4-6");
1060
+ expect(raw.llm.profiles.balanced.model).toBe(
1061
+ "accounts/fireworks/models/minimax-m3",
1062
+ );
896
1063
  });
897
1064
 
898
1065
  test("still quarantines corrupt JSON", () => {
@@ -989,6 +1156,11 @@ describe("seedInferenceProfiles BYOK-mode managed profile labels", () => {
989
1156
 
990
1157
  // Personal profiles keep their bare labels — they're the daily driver.
991
1158
  expect(config.llm.profiles["custom-balanced"]?.label).toBe("Balanced");
1159
+
1160
+ // top_p is scoped to the managed Balanced profile only; the BYOK
1161
+ // custom-balanced profile must not pick it up.
1162
+ expect(config.llm.profiles.balanced?.topP).toBe(0.95);
1163
+ expect(config.llm.profiles["custom-balanced"]?.topP).toBeUndefined();
992
1164
  });
993
1165
 
994
1166
  test("off-platform hatch initializes managed profile status to 'disabled'", () => {
@@ -1036,7 +1208,7 @@ describe("seedInferenceProfiles BYOK-mode managed profile labels", () => {
1036
1208
 
1037
1209
  expect(raw.llm.activeProfile).toBe("balanced");
1038
1210
  expect(raw.llm.profiles.balanced.provider_connection).toBe(
1039
- "anthropic-managed",
1211
+ "fireworks-managed",
1040
1212
  );
1041
1213
  expect("status" in raw.llm.profiles.balanced).toBe(false);
1042
1214
  // Connections exist (status is no longer a connection-level concept).
@@ -1283,3 +1455,72 @@ describe("seedInferenceProfiles BYOK-mode managed profile labels", () => {
1283
1455
  expect(config.llm.profiles.balanced?.label).toBe("Balanced (Managed)");
1284
1456
  });
1285
1457
  });
1458
+
1459
+ // ---------------------------------------------------------------------------
1460
+ // Tests: OS Beta flag-gated managed profile. The template is defined but
1461
+ // intentionally NOT part of MANAGED_PROFILE_TEMPLATES, so seedInferenceProfiles
1462
+ // must never create it. A later PR reconciles it in/out based on the `os-beta`
1463
+ // feature flag.
1464
+ // ---------------------------------------------------------------------------
1465
+
1466
+ describe("OS Beta managed profile template", () => {
1467
+ beforeEach(() => {
1468
+ ensureTestDir();
1469
+ const resetPaths = [
1470
+ CONFIG_PATH,
1471
+ join(WORKSPACE_DIR, "default-config.json"),
1472
+ join(WORKSPACE_DIR, "hatch-overlay.json"),
1473
+ join(WORKSPACE_DIR, "keys.enc"),
1474
+ join(WORKSPACE_DIR, "data"),
1475
+ join(WORKSPACE_DIR, "data", "memory"),
1476
+ ];
1477
+ for (const path of resetPaths) {
1478
+ if (existsSync(path)) {
1479
+ rmSync(path, { recursive: true, force: true });
1480
+ }
1481
+ }
1482
+ ensureTestDir();
1483
+ setStorePathForTesting(join(WORKSPACE_DIR, "keys.enc"));
1484
+ delete process.env.VELLUM_DEFAULT_WORKSPACE_CONFIG_PATH;
1485
+ delete process.env.IS_PLATFORM;
1486
+ invalidateConfigCache();
1487
+ });
1488
+
1489
+ afterEach(() => {
1490
+ setStorePathForTesting(null);
1491
+ delete process.env.VELLUM_DEFAULT_WORKSPACE_CONFIG_PATH;
1492
+ delete process.env.IS_PLATFORM;
1493
+ invalidateConfigCache();
1494
+ });
1495
+
1496
+ test("seedInferenceProfiles does not create an os-beta profile", () => {
1497
+ writeConfig({ llm: { default: { provider: "anthropic" } } });
1498
+
1499
+ mergeDefaultConfigAndSeedInferenceProfiles();
1500
+ const raw = JSON.parse(readFileSync(CONFIG_PATH, "utf-8"));
1501
+
1502
+ expect(raw.llm.profiles["os-beta"]).toBeUndefined();
1503
+ expect((raw.llm.profileOrder as string[]).includes("os-beta")).toBe(false);
1504
+ });
1505
+
1506
+ test("MANAGED_PROFILE_NAMES contains os-beta", () => {
1507
+ expect(MANAGED_PROFILE_NAMES.has("os-beta")).toBe(true);
1508
+ });
1509
+
1510
+ test("materializeProfile honors the explicit OS Beta model", () => {
1511
+ const entry = materializeProfile(
1512
+ OS_BETA_PROFILE_TEMPLATE,
1513
+ "fireworks",
1514
+ "fireworks-managed",
1515
+ );
1516
+
1517
+ expect(entry.model).toBe("accounts/fireworks/models/glm-5p2");
1518
+ expect(entry.provider_connection).toBe("fireworks-managed");
1519
+ expect(entry.provider).toBe("fireworks");
1520
+ expect(entry.label).toBe("OS Beta");
1521
+ expect(entry.source).toBe("managed");
1522
+ expect(entry.maxTokens).toBe(32000);
1523
+ expect(entry.effort).toBe("high");
1524
+ expect(entry.thinking?.enabled).toBe(true);
1525
+ });
1526
+ });