@vellumai/assistant 0.9.1-staging.1 → 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 (433) hide show
  1. package/docs/activation-funnel-telemetry.md +24 -18
  2. package/node_modules/@vellumai/gateway-client/src/admission-policy-contract.ts +97 -0
  3. package/node_modules/@vellumai/gateway-client/src/inbound-contract.ts +10 -0
  4. package/node_modules/@vellumai/gateway-client/src/index.ts +15 -0
  5. package/openapi.yaml +852 -15
  6. package/package.json +1 -1
  7. package/src/__tests__/access-request-card-view.test.ts +98 -0
  8. package/src/__tests__/access-request-seed-content-blocks.test.ts +2 -4
  9. package/src/__tests__/actor-trust-resolver-address-fallback.test.ts +59 -7
  10. package/src/__tests__/agent-loop-compaction-strip.test.ts +17 -16
  11. package/src/__tests__/agent-loop-mutable-latest-user-message.test.ts +16 -13
  12. package/src/__tests__/app-compiler.test.ts +15 -1
  13. package/src/__tests__/auth-fallback-events-store.test.ts +6 -14
  14. package/src/__tests__/call-pointer-messages.test.ts +28 -0
  15. package/src/__tests__/cancel-clears-processing.test.ts +89 -0
  16. package/src/__tests__/channel-approval-routes.test.ts +0 -4
  17. package/src/__tests__/channel-inbound-disk-pressure.test.ts +5 -15
  18. package/src/__tests__/cli-memory-v2-reembed-skills.test.ts +3 -4
  19. package/src/__tests__/compactor-image-manifest-trust.test.ts +21 -1
  20. package/src/__tests__/compactor-summary-call-truncation.test.ts +223 -0
  21. package/src/__tests__/config-loader-backfill.test.ts +174 -30
  22. package/src/__tests__/config-schema.test.ts +35 -0
  23. package/src/__tests__/confirmation-request-guardian-bridge.test.ts +2 -2
  24. package/src/__tests__/contact-store-user-file.test.ts +0 -6
  25. package/src/__tests__/contacts-tools.test.ts +29 -0
  26. package/src/__tests__/conversation-agent-loop-overflow.test.ts +1 -0
  27. package/src/__tests__/conversation-agent-loop.test.ts +58 -0
  28. package/src/__tests__/conversation-attention-telegram.test.ts +0 -1
  29. package/src/__tests__/conversation-lifecycle.test.ts +7 -9
  30. package/src/__tests__/conversation-load-history-repair.test.ts +101 -0
  31. package/src/__tests__/conversation-routes-guardian-reply.test.ts +15 -12
  32. package/src/__tests__/conversation-surfaces-activation-emit.test.ts +6 -3
  33. package/src/__tests__/conversation-title-service.test.ts +62 -0
  34. package/src/__tests__/credential-execution-shell-lockdown.test.ts +18 -11
  35. package/src/__tests__/credential-prompt-route.test.ts +1 -0
  36. package/src/__tests__/credential-security-invariants.test.ts +2 -0
  37. package/src/__tests__/disk-pressure-policy.test.ts +12 -0
  38. package/src/__tests__/disk-usage.test.ts +65 -0
  39. package/src/__tests__/dynamic-page-surface.test.ts +51 -0
  40. package/src/__tests__/gateway-flag-listener.test.ts +110 -1
  41. package/src/__tests__/guardian-binding-drift-heal.test.ts +1 -1
  42. package/src/__tests__/guardian-card-withdrawal.test.ts +403 -0
  43. package/src/__tests__/guardian-decision-primitive-canonical.test.ts +5 -3
  44. package/src/__tests__/guardian-grant-minting.test.ts +3 -35
  45. package/src/__tests__/guardian-routing-invariants.test.ts +64 -26
  46. package/src/__tests__/guardian-routing-state.test.ts +0 -1
  47. package/src/__tests__/headless-browser-mode.test.ts +10 -0
  48. package/src/__tests__/headless-browser-navigate.test.ts +8 -3
  49. package/src/__tests__/helpers/create-guardian-binding.ts +0 -1
  50. package/src/__tests__/host-browser-proxy.test.ts +87 -0
  51. package/src/__tests__/injector-v3-suppression.test.ts +27 -20
  52. package/src/__tests__/internal-telemetry-routes.test.ts +6 -14
  53. package/src/__tests__/invite-redemption-service.test.ts +0 -3
  54. package/src/__tests__/llm-catalog-parity.test.ts +30 -1
  55. package/src/__tests__/llm-resolver.test.ts +21 -0
  56. package/src/__tests__/llm-schema.test.ts +1 -0
  57. package/src/__tests__/managed-profile-guard.test.ts +163 -4
  58. package/src/__tests__/mcp-health-check.test.ts +6 -7
  59. package/src/__tests__/media-stream-server-integration.test.ts +317 -13
  60. package/src/__tests__/path-policy.test.ts +34 -0
  61. package/src/__tests__/persona-resolver.test.ts +38 -0
  62. package/src/__tests__/plugin-api-provider.test.ts +24 -0
  63. package/src/__tests__/plugin-tool-contribution.test.ts +6 -3
  64. package/src/__tests__/post-compaction-reinjection-idempotency.test.ts +214 -0
  65. package/src/__tests__/provider-send-message-override-profile.test.ts +76 -0
  66. package/src/__tests__/reaction-persistence.test.ts +150 -29
  67. package/src/__tests__/relay-server.test.ts +285 -0
  68. package/src/__tests__/runtime-attachment-metadata.test.ts +0 -1
  69. package/src/__tests__/scheduler-reuse-conversation.test.ts +8 -5
  70. package/src/__tests__/skill-execute-input.test.ts +5 -0
  71. package/src/__tests__/skills.test.ts +51 -0
  72. package/src/__tests__/slack-notification-approval-card.test.ts +176 -0
  73. package/src/__tests__/slack-reaction-canonical-approval.test.ts +285 -0
  74. package/src/__tests__/subagent-tools.test.ts +150 -0
  75. package/src/__tests__/task-progress-nudge-hook.test.ts +1 -1
  76. package/src/__tests__/title-generate-hook.test.ts +100 -3
  77. package/src/__tests__/tool-approval-seed-content-blocks.test.ts +1 -1
  78. package/src/__tests__/tool-audit-listener.test.ts +7 -7
  79. package/src/__tests__/tool-executor-lifecycle-events.test.ts +6 -3
  80. package/src/__tests__/trusted-contact-approval-notifier.test.ts +4 -2
  81. package/src/__tests__/trusted-contact-inline-approval-integration.test.ts +220 -3
  82. package/src/__tests__/trusted-contact-verification.test.ts +2 -4
  83. package/src/__tests__/twilio-routes.test.ts +81 -1
  84. package/src/__tests__/voice-invite-redemption.test.ts +0 -1
  85. package/src/__tests__/weak-open-model.test.ts +30 -0
  86. package/src/__tests__/workspace-migration-105-enable-memory-v3-live-for-new-workspaces.test.ts +149 -0
  87. package/src/__tests__/workspace-migration-108-drop-balanced-economy-profile.test.ts +285 -0
  88. package/src/__tests__/workspace-migration-add-send-diagnostics.test.ts +1 -1
  89. package/src/__tests__/workspace-migration-drop-collect-usage-data.test.ts +118 -0
  90. package/src/__tests__/workspace-migration-drop-send-diagnostics.test.ts +118 -0
  91. package/src/a2a/__tests__/e2e-a2a-channel.test.ts +0 -4
  92. package/src/agent/loop.ts +33 -33
  93. package/src/api/events/tool-result.ts +6 -0
  94. package/src/api/events/workflow-completed.ts +53 -0
  95. package/src/api/events/workflow-leaf-finished.ts +38 -0
  96. package/src/api/events/workflow-leaf-started.ts +35 -0
  97. package/src/api/events/workflow-progress.ts +32 -0
  98. package/src/api/events/workflow-started.ts +31 -0
  99. package/src/api/index.ts +40 -0
  100. package/src/api/responses/conversation-message.ts +26 -0
  101. package/src/api/responses/home.ts +26 -0
  102. package/src/api/responses/workflow-journal.ts +53 -0
  103. package/src/approvals/guardian-card-withdrawal.ts +145 -0
  104. package/src/approvals/guardian-decision-primitive.ts +26 -3
  105. package/src/approvals/guardian-request-resolvers.ts +181 -78
  106. package/src/calls/__tests__/channel-admission-reader.test.ts +132 -0
  107. package/src/calls/__tests__/relay-setup-router.test.ts +350 -0
  108. package/src/calls/call-pointer-messages.ts +10 -4
  109. package/src/calls/channel-admission-reader.ts +104 -0
  110. package/src/calls/guardian-dispatch.ts +17 -45
  111. package/src/calls/media-stream-server.ts +84 -2
  112. package/src/calls/relay-server.ts +66 -0
  113. package/src/calls/relay-setup-router.ts +82 -1
  114. package/src/calls/twilio-routes.ts +17 -8
  115. package/src/cli/commands/clients.ts +3 -0
  116. package/src/cli/commands/{__tests__ → memory/__tests__}/memory-v2-compare-render.test.ts +1 -1
  117. package/src/cli/commands/{__tests__ → memory/__tests__}/memory-v2.test.ts +8 -7
  118. package/src/cli/commands/{__tests__ → memory/__tests__}/memory-v3.test.ts +5 -4
  119. package/src/cli/commands/memory/index.ts +30 -0
  120. package/src/cli/commands/{memory-v2-compare-render.ts → memory/memory-v2-compare-render.ts} +1 -1
  121. package/src/cli/commands/{memory-v2.ts → memory/memory-v2.ts} +6 -15
  122. package/src/cli/commands/{memory-v3.ts → memory/memory-v3.ts} +97 -11
  123. package/src/cli/commands/oauth/status.test.ts +36 -0
  124. package/src/cli/commands/oauth/status.ts +23 -3
  125. package/src/cli/commands/plugins.ts +57 -5
  126. package/src/cli/lib/__tests__/inspect-plugin.test.ts +54 -0
  127. package/src/cli/lib/__tests__/merge-plugin-tree.test.ts +134 -4
  128. package/src/cli/lib/__tests__/plugin-surfaces.test.ts +111 -0
  129. package/src/cli/lib/__tests__/upgrade-plugin.test.ts +53 -11
  130. package/src/cli/lib/inspect-plugin.ts +12 -1
  131. package/src/cli/lib/merge-plugin-tree.ts +149 -49
  132. package/src/cli/lib/plugin-surfaces.ts +104 -0
  133. package/src/cli/lib/upgrade-plugin.ts +64 -36
  134. package/src/cli/program.ts +2 -4
  135. package/src/config/__tests__/sync-gated-profiles.test.ts +368 -0
  136. package/src/config/assistant-feature-flags.ts +22 -7
  137. package/src/config/bundled-skills/contacts/tools/contact-search.ts +0 -1
  138. package/src/config/bundled-skills/messaging/SKILL.md +6 -4
  139. package/src/config/bundled-skills/messaging/tools/messaging-archive-by-sender.ts +9 -8
  140. package/src/config/bundled-skills/workflows/SKILL.md +14 -7
  141. package/src/config/call-site-defaults.ts +3 -0
  142. package/src/config/feature-flag-registry.json +49 -18
  143. package/src/config/llm-resolver.ts +3 -0
  144. package/src/config/memory-v3-gate.ts +11 -0
  145. package/src/config/schema.ts +8 -6
  146. package/src/config/schemas/__tests__/memory-v3.test.ts +1 -0
  147. package/src/config/schemas/call-site-catalog.ts +7 -0
  148. package/src/config/schemas/channels.ts +11 -0
  149. package/src/config/schemas/llm.ts +31 -0
  150. package/src/config/schemas/memory-lifecycle.ts +3 -7
  151. package/src/config/schemas/memory-v3.ts +6 -0
  152. package/src/config/schemas/services.ts +18 -0
  153. package/src/config/seed-inference-profiles.ts +94 -34
  154. package/src/config/skills.ts +21 -0
  155. package/src/config/sync-gated-profiles.ts +220 -0
  156. package/src/contacts/contact-store.ts +2 -10
  157. package/src/contacts/contacts-write.ts +1 -2
  158. package/src/contacts/types.ts +0 -1
  159. package/src/context/compactor.ts +86 -52
  160. package/src/context/strip-injections.ts +58 -10
  161. package/src/context/token-estimator.ts +1 -1
  162. package/src/daemon/__tests__/conversation-lifecycle-auto-analyze.test.ts +3 -2
  163. package/src/daemon/conversation-agent-loop-handlers.ts +2 -0
  164. package/src/daemon/conversation-agent-loop.ts +100 -19
  165. package/src/daemon/conversation-history.ts +1 -1
  166. package/src/daemon/conversation-lifecycle.ts +3 -5
  167. package/src/daemon/conversation-process.ts +13 -5
  168. package/src/daemon/conversation-runtime-assembly.ts +13 -15
  169. package/src/daemon/conversation-surfaces.ts +26 -0
  170. package/src/daemon/conversation-tool-setup.ts +16 -11
  171. package/src/daemon/conversation.ts +64 -14
  172. package/src/daemon/disk-pressure-policy.ts +5 -3
  173. package/src/daemon/handlers/__tests__/config-a2a-complete.test.ts +0 -1
  174. package/src/daemon/handlers/__tests__/config-a2a-redeem.test.ts +0 -1
  175. package/src/daemon/handlers/config-a2a.ts +0 -2
  176. package/src/daemon/handlers/config-channels.ts +5 -10
  177. package/src/daemon/handlers/config-slack-channel.ts +20 -0
  178. package/src/daemon/handlers/conversations.ts +107 -0
  179. package/src/daemon/host-browser-proxy.ts +41 -0
  180. package/src/daemon/lifecycle.ts +55 -20
  181. package/src/daemon/message-provenance.ts +2 -0
  182. package/src/daemon/message-types/contacts.ts +0 -1
  183. package/src/daemon/message-types/web-activity.ts +7 -1
  184. package/src/daemon/message-types/workflows.ts +83 -1
  185. package/src/daemon/tool-setup-types.ts +4 -0
  186. package/src/daemon/trust-context.ts +1 -1
  187. package/src/events/tool-audit-listener.ts +2 -2
  188. package/src/home/feed-source-enrichment.test.ts +151 -0
  189. package/src/home/feed-source-enrichment.ts +176 -0
  190. package/src/instrument.ts +18 -6
  191. package/src/ipc/__tests__/binary-result-ipc.test.ts +81 -0
  192. package/src/ipc/__tests__/clients-list-ipc.test.ts +20 -0
  193. package/src/ipc/assistant-server.ts +37 -4
  194. package/src/ipc/gateway-flag-listener.ts +18 -2
  195. package/src/memory/__tests__/auto-analysis-enqueue.test.ts +5 -16
  196. package/src/memory/__tests__/jobs-store-enqueue-gate.test.ts +7 -11
  197. package/src/memory/__tests__/memory-retrospective-enqueue.test.ts +37 -7
  198. package/src/memory/__tests__/memory-retrospective-job.test.ts +34 -0
  199. package/src/memory/__tests__/onboarding-events-store.test.ts +7 -7
  200. package/src/memory/auth-fallback-events-store.ts +2 -2
  201. package/src/memory/auto-analysis-enqueue.ts +3 -5
  202. package/src/memory/canonical-guardian-store.ts +39 -1
  203. package/src/memory/conversation-crud.ts +9 -4
  204. package/src/memory/conversation-key-store.ts +17 -2
  205. package/src/memory/conversation-title-service.ts +64 -7
  206. package/src/memory/db-init.ts +10 -0
  207. package/src/memory/embedding-backend.ts +15 -1
  208. package/src/memory/jobs-worker.ts +2 -1
  209. package/src/memory/lifecycle-events-store.ts +2 -2
  210. package/src/memory/memory-retrospective-enqueue.ts +31 -6
  211. package/src/memory/memory-retrospective-job.ts +9 -0
  212. package/src/memory/migrations/129-contact-channels-access-fields.ts +18 -9
  213. package/src/memory/migrations/131-drop-legacy-member-guardian-tables.ts +14 -2
  214. package/src/memory/migrations/289-contact-channels-unique-ext-user.ts +10 -0
  215. package/src/memory/migrations/291-contact-channels-renormalize-addresses.ts +10 -0
  216. package/src/memory/migrations/292-schedule-default-no-reuse-conversation.test.ts +67 -0
  217. package/src/memory/migrations/292-schedule-default-no-reuse-conversation.ts +25 -0
  218. package/src/memory/migrations/293-workflow-journal-leaf-tokens.ts +32 -0
  219. package/src/memory/migrations/294-drop-external-user-id.ts +31 -0
  220. package/src/memory/migrations/295-drop-approval-prompt-ts-tracker.ts +20 -0
  221. package/src/memory/migrations/296-rewrite-balanced-economy-profile-pins.test.ts +110 -0
  222. package/src/memory/migrations/296-rewrite-balanced-economy-profile-pins.ts +68 -0
  223. package/src/memory/migrations/__tests__/131-drop-legacy-member-guardian-tables.test.ts +154 -0
  224. package/src/memory/migrations/__tests__/289-contact-channels-unique-ext-user.test.ts +31 -0
  225. package/src/memory/migrations/__tests__/291-contact-channels-renormalize-addresses.test.ts +30 -0
  226. package/src/memory/migrations/index.ts +5 -0
  227. package/src/memory/onboarding-events-store.ts +3 -3
  228. package/src/memory/schema/contacts.ts +0 -1
  229. package/src/memory/skill-loaded-events-store.test.ts +7 -15
  230. package/src/memory/skill-loaded-events-store.ts +2 -2
  231. package/src/memory/tool-executed-events-store.test.ts +7 -7
  232. package/src/memory/turn-trace-store.test.ts +736 -0
  233. package/src/memory/turn-trace-store.ts +364 -0
  234. package/src/memory/v2/__tests__/consolidation-job.test.ts +8 -0
  235. package/src/memory/v2/__tests__/skill-content.test.ts +30 -0
  236. package/src/memory/v2/consolidation-job.ts +2 -2
  237. package/src/memory/v2/skill-content.ts +25 -7
  238. package/src/memory/v2/skill-store.ts +7 -1
  239. package/src/memory/v3-eval/__tests__/eval-packets.test.ts +248 -0
  240. package/src/memory/v3-eval/eval-packets.ts +546 -0
  241. package/src/messaging/providers/slack/api.ts +31 -0
  242. package/src/messaging/providers/slack/send.test.ts +114 -2
  243. package/src/messaging/providers/slack/send.ts +30 -7
  244. package/src/messaging/providers/slack/withdraw.test.ts +200 -0
  245. package/src/messaging/providers/slack/withdraw.ts +161 -0
  246. package/src/notifications/AGENTS.md +2 -0
  247. package/src/notifications/access-request-copy.ts +72 -59
  248. package/src/notifications/adapters/slack.ts +55 -73
  249. package/src/notifications/approval-card-data.ts +333 -0
  250. package/src/notifications/broadcaster.ts +6 -2
  251. package/src/notifications/canonical-delivery-recorder.ts +139 -0
  252. package/src/notifications/copy-composer.ts +3 -3
  253. package/src/notifications/decision-engine.ts +4 -2
  254. package/src/notifications/destination-resolver.ts +4 -6
  255. package/src/notifications/guardian-question-mode.ts +10 -0
  256. package/src/notifications/home-feed-side-effect.ts +3 -13
  257. package/src/notifications/notification-utils.ts +2 -1
  258. package/src/notifications/signal.ts +79 -43
  259. package/src/notifications/types.ts +98 -128
  260. package/src/permissions/checker.test.ts +51 -0
  261. package/src/permissions/checker.ts +185 -26
  262. package/src/permissions/ipc-risk-types.ts +24 -0
  263. package/src/permissions/question-prompter.test.ts +27 -0
  264. package/src/permissions/question-prompter.ts +4 -0
  265. package/src/platform/client.test.ts +119 -0
  266. package/src/platform/client.ts +66 -0
  267. package/src/platform/consent-cache.test.ts +267 -0
  268. package/src/platform/consent-cache.ts +174 -0
  269. package/src/plugin-api/index.ts +27 -0
  270. package/src/plugins/defaults/advisor/__tests__/advisor-gate.test.ts +56 -0
  271. package/src/plugins/defaults/advisor/__tests__/advisor-state-store.test.ts +43 -0
  272. package/src/plugins/defaults/advisor/__tests__/agent-loop-integration.test.ts +137 -0
  273. package/src/plugins/defaults/advisor/__tests__/consult.test.ts +153 -0
  274. package/src/plugins/defaults/advisor/__tests__/hooks.test.ts +138 -0
  275. package/src/plugins/defaults/advisor/__tests__/transcript.test.ts +147 -0
  276. package/src/plugins/defaults/advisor/advisor-gate.ts +29 -0
  277. package/src/plugins/defaults/advisor/advisor-state-store.ts +94 -0
  278. package/src/plugins/defaults/advisor/config.ts +21 -0
  279. package/src/plugins/defaults/advisor/consult.ts +93 -0
  280. package/src/plugins/defaults/advisor/hooks/post-model-call.ts +34 -0
  281. package/src/plugins/defaults/advisor/hooks/pre-model-call.ts +30 -0
  282. package/src/plugins/defaults/advisor/hooks/user-prompt-submit.ts +19 -0
  283. package/src/plugins/defaults/advisor/package.json +14 -0
  284. package/src/plugins/defaults/advisor/steering.ts +67 -0
  285. package/src/plugins/defaults/advisor/tools/advisor.ts +65 -0
  286. package/src/plugins/defaults/advisor/transcript.ts +76 -0
  287. package/src/plugins/defaults/index.ts +35 -0
  288. package/src/plugins/defaults/memory-retrieval/hooks/post-compact.ts +22 -9
  289. package/src/plugins/defaults/memory-retrieval/hooks/user-prompt-submit.ts +2 -2
  290. package/src/plugins/defaults/memory-retrieval/tail-reinjection-strip.ts +64 -0
  291. package/src/plugins/defaults/memory-retrieval/unified-turn-context.ts +29 -21
  292. package/src/plugins/defaults/memory-v3-shadow/__tests__/carry-integration.test.ts +1 -0
  293. package/src/plugins/defaults/memory-v3-shadow/__tests__/injection.test.ts +1 -0
  294. package/src/plugins/defaults/memory-v3-shadow/__tests__/maintain-job.test.ts +75 -7
  295. package/src/plugins/defaults/memory-v3-shadow/__tests__/orchestrate.test.ts +31 -4
  296. package/src/plugins/defaults/memory-v3-shadow/__tests__/selection-log-store.test.ts +77 -2
  297. package/src/plugins/defaults/memory-v3-shadow/__tests__/shadow-plugin.test.ts +1 -0
  298. package/src/plugins/defaults/memory-v3-shadow/injector.ts +7 -10
  299. package/src/plugins/defaults/memory-v3-shadow/maintain-job.ts +37 -4
  300. package/src/plugins/defaults/memory-v3-shadow/orchestrate.ts +32 -20
  301. package/src/plugins/defaults/memory-v3-shadow/selection-log-store.ts +56 -3
  302. package/src/plugins/defaults/memory-v3-shadow/shadow-plugin.ts +23 -2
  303. package/src/plugins/defaults/task-progress-nudge/hooks/post-tool-use.ts +2 -12
  304. package/src/plugins/defaults/title-generate/hooks/stop.ts +56 -21
  305. package/src/prompts/persona-resolver.ts +12 -2
  306. package/src/prompts/templates/system-sections.ts +7 -2
  307. package/src/providers/__tests__/provider-env-vars.test.ts +6 -0
  308. package/src/providers/__tests__/provider-secret-catalog.test.ts +1 -0
  309. package/src/providers/__tests__/retry-callsite.test.ts +176 -0
  310. package/src/providers/atlascloud/client.ts +85 -0
  311. package/src/providers/fetch-provider-catalog.ts +85 -0
  312. package/src/providers/inference/adapter-factory.ts +3 -0
  313. package/src/providers/model-catalog.ts +58 -0
  314. package/src/providers/openai/__tests__/chat-completions-provider-reasoning.test.ts +33 -0
  315. package/src/providers/openai/chat-completions-provider.ts +7 -0
  316. package/src/providers/openai/responses-provider.ts +10 -0
  317. package/src/providers/provider-send-message.ts +11 -3
  318. package/src/providers/retry.ts +53 -12
  319. package/src/providers/search-provider-catalog.ts +10 -0
  320. package/src/providers/weak-open-model.ts +22 -0
  321. package/src/runtime/__tests__/agent-wake.test.ts +181 -0
  322. package/src/runtime/__tests__/client-health.test.ts +44 -0
  323. package/src/runtime/access-request-helper.ts +21 -53
  324. package/src/runtime/actor-trust-resolver.ts +49 -21
  325. package/src/runtime/agent-wake.ts +52 -0
  326. package/src/runtime/assistant-event-hub.ts +18 -4
  327. package/src/runtime/auth/__tests__/route-policy.test.ts +12 -0
  328. package/src/runtime/auth/require-bound-guardian.ts +1 -4
  329. package/src/runtime/capabilities.test.ts +120 -0
  330. package/src/runtime/capabilities.ts +197 -0
  331. package/src/runtime/channel-approval-types.ts +5 -1
  332. package/src/runtime/channel-retry-sweep.ts +1 -0
  333. package/src/runtime/channel-verification-service.ts +1 -2
  334. package/src/runtime/client-health.ts +26 -0
  335. package/src/runtime/confirmation-request-guardian-bridge.ts +38 -29
  336. package/src/runtime/effective-capabilities.test.ts +128 -0
  337. package/src/runtime/effective-capabilities.ts +84 -0
  338. package/src/runtime/guardian-reply-router.ts +106 -21
  339. package/src/runtime/invite-redemption-service.ts +6 -22
  340. package/src/runtime/migrations/__tests__/vbundle-builder-fd-leak.test.ts +123 -0
  341. package/src/runtime/migrations/vbundle-builder.ts +49 -20
  342. package/src/runtime/pending-interactions.ts +15 -0
  343. package/src/runtime/routes/__tests__/client-routes.test.ts +13 -0
  344. package/src/runtime/routes/__tests__/conversation-management-routes.test.ts +67 -0
  345. package/src/runtime/routes/__tests__/plugins-routes.test.ts +35 -13
  346. package/src/runtime/routes/browser-tabs-routes.ts +9 -0
  347. package/src/runtime/routes/canonical-guardian-expiry-sweep.ts +17 -8
  348. package/src/runtime/routes/client-routes.ts +10 -0
  349. package/src/runtime/routes/contact-routes.ts +31 -8
  350. package/src/runtime/routes/conversation-management-routes.ts +80 -1
  351. package/src/runtime/routes/conversation-query-routes.ts +68 -22
  352. package/src/runtime/routes/conversation-routes.ts +37 -12
  353. package/src/runtime/routes/events-routes.ts +1 -3
  354. package/src/runtime/routes/guardian-approval-interception.ts +14 -73
  355. package/src/runtime/routes/guardian-approval-prompt.ts +22 -4
  356. package/src/runtime/routes/home-feed-routes.ts +8 -3
  357. package/src/runtime/routes/inbound-message-handler.ts +214 -228
  358. package/src/runtime/routes/inbound-stages/acl-enforcement.ts +88 -6
  359. package/src/runtime/routes/inbound-stages/admission-policy.test.ts +154 -0
  360. package/src/runtime/routes/inbound-stages/admission-policy.ts +140 -0
  361. package/src/runtime/routes/inbound-stages/background-dispatch.test.ts +3 -3
  362. package/src/runtime/routes/inbound-stages/background-dispatch.ts +11 -6
  363. package/src/runtime/routes/inbound-stages/escalation-intercept.ts +1 -2
  364. package/src/runtime/routes/inbound-stages/guardian-activation-intercept.ts +1 -2
  365. package/src/runtime/routes/inbound-stages/guardian-reply-intercept.test.ts +7 -7
  366. package/src/runtime/routes/inbound-stages/guardian-reply-intercept.ts +47 -28
  367. package/src/runtime/routes/inbound-stages/reaction-intercept.ts +358 -0
  368. package/src/runtime/routes/index.ts +2 -0
  369. package/src/runtime/routes/integrations/slack/__tests__/channel.test.ts +8 -0
  370. package/src/runtime/routes/integrations/slack/channel.ts +36 -0
  371. package/src/runtime/routes/internal-telemetry-routes.ts +1 -1
  372. package/src/runtime/routes/mcp-auth-routes.ts +233 -41
  373. package/src/runtime/routes/memory-eval-routes.ts +87 -0
  374. package/src/runtime/routes/notification-routes.ts +122 -133
  375. package/src/runtime/routes/platform-routes.ts +2 -2
  376. package/src/runtime/routes/plugins-routes.ts +40 -7
  377. package/src/runtime/routes/secret-routes.ts +10 -0
  378. package/src/runtime/routes/surface-action-routes.ts +2 -1
  379. package/src/runtime/routes/tool-call-question-enrichment.test.ts +146 -0
  380. package/src/runtime/routes/tool-call-question-enrichment.ts +66 -0
  381. package/src/runtime/routes/workflow-routes.test.ts +225 -1
  382. package/src/runtime/routes/workflow-routes.ts +131 -1
  383. package/src/runtime/tool-grant-request-helper.ts +18 -16
  384. package/src/runtime/trust-context-resolver.ts +8 -5
  385. package/src/schedule/schedule-store.ts +1 -1
  386. package/src/schedule/scheduler-types.ts +5 -1
  387. package/src/security/__tests__/provider-key-env-fallback.test.ts +6 -0
  388. package/src/security/secret-patterns.ts +3 -0
  389. package/src/subagent/manager.ts +11 -4
  390. package/src/telemetry/trace-collection-policy.test.ts +28 -0
  391. package/src/telemetry/trace-collection-policy.ts +30 -0
  392. package/src/telemetry/types.ts +89 -0
  393. package/src/telemetry/usage-telemetry-reporter.test.ts +586 -36
  394. package/src/telemetry/usage-telemetry-reporter.ts +148 -41
  395. package/src/tools/browser/__tests__/browser-execution-acquire.test.ts +31 -0
  396. package/src/tools/browser/browser-execution.ts +29 -18
  397. package/src/tools/document/document-tool.ts +2 -3
  398. package/src/tools/executor.ts +5 -3
  399. package/src/tools/host-terminal/host-shell.ts +5 -4
  400. package/src/tools/memory/register.ts +2 -2
  401. package/src/tools/network/__tests__/web-fetch-firecrawl.test.ts +360 -0
  402. package/src/tools/network/__tests__/web-search.test.ts +143 -0
  403. package/src/tools/network/web-fetch.ts +372 -1
  404. package/src/tools/network/web-search.ts +213 -10
  405. package/src/tools/permission-checker.ts +3 -2
  406. package/src/tools/registry.ts +20 -0
  407. package/src/tools/schedule/create.ts +4 -3
  408. package/src/tools/schedule/update.ts +2 -1
  409. package/src/tools/shared/filesystem/path-policy.ts +39 -13
  410. package/src/tools/skills/execute.ts +1 -2
  411. package/src/tools/subagent/spawn.ts +37 -13
  412. package/src/tools/terminal/shell.ts +10 -4
  413. package/src/tools/tool-approval-handler.ts +17 -10
  414. package/src/tools/types.ts +9 -0
  415. package/src/tools/ui-surface/definitions.ts +25 -2
  416. package/src/tools/verification-control-plane-policy.ts +3 -1
  417. package/src/tools/workflows/run-workflow.ts +1 -0
  418. package/src/util/disk-usage.ts +78 -23
  419. package/src/util/platform.ts +8 -1
  420. package/src/watcher/telemetry.ts +2 -2
  421. package/src/workflows/engine.test.ts +175 -1
  422. package/src/workflows/engine.ts +82 -0
  423. package/src/workflows/journal-store.test.ts +70 -0
  424. package/src/workflows/journal-store.ts +18 -3
  425. package/src/workflows/run-manager.test.ts +171 -3
  426. package/src/workflows/run-manager.ts +64 -0
  427. package/src/workspace/migrations/105-enable-memory-v3-live-for-new-workspaces.ts +63 -0
  428. package/src/workspace/migrations/106-drop-collect-usage-data.ts +47 -0
  429. package/src/workspace/migrations/107-drop-send-diagnostics.ts +47 -0
  430. package/src/workspace/migrations/108-drop-balanced-economy-profile.ts +129 -0
  431. package/src/workspace/migrations/registry.ts +8 -0
  432. package/src/notifications/tool-approval-copy.ts +0 -142
  433. package/src/runtime/routes/approval-prompt-ts-tracker.ts +0 -78
@@ -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";
@@ -480,9 +485,11 @@ describe("loadConfig startup behavior", () => {
480
485
  "anthropic-personal",
481
486
  );
482
487
  // Managed profiles exist as well.
483
- 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
+ );
484
491
  expect(config.llm.profiles.balanced?.provider_connection).toBe(
485
- "anthropic-managed",
492
+ "fireworks-managed",
486
493
  );
487
494
 
488
495
  const raw = JSON.parse(readFileSync(CONFIG_PATH, "utf-8"));
@@ -491,7 +498,9 @@ describe("loadConfig startup behavior", () => {
491
498
  model: "claude-opus-4-7",
492
499
  });
493
500
  expect(raw.llm.activeProfile).toBe("custom-balanced");
494
- 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
+ );
495
504
  });
496
505
 
497
506
  test("on-platform hatch seeds only managed profiles", () => {
@@ -519,9 +528,11 @@ describe("loadConfig startup behavior", () => {
519
528
  const config = loadConfig();
520
529
 
521
530
  expect(config.llm.activeProfile).toBe("balanced");
522
- 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
+ );
523
534
  expect(config.llm.profiles.balanced?.provider_connection).toBe(
524
- "anthropic-managed",
535
+ "fireworks-managed",
525
536
  );
526
537
  // No user profiles created on platform.
527
538
  expect(config.llm.profiles["custom-balanced"]).toBeUndefined();
@@ -563,10 +574,10 @@ describe("loadConfig startup behavior", () => {
563
574
  expect(raw.llm.profiles["custom-balanced"].provider_connection).toBe(
564
575
  "anthropic-personal",
565
576
  );
566
- // Managed balanced profile is seeded for anthropic-managed.
567
- 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");
568
579
  expect(raw.llm.profiles.balanced.provider_connection).toBe(
569
- "anthropic-managed",
580
+ "fireworks-managed",
570
581
  );
571
582
  });
572
583
 
@@ -600,9 +611,9 @@ describe("loadConfig startup behavior", () => {
600
611
  const raw = JSON.parse(readFileSync(CONFIG_PATH, "utf-8"));
601
612
  // On-platform: no user profiles created, active resets to managed balanced.
602
613
  expect(raw.llm.activeProfile).toBe("balanced");
603
- expect(raw.llm.profiles.balanced.provider).toBe("anthropic");
614
+ expect(raw.llm.profiles.balanced.provider).toBe("fireworks");
604
615
  expect(raw.llm.profiles.balanced.provider_connection).toBe(
605
- "anthropic-managed",
616
+ "fireworks-managed",
606
617
  );
607
618
  // The old custom-balanced is preserved on disk but no longer active.
608
619
  expect(raw.llm.profiles["custom-balanced"].provider).toBe("openai");
@@ -654,10 +665,10 @@ describe("loadConfig startup behavior", () => {
654
665
  "gpt-5.4-nano",
655
666
  );
656
667
 
657
- // Managed profiles are also seeded (balanced uses Anthropic).
658
- 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");
659
670
  expect(raw.llm.profiles.balanced.provider_connection).toBe(
660
- "anthropic-managed",
671
+ "fireworks-managed",
661
672
  );
662
673
  expect(raw.llm.profiles.balanced.source).toBe("managed");
663
674
  expect(raw.llm.profiles["quality-optimized"].provider).toBe("anthropic");
@@ -685,9 +696,11 @@ describe("loadConfig startup behavior", () => {
685
696
  mergeDefaultConfigAndSeedInferenceProfiles();
686
697
  const raw = JSON.parse(readFileSync(CONFIG_PATH, "utf-8"));
687
698
 
688
- 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
+ );
689
702
  expect(raw.llm.profiles.balanced.provider_connection).toBe(
690
- "anthropic-managed",
703
+ "fireworks-managed",
691
704
  );
692
705
  expect(raw.llm.activeProfile).toBe("balanced");
693
706
  });
@@ -717,10 +730,13 @@ describe("loadConfig startup behavior", () => {
717
730
  mergeDefaultConfigAndSeedInferenceProfiles();
718
731
  const raw = JSON.parse(readFileSync(CONFIG_PATH, "utf-8"));
719
732
 
720
- expect(raw.llm.profiles.balanced.model).toBe("claude-sonnet-4-6");
721
- expect(raw.llm.profiles.balanced.maxTokens).toBe(16000);
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);
722
738
  expect(raw.llm.profiles.balanced.provider_connection).toBe(
723
- "anthropic-managed",
739
+ "fireworks-managed",
724
740
  );
725
741
  expect(raw.llm.activeProfile).toBe("balanced");
726
742
  });
@@ -750,7 +766,9 @@ describe("loadConfig startup behavior", () => {
750
766
  const raw = JSON.parse(readFileSync(CONFIG_PATH, "utf-8"));
751
767
 
752
768
  // Content refreshes from the template...
753
- expect(raw.llm.profiles.balanced.model).toBe("claude-sonnet-4-6");
769
+ expect(raw.llm.profiles.balanced.model).toBe(
770
+ "accounts/fireworks/models/minimax-m3",
771
+ );
754
772
  // ...but the user's label and status overrides are preserved.
755
773
  expect(raw.llm.profiles.balanced.label).toBe("My Default");
756
774
  expect(raw.llm.profiles.balanced.status).toBe("disabled");
@@ -778,7 +796,9 @@ describe("loadConfig startup behavior", () => {
778
796
  const raw = JSON.parse(readFileSync(CONFIG_PATH, "utf-8"));
779
797
 
780
798
  // Model still gets the new template value (provider-controlled).
781
- 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
+ );
782
802
  // But the user's label override is preserved across the reseed.
783
803
  expect(raw.llm.profiles.balanced.label).toBe("My Default");
784
804
  });
@@ -806,7 +826,51 @@ describe("loadConfig startup behavior", () => {
806
826
 
807
827
  expect(raw.llm.profiles.balanced.status).toBe("disabled");
808
828
  // Model still refreshes — only label/status are user-owned.
809
- 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);
810
874
  });
811
875
 
812
876
  test("off-platform reseed preserves an explicit null label (user cleared it)", () => {
@@ -847,7 +911,9 @@ describe("loadConfig startup behavior", () => {
847
911
  const raw = JSON.parse(readFileSync(CONFIG_PATH, "utf-8"));
848
912
 
849
913
  expect(raw.llm.profiles.balanced.label).toBe("Balanced (Managed)");
850
- 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
+ );
851
917
  // Status is unset by default — must not appear as `undefined`.
852
918
  expect("status" in raw.llm.profiles.balanced).toBe(false);
853
919
  });
@@ -933,18 +999,20 @@ describe("loadConfig startup behavior", () => {
933
999
  expect(raw.llm.profiles.balanced.maxTokens).toBeUndefined();
934
1000
  expect(raw.llm.profiles.balanced.thinking).toBeUndefined();
935
1001
 
936
- // Next boot, no overlay: content reconciles to the anthropic-managed code
1002
+ // Next boot, no overlay: content reconciles to the fireworks-managed code
937
1003
  // template; only the overlay-set label is carried across.
938
1004
  mergeDefaultConfigAndSeedInferenceProfiles();
939
1005
 
940
1006
  const afterRestart = JSON.parse(readFileSync(CONFIG_PATH, "utf-8"));
941
1007
  expect(afterRestart.llm.activeProfile).toBe("balanced");
942
- expect(afterRestart.llm.profiles.balanced.provider).toBe("anthropic");
1008
+ expect(afterRestart.llm.profiles.balanced.provider).toBe("fireworks");
943
1009
  expect(afterRestart.llm.profiles.balanced.provider_connection).toBe(
944
- "anthropic-managed",
1010
+ "fireworks-managed",
1011
+ );
1012
+ expect(afterRestart.llm.profiles.balanced.model).toBe(
1013
+ "accounts/fireworks/models/minimax-m3",
945
1014
  );
946
- expect(afterRestart.llm.profiles.balanced.model).toBe("claude-sonnet-4-6");
947
- expect(afterRestart.llm.profiles.balanced.maxTokens).toBe(16000);
1015
+ expect(afterRestart.llm.profiles.balanced.maxTokens).toBe(32000);
948
1016
  expect(afterRestart.llm.profiles.balanced.thinking).toEqual({
949
1017
  enabled: true,
950
1018
  streamThinking: true,
@@ -989,7 +1057,9 @@ describe("loadConfig startup behavior", () => {
989
1057
  // Off-platform hatch: user profiles are active.
990
1058
  expect(raw.llm.activeProfile).toBe("custom-balanced");
991
1059
  expect(raw.llm.profiles["custom-balanced"].provider).toBe("anthropic");
992
- 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
+ );
993
1063
  });
994
1064
 
995
1065
  test("still quarantines corrupt JSON", () => {
@@ -1086,6 +1156,11 @@ describe("seedInferenceProfiles BYOK-mode managed profile labels", () => {
1086
1156
 
1087
1157
  // Personal profiles keep their bare labels — they're the daily driver.
1088
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();
1089
1164
  });
1090
1165
 
1091
1166
  test("off-platform hatch initializes managed profile status to 'disabled'", () => {
@@ -1133,7 +1208,7 @@ describe("seedInferenceProfiles BYOK-mode managed profile labels", () => {
1133
1208
 
1134
1209
  expect(raw.llm.activeProfile).toBe("balanced");
1135
1210
  expect(raw.llm.profiles.balanced.provider_connection).toBe(
1136
- "anthropic-managed",
1211
+ "fireworks-managed",
1137
1212
  );
1138
1213
  expect("status" in raw.llm.profiles.balanced).toBe(false);
1139
1214
  // Connections exist (status is no longer a connection-level concept).
@@ -1380,3 +1455,72 @@ describe("seedInferenceProfiles BYOK-mode managed profile labels", () => {
1380
1455
  expect(config.llm.profiles.balanced?.label).toBe("Balanced (Managed)");
1381
1456
  });
1382
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
+ });
@@ -145,6 +145,40 @@ describe("AssistantConfigSchema", () => {
145
145
  expect(result.services["web-search"].mode).toBe("your-own");
146
146
  });
147
147
 
148
+ test("accepts Firecrawl as a web search provider", () => {
149
+ const result = AssistantConfigSchema.parse({
150
+ services: {
151
+ "web-search": { mode: "your-own", provider: "firecrawl" },
152
+ },
153
+ });
154
+
155
+ expect(result.services["web-search"].provider).toBe("firecrawl");
156
+ expect(result.services["web-search"].mode).toBe("your-own");
157
+ });
158
+
159
+ test("defaults the web-fetch provider to the built-in fetcher", () => {
160
+ const result = AssistantConfigSchema.parse({});
161
+ expect(result.services["web-fetch"].provider).toBe("default");
162
+ });
163
+
164
+ test("accepts Firecrawl as a web fetch provider", () => {
165
+ const result = AssistantConfigSchema.parse({
166
+ services: {
167
+ "web-fetch": { mode: "your-own", provider: "firecrawl" },
168
+ },
169
+ });
170
+
171
+ expect(result.services["web-fetch"].provider).toBe("firecrawl");
172
+ });
173
+
174
+ test("rejects an unknown web-fetch provider", () => {
175
+ expect(() =>
176
+ AssistantConfigSchema.parse({
177
+ services: { "web-fetch": { provider: "nope" } },
178
+ }),
179
+ ).toThrow();
180
+ });
181
+
148
182
  test("accepts valid complete config", () => {
149
183
  const input = {
150
184
  llm: {
@@ -185,6 +219,7 @@ describe("AssistantConfigSchema", () => {
185
219
  speed: "standard",
186
220
  verbosity: "medium",
187
221
  temperature: null,
222
+ topP: null,
188
223
  thinking: { enabled: true, streamThinking: true },
189
224
  contextWindow: {
190
225
  enabled: true,
@@ -178,7 +178,7 @@ describe("bridgeConfirmationRequestToGuardian", () => {
178
178
 
179
179
  expect("skipped" in result && result.skipped).toBe(true);
180
180
  if ("skipped" in result) {
181
- expect(result.reason).toBe("not_trusted_contact");
181
+ expect(result.reason).toBe("not_bridgeable_trust_class");
182
182
  }
183
183
  expect(emittedSignals).toHaveLength(0);
184
184
  });
@@ -199,7 +199,7 @@ describe("bridgeConfirmationRequestToGuardian", () => {
199
199
 
200
200
  expect("skipped" in result && result.skipped).toBe(true);
201
201
  if ("skipped" in result) {
202
- expect(result.reason).toBe("not_trusted_contact");
202
+ expect(result.reason).toBe("not_bridgeable_trust_class");
203
203
  }
204
204
  expect(emittedSignals).toHaveLength(0);
205
205
  });