@vellumai/assistant 0.8.12 → 0.9.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 (442) hide show
  1. package/AGENTS.md +0 -14
  2. package/ARCHITECTURE.md +45 -45
  3. package/README.md +1 -1
  4. package/bun.lock +200 -154
  5. package/docs/architecture/integrations.md +3 -3
  6. package/docs/architecture/memory.md +2 -2
  7. package/docs/architecture/security.md +10 -10
  8. package/docs/runbook-trusted-contacts.md +12 -12
  9. package/docs/skills.md +6 -6
  10. package/docs/workflows-testing.md +221 -0
  11. package/docs/workflows.md +510 -0
  12. package/examples/plugins/echo/README.md +5 -5
  13. package/knip.json +2 -0
  14. package/node_modules/@vellumai/gateway-client/src/inbound-contract.ts +105 -0
  15. package/node_modules/@vellumai/gateway-client/src/index.ts +12 -0
  16. package/openapi.yaml +7197 -5708
  17. package/package.json +8 -4
  18. package/scripts/generate-openapi.ts +66 -114
  19. package/src/__tests__/access-request-seed-content-blocks.test.ts +213 -0
  20. package/src/__tests__/adaptive-thinking-repair.test.ts +32 -3
  21. package/src/__tests__/agent-loop-output-hooks.test.ts +183 -0
  22. package/src/__tests__/agent-loop-regrowth-guard.test.ts +506 -0
  23. package/src/__tests__/agent-wake-disk-pressure-callsite.test.ts +2 -0
  24. package/src/__tests__/agent-wake-override-profile.test.ts +77 -0
  25. package/src/__tests__/app-compiler.test.ts +7 -1
  26. package/src/__tests__/app-dir-path-guard.test.ts +27 -3
  27. package/src/__tests__/app-executors.test.ts +43 -0
  28. package/src/__tests__/approval-cascade.test.ts +0 -5
  29. package/src/__tests__/approval-routes-http.test.ts +91 -0
  30. package/src/__tests__/assistant-stream-state.test.ts +107 -0
  31. package/src/__tests__/browser-fill-credential.test.ts +3 -3
  32. package/src/__tests__/bundled-skill-retrieval-guard.test.ts +1 -1
  33. package/src/__tests__/compaction-events.test.ts +63 -7
  34. package/src/__tests__/compaction-trail-store.test.ts +74 -1
  35. package/src/__tests__/compaction.benchmark.test.ts +63 -41
  36. package/src/__tests__/compactor-low-watermark-cut.test.ts +349 -0
  37. package/src/__tests__/context-window-manager-compact-retry.test.ts +64 -0
  38. package/src/__tests__/conversation-abort-tool-results.test.ts +0 -5
  39. package/src/__tests__/conversation-confirmation-signals.test.ts +0 -5
  40. package/src/__tests__/conversation-history-web-search.test.ts +7 -0
  41. package/src/__tests__/conversation-process-callsite.test.ts +0 -5
  42. package/src/__tests__/conversation-provider-retry-repair.test.ts +0 -5
  43. package/src/__tests__/conversation-queue.test.ts +0 -5
  44. package/src/__tests__/conversation-slash-queue.test.ts +0 -5
  45. package/src/__tests__/conversation-slash-unknown.test.ts +0 -5
  46. package/src/__tests__/conversation-speed-override.test.ts +0 -5
  47. package/src/__tests__/conversation-surfaces-data-persist.test.ts +97 -0
  48. package/src/__tests__/conversation-surfaces-task-progress.test.ts +67 -0
  49. package/src/__tests__/conversation-usage.test.ts +2 -0
  50. package/src/__tests__/conversation-workspace-injection.test.ts +0 -5
  51. package/src/__tests__/conversation-workspace-tool-tracking.test.ts +0 -5
  52. package/src/__tests__/credential-broker-browser-fill.test.ts +2 -2
  53. package/src/__tests__/credential-broker-server-use.test.ts +2 -2
  54. package/src/__tests__/credential-broker.test.ts +1 -1
  55. package/src/__tests__/credential-prompt-route.test.ts +417 -0
  56. package/src/__tests__/credential-security-invariants.test.ts +1 -0
  57. package/src/__tests__/credential-vault.test.ts +37 -0
  58. package/src/__tests__/db-schedule-syntax-migration.test.ts +24 -0
  59. package/src/__tests__/dynamic-page-surface.test.ts +219 -0
  60. package/src/__tests__/empty-state-greeting-cache.test.ts +94 -0
  61. package/src/__tests__/gateway-flag-listener.test.ts +24 -7
  62. package/src/__tests__/guardian-action-sweep.test.ts +56 -219
  63. package/src/__tests__/guardian-routing-invariants.test.ts +138 -0
  64. package/src/__tests__/helpers/channel-test-adapter.ts +0 -2
  65. package/src/__tests__/list-messages-hidden-metadata.test.ts +99 -0
  66. package/src/__tests__/llm-request-log-source-clickhouse.test.ts +87 -1
  67. package/src/__tests__/llm-resolver.test.ts +115 -0
  68. package/src/__tests__/managed-profile-guard.test.ts +6 -5
  69. package/src/__tests__/max-tokens-continue-hook.test.ts +184 -0
  70. package/src/__tests__/media-generate-image.test.ts +20 -9
  71. package/src/__tests__/mock-gateway-ipc.ts +23 -0
  72. package/src/__tests__/model-intents.test.ts +1 -1
  73. package/src/__tests__/normalize-onboarding.test.ts +26 -0
  74. package/src/__tests__/notification-decision-strategy.test.ts +4 -2
  75. package/src/__tests__/notification-telegram-adapter.test.ts +21 -3
  76. package/src/__tests__/pending-interactions-resolved-event.test.ts +62 -0
  77. package/src/__tests__/post-turn-tool-result-truncation.test.ts +72 -18
  78. package/src/__tests__/require-fresh-approval.test.ts +425 -1
  79. package/src/__tests__/resolve-app-id.test.ts +56 -0
  80. package/src/__tests__/runtime-events-sse-parity.test.ts +2 -0
  81. package/src/__tests__/schedule-routes-workflow-validation.test.ts +408 -0
  82. package/src/__tests__/schedule-routes.test.ts +257 -4
  83. package/src/__tests__/schedule-store.test.ts +60 -0
  84. package/src/__tests__/schedule-tools.test.ts +247 -2
  85. package/src/__tests__/skill-execute-input.test.ts +85 -0
  86. package/src/__tests__/skill-secret-handling-guard.test.ts +21 -20
  87. package/src/__tests__/skills.test.ts +3 -3
  88. package/src/__tests__/slack-app-setup-skill-regression.test.ts +1 -1
  89. package/src/__tests__/subagent-tool-filtering.test.ts +50 -0
  90. package/src/__tests__/subagent-tool-gate-mode.test.ts +547 -0
  91. package/src/__tests__/system-prompt.test.ts +1 -1
  92. package/src/__tests__/task-progress-nudge-hook.test.ts +372 -0
  93. package/src/__tests__/task-scheduler.test.ts +299 -0
  94. package/src/__tests__/tool-approval-seed-content-blocks.test.ts +209 -0
  95. package/src/__tests__/tool-result-spool.test.ts +3 -1
  96. package/src/__tests__/workspace-migration-102-preserve-heartbeat-enabled-for-existing-workspaces.test.ts +181 -0
  97. package/src/__tests__/workspace-migration-103-upgrade-quality-profile-to-opus-4-8.test.ts +174 -0
  98. package/src/agent/compaction-circuit.ts +11 -0
  99. package/src/agent/loop.ts +181 -12
  100. package/src/api/constants/call-sites.ts +12 -0
  101. package/src/api/events/assistant-thinking-delta.ts +10 -0
  102. package/src/api/events/usage-update.ts +7 -0
  103. package/src/api/index.ts +4 -1
  104. package/src/api/responses/memory-v3-selection-log.ts +18 -11
  105. package/src/approvals/approval-primitive.ts +2 -2
  106. package/src/background-wake/background-wake-routes.test.ts +5 -2
  107. package/src/bundler/compiler-tools.ts +1 -1
  108. package/src/bundler/package-resolver.ts +0 -1
  109. package/src/calls/call-domain.ts +1 -1
  110. package/src/calls/guardian-action-sweep.ts +16 -93
  111. package/src/cli/AGENTS.md +4 -0
  112. package/src/cli/commands/__tests__/schedules.test.ts +430 -1
  113. package/src/cli/commands/credentials.ts +28 -24
  114. package/src/cli/commands/image-generation.ts +23 -9
  115. package/src/cli/commands/notifications.ts +1 -1
  116. package/src/cli/commands/plugins.ts +89 -46
  117. package/src/cli/commands/schedules.ts +384 -11
  118. package/src/cli/lib/__tests__/inspect-plugin.test.ts +69 -5
  119. package/src/cli/lib/__tests__/install-from-github.test.ts +15 -0
  120. package/src/cli/lib/__tests__/upgrade-plugin.test.ts +81 -4
  121. package/src/cli/lib/inspect-plugin.ts +62 -1
  122. package/src/cli/lib/install-from-github.ts +52 -5
  123. package/src/cli/lib/upgrade-plugin.ts +18 -0
  124. package/src/config/__tests__/workflows-schema.test.ts +60 -0
  125. package/src/config/bundled-skills/acp/SKILL.md +2 -2
  126. package/src/config/bundled-skills/app-builder/SKILL.md +1 -1
  127. package/src/config/bundled-skills/app-builder/tools/app-create.ts +6 -1
  128. package/src/config/bundled-skills/app-builder/tools/app-generate-icon.ts +7 -1
  129. package/src/config/bundled-skills/app-builder/tools/app-refresh.ts +7 -1
  130. package/src/config/bundled-skills/app-builder/tools/app-update.ts +10 -1
  131. package/src/config/bundled-skills/image-studio/SKILL.md +66 -19
  132. package/src/config/bundled-skills/image-studio/TOOLS.json +1 -6
  133. package/src/config/bundled-skills/image-studio/tools/media-generate-image.ts +22 -3
  134. package/src/config/bundled-skills/personal-page/SKILL.md +57 -0
  135. package/src/config/bundled-skills/personal-page/TOOLS.json +27 -0
  136. package/src/config/bundled-skills/personal-page/tools/app-refresh.ts +17 -0
  137. package/src/config/bundled-skills/schedule/SKILL.md +7 -2
  138. package/src/config/bundled-skills/schedule/TOOLS.json +48 -4
  139. package/src/config/bundled-skills/workflows/SKILL.md +214 -0
  140. package/src/config/bundled-skills/workflows/TOOLS.json +84 -0
  141. package/src/config/bundled-skills/workflows/tools/manage-workflows.ts +12 -0
  142. package/src/config/bundled-skills/workflows/tools/run-workflow.ts +12 -0
  143. package/src/config/bundled-tool-registry.ts +14 -2
  144. package/src/config/call-site-defaults.ts +5 -0
  145. package/src/config/feature-flag-registry.json +12 -4
  146. package/src/config/llm-context-resolution.ts +8 -0
  147. package/src/config/llm-resolver.ts +30 -0
  148. package/src/config/preloaded-apps/personal-page/src/components/About.tsx +22 -0
  149. package/src/config/preloaded-apps/personal-page/src/components/App.tsx +16 -0
  150. package/src/config/preloaded-apps/personal-page/src/components/Features.tsx +77 -0
  151. package/src/config/preloaded-apps/personal-page/src/components/Hero.tsx +57 -0
  152. package/src/config/preloaded-apps/personal-page/src/components/Pending.tsx +28 -0
  153. package/src/config/preloaded-apps/personal-page/src/components/animations.tsx +234 -0
  154. package/src/config/preloaded-apps/personal-page/src/components/icons.tsx +48 -0
  155. package/src/config/preloaded-apps/personal-page/src/components/media.ts +16 -0
  156. package/src/config/preloaded-apps/personal-page/src/index.html +20 -0
  157. package/src/config/preloaded-apps/personal-page/src/main.tsx +7 -0
  158. package/src/config/preloaded-apps/personal-page/src/profile-data.ts +82 -0
  159. package/src/config/preloaded-apps/personal-page/src/styles.css +759 -0
  160. package/src/config/schema.ts +2 -0
  161. package/src/config/schemas/call-site-catalog.ts +7 -0
  162. package/src/config/schemas/heartbeat.ts +4 -1
  163. package/src/config/schemas/llm.ts +33 -26
  164. package/src/config/schemas/memory-retrospective.ts +19 -0
  165. package/src/config/schemas/platform.ts +8 -0
  166. package/src/config/schemas/services.ts +5 -2
  167. package/src/config/schemas/workflows.ts +42 -0
  168. package/src/config/skills.ts +3 -3
  169. package/src/context/compactor.ts +273 -39
  170. package/src/context/post-turn-tool-result-truncation.ts +23 -6
  171. package/src/context/tool-result-spool.ts +12 -17
  172. package/src/credential-execution/executable-discovery.ts +1 -1
  173. package/src/credential-execution/process-manager.ts +37 -3
  174. package/src/credential-execution/prompted-credential.ts +205 -0
  175. package/src/daemon/conversation-agent-loop-handlers.ts +14 -0
  176. package/src/daemon/conversation-process.ts +11 -2
  177. package/src/daemon/conversation-surfaces.ts +167 -3
  178. package/src/daemon/conversation-tool-setup.ts +103 -26
  179. package/src/daemon/conversation-usage.ts +2 -0
  180. package/src/daemon/conversation.ts +115 -11
  181. package/src/daemon/handlers/shared.ts +26 -14
  182. package/src/daemon/host-cu-proxy.ts +15 -12
  183. package/src/daemon/host-file-proxy.ts +15 -12
  184. package/src/daemon/host-transfer-proxy.ts +30 -24
  185. package/src/daemon/lifecycle.ts +40 -3
  186. package/src/daemon/message-protocol.ts +3 -0
  187. package/src/daemon/message-types/messages.ts +2 -10
  188. package/src/daemon/message-types/workflows.ts +49 -0
  189. package/src/daemon/parse-actual-tokens-from-error.test.ts +62 -1
  190. package/src/daemon/parse-actual-tokens-from-error.ts +43 -4
  191. package/src/daemon/process-message.ts +6 -0
  192. package/src/daemon/tool-setup-types.ts +57 -0
  193. package/src/daemon/wake-conversation-ops.ts +18 -0
  194. package/src/heartbeat/heartbeat-run-store.ts +8 -2
  195. package/src/home/feed-types.ts +1 -1
  196. package/src/ipc/gateway-flag-listener.ts +28 -6
  197. package/src/mcp/mcp-auth-state.ts +8 -20
  198. package/src/media/__tests__/image-models.test.ts +57 -0
  199. package/src/media/image-models.ts +66 -0
  200. package/src/memory/__tests__/auto-analysis-enqueue.test.ts +38 -0
  201. package/src/memory/__tests__/find-most-recent-retrospective-for.test.ts +12 -2
  202. package/src/memory/__tests__/memory-retrospective-job.test.ts +911 -34
  203. package/src/memory/__tests__/memory-retrospective-startup-cleanup.test.ts +227 -5
  204. package/src/memory/__tests__/memory-retrospective-state.test.ts +195 -0
  205. package/src/memory/__tests__/preloaded-apps.test.ts +85 -0
  206. package/src/memory/auto-analysis-enqueue.ts +14 -1
  207. package/src/memory/compaction-log-store-clickhouse.ts +6 -4
  208. package/src/memory/conversation-crud.ts +9 -2
  209. package/src/memory/conversation-disk-view.ts +1 -1
  210. package/src/memory/conversation-queries.ts +22 -7
  211. package/src/memory/db-init.ts +20 -0
  212. package/src/memory/db-maintenance.ts +16 -0
  213. package/src/memory/embedding-runtime-manager.ts +1 -1
  214. package/src/memory/llm-request-log-source-clickhouse.ts +112 -14
  215. package/src/memory/llm-request-log-source-local.ts +19 -1
  216. package/src/memory/llm-request-log-source.ts +35 -6
  217. package/src/memory/llm-request-log-store.ts +90 -2
  218. package/src/memory/memory-retrospective-constants.ts +9 -0
  219. package/src/memory/memory-retrospective-enqueue.ts +3 -6
  220. package/src/memory/memory-retrospective-fork-boundary.ts +94 -0
  221. package/src/memory/memory-retrospective-job.ts +500 -208
  222. package/src/memory/memory-retrospective-startup-cleanup.ts +97 -19
  223. package/src/memory/memory-retrospective-state.ts +85 -2
  224. package/src/memory/migrations/281-memory-retrospective-remembered-log.ts +40 -0
  225. package/src/memory/migrations/282-schedule-inference-profile.test.ts +77 -0
  226. package/src/memory/migrations/282-schedule-inference-profile.ts +26 -0
  227. package/src/memory/migrations/283-memory-v3-selections-message-id-and-sections.test.ts +102 -0
  228. package/src/memory/migrations/283-memory-v3-selections-message-id-and-sections.ts +53 -0
  229. package/src/memory/migrations/284-workflow-runs.ts +51 -0
  230. package/src/memory/migrations/285-schedule-workflow-mode.ts +26 -0
  231. package/src/memory/migrations/286-workflow-run-trust.ts +27 -0
  232. package/src/memory/migrations/287-conversation-origin-channel-index.ts +15 -0
  233. package/src/memory/migrations/288-backfill-origin-channel-from-bindings.ts +43 -0
  234. package/src/memory/migrations/289-contact-channels-unique-ext-user.ts +115 -0
  235. package/src/memory/migrations/290-schedule-capabilities.test.ts +77 -0
  236. package/src/memory/migrations/290-schedule-capabilities.ts +25 -0
  237. package/src/memory/migrations/__tests__/281-memory-retrospective-remembered-log.test.ts +96 -0
  238. package/src/memory/migrations/__tests__/289-contact-channels-unique-ext-user.test.ts +571 -0
  239. package/src/memory/migrations/index.ts +10 -0
  240. package/src/memory/preloaded-apps.ts +116 -0
  241. package/src/memory/schema/infrastructure.ts +4 -0
  242. package/src/memory/schema/memory-core.ts +4 -0
  243. package/src/memory/v2/__tests__/concept-page-frontmatter-schema.test.ts +45 -0
  244. package/src/memory/v2/__tests__/frontmatter-sweep.test.ts +11 -7
  245. package/src/memory/v2/__tests__/page-store.test.ts +13 -2
  246. package/src/memory/v2/__tests__/qdrant.test.ts +24 -0
  247. package/src/memory/v2/frontmatter-sweep.ts +7 -6
  248. package/src/memory/v2/page-store.ts +4 -3
  249. package/src/memory/v2/qdrant.ts +42 -3
  250. package/src/memory/v2/types.ts +16 -10
  251. package/src/messaging/draft-store.ts +1 -1
  252. package/src/notifications/access-request-copy.ts +200 -113
  253. package/src/notifications/adapters/slack.ts +250 -111
  254. package/src/notifications/adapters/telegram.ts +7 -44
  255. package/src/notifications/approval-card-builder.ts +93 -0
  256. package/src/notifications/broadcaster.ts +74 -0
  257. package/src/notifications/conversation-pairing.ts +8 -6
  258. package/src/notifications/copy-composer.ts +32 -26
  259. package/src/notifications/decision-engine.ts +59 -7
  260. package/src/notifications/guardian-question-mode.ts +145 -155
  261. package/src/notifications/home-feed-side-effect.ts +28 -11
  262. package/src/notifications/notification-utils.ts +66 -0
  263. package/src/notifications/signal.ts +6 -0
  264. package/src/notifications/tool-approval-copy.ts +142 -0
  265. package/src/notifications/types.ts +19 -0
  266. package/src/permissions/threshold.ts +11 -0
  267. package/src/plugin-api/types.ts +16 -4
  268. package/src/plugins/defaults/compaction/window-manager.ts +44 -0
  269. package/src/plugins/defaults/index.ts +46 -0
  270. package/src/plugins/defaults/max-tokens-continue/continue-state-store.ts +53 -0
  271. package/src/plugins/defaults/max-tokens-continue/hooks/post-model-call.ts +80 -0
  272. package/src/plugins/defaults/max-tokens-continue/hooks/stop.ts +20 -0
  273. package/src/plugins/defaults/max-tokens-continue/package.json +14 -0
  274. package/src/plugins/defaults/memory-retrieval/hooks/__tests__/user-prompt-submit.test.ts +37 -0
  275. package/src/plugins/defaults/memory-retrieval/hooks/user-prompt-submit.ts +29 -1
  276. package/src/plugins/defaults/memory-v3-shadow/__tests__/carry-integration.test.ts +8 -3
  277. package/src/plugins/defaults/memory-v3-shadow/__tests__/injection.test.ts +4 -2
  278. package/src/plugins/defaults/memory-v3-shadow/__tests__/pool-select.test.ts +28 -18
  279. package/src/plugins/defaults/memory-v3-shadow/__tests__/section-dense-store.test.ts +67 -0
  280. package/src/plugins/defaults/memory-v3-shadow/__tests__/selection-log-store.test.ts +122 -22
  281. package/src/plugins/defaults/memory-v3-shadow/__tests__/shadow-integration.test.ts +2 -0
  282. package/src/plugins/defaults/memory-v3-shadow/__tests__/shadow-plugin.test.ts +63 -1
  283. package/src/plugins/defaults/memory-v3-shadow/injector.ts +61 -18
  284. package/src/plugins/defaults/memory-v3-shadow/orchestrate.ts +1 -1
  285. package/src/plugins/defaults/memory-v3-shadow/pool-select.ts +39 -10
  286. package/src/plugins/defaults/memory-v3-shadow/section-dense-store.ts +34 -1
  287. package/src/plugins/defaults/memory-v3-shadow/selection-log-store.ts +112 -47
  288. package/src/plugins/defaults/memory-v3-shadow/shadow-plugin.ts +78 -15
  289. package/src/plugins/defaults/task-progress-nudge/hooks/post-tool-use.ts +206 -0
  290. package/src/plugins/defaults/task-progress-nudge/package.json +15 -0
  291. package/src/prompts/__tests__/system-prompt.test.ts +100 -1
  292. package/src/prompts/__tests__/task-progress-hint-section.test.ts +5 -7
  293. package/src/prompts/normalize-onboarding.ts +2 -0
  294. package/src/prompts/persona-resolver.ts +3 -0
  295. package/src/prompts/system-prompt.ts +51 -2
  296. package/src/prompts/templates/BOOTSTRAP-ACTIVATION-RAIL.md +3 -1
  297. package/src/prompts/templates/system-sections.ts +8 -3
  298. package/src/providers/call-site-routing.ts +6 -3
  299. package/src/providers/fireworks/client.ts +3 -0
  300. package/src/providers/inference/auth.ts +52 -46
  301. package/src/providers/model-intents.ts +2 -2
  302. package/src/providers/openai/__tests__/coerce-object-args.test.ts +105 -0
  303. package/src/providers/openai/chat-completions-provider.ts +47 -9
  304. package/src/providers/openai/coerce-object-args.ts +104 -0
  305. package/src/providers/retry.ts +8 -5
  306. package/src/providers/types.ts +10 -0
  307. package/src/runtime/__tests__/agent-wake.test.ts +629 -7
  308. package/src/runtime/access-request-helper.ts +16 -9
  309. package/src/runtime/agent-wake.ts +302 -51
  310. package/src/runtime/assistant-stream-state.ts +141 -8
  311. package/src/runtime/background-job-runner.ts +9 -0
  312. package/src/runtime/channel-approval-types.ts +1 -0
  313. package/src/runtime/channel-invite-transports/telegram.ts +6 -5
  314. package/src/runtime/channel-invite-transports/voice.ts +2 -2
  315. package/src/runtime/channel-invite-types.ts +4 -2
  316. package/src/runtime/channel-retry-sweep.ts +19 -41
  317. package/src/runtime/finalize-event-delivery.ts +72 -0
  318. package/src/runtime/guardian-action-message-composer.ts +0 -54
  319. package/src/runtime/http-server.ts +6 -14
  320. package/src/runtime/http-types.ts +0 -1
  321. package/src/runtime/message-composer-types.ts +0 -9
  322. package/src/runtime/middleware/__tests__/rate-limiter.test.ts +63 -0
  323. package/src/runtime/middleware/auth.ts +27 -3
  324. package/src/runtime/middleware/rate-limiter.ts +28 -1
  325. package/src/runtime/migrations/vbundle-builder.ts +6 -5
  326. package/src/runtime/pending-interactions.ts +20 -1
  327. package/src/runtime/routes/__tests__/conversation-compaction-routes.test.ts +232 -173
  328. package/src/runtime/routes/__tests__/plugins-routes.test.ts +18 -0
  329. package/src/runtime/routes/__tests__/retrospective-routes.test.ts +436 -0
  330. package/src/runtime/routes/__tests__/surface-action-routes.test.ts +11 -0
  331. package/src/runtime/routes/approval-routes.ts +35 -8
  332. package/src/runtime/routes/approval-strategies/guardian-callback-strategy.ts +2 -1
  333. package/src/runtime/routes/btw-routes.ts +37 -1
  334. package/src/runtime/routes/channel-delivery-routes.ts +11 -7
  335. package/src/runtime/routes/channel-route-definitions.ts +3 -0
  336. package/src/runtime/routes/channel-route-shared.ts +3 -1
  337. package/src/runtime/routes/consolidation-routes.ts +17 -13
  338. package/src/runtime/routes/conversation-compaction-routes.ts +159 -119
  339. package/src/runtime/routes/conversation-list-routes.ts +41 -4
  340. package/src/runtime/routes/conversation-query-routes.ts +196 -11
  341. package/src/runtime/routes/conversation-routes.ts +15 -1
  342. package/src/runtime/routes/credential-prompt-routes.ts +39 -17
  343. package/src/runtime/routes/empty-state-greeting-cache.ts +65 -0
  344. package/src/runtime/routes/heartbeat-routes.ts +18 -13
  345. package/src/runtime/routes/image-generation-routes.ts +20 -2
  346. package/src/runtime/routes/inbound-message-handler.ts +13 -12
  347. package/src/runtime/routes/inbound-stages/acl-enforcement.ts +32 -31
  348. package/src/runtime/routes/inbound-stages/background-dispatch.test.ts +7 -5
  349. package/src/runtime/routes/inbound-stages/background-dispatch.ts +7 -21
  350. package/src/runtime/routes/inbound-stages/escalation-intercept.ts +5 -5
  351. package/src/runtime/routes/inbound-stages/guardian-activation-intercept.ts +6 -15
  352. package/src/runtime/routes/inbound-stages/secret-ingress-check.ts +1 -1
  353. package/src/runtime/routes/index.ts +6 -0
  354. package/src/runtime/routes/log-export/AGENTS.md +1 -1
  355. package/src/runtime/routes/log-export/workspace-allowlist.ts +1 -1
  356. package/src/runtime/routes/migration-routes.ts +5 -9
  357. package/src/runtime/routes/plugins-routes.ts +26 -0
  358. package/src/runtime/routes/ps-routes.ts +10 -8
  359. package/src/runtime/routes/retrospective-routes.ts +235 -0
  360. package/src/runtime/routes/runs-pagination.ts +75 -0
  361. package/src/runtime/routes/schedule-routes.ts +367 -59
  362. package/src/runtime/routes/sounds-config-routes.ts +239 -0
  363. package/src/runtime/routes/surface-action-routes.ts +84 -4
  364. package/src/runtime/routes/workflow-routes.test.ts +372 -0
  365. package/src/runtime/routes/workflow-routes.ts +363 -0
  366. package/src/runtime/routes/workspace-routes.test.ts +61 -1
  367. package/src/runtime/routes/workspace-routes.ts +26 -1
  368. package/src/runtime/services/__tests__/analyze-conversation.test.ts +38 -0
  369. package/src/runtime/services/analyze-conversation.ts +26 -13
  370. package/src/schedule/inference-profile.ts +28 -0
  371. package/src/schedule/schedule-store.ts +152 -4
  372. package/src/schedule/scheduler-types.ts +6 -0
  373. package/src/schedule/scheduler.ts +96 -0
  374. package/src/security/secret-allowlist.ts +1 -1
  375. package/src/skills/path-classifier.ts +1 -1
  376. package/src/tools/apps/executors.ts +23 -0
  377. package/src/tools/apps/resolve-app-id.ts +42 -0
  378. package/src/tools/browser/browser-execution.ts +9 -11
  379. package/src/tools/credentials/broker.ts +4 -4
  380. package/src/tools/credentials/vault.ts +26 -137
  381. package/src/tools/executor.ts +69 -0
  382. package/src/tools/flag-gated-tools.test.ts +76 -0
  383. package/src/tools/permission-checker.ts +8 -1
  384. package/src/tools/registry.ts +51 -0
  385. package/src/tools/schedule/create.ts +77 -1
  386. package/src/tools/schedule/list.ts +1 -0
  387. package/src/tools/schedule/update.ts +73 -1
  388. package/src/tools/skills/execute.ts +56 -0
  389. package/src/tools/terminal/shell.ts +1 -1
  390. package/src/tools/ui-surface/definitions.ts +91 -2
  391. package/src/tools/workflows/manage-workflows.ts +183 -0
  392. package/src/tools/workflows/run-workflow.test.ts +442 -0
  393. package/src/tools/workflows/run-workflow.ts +88 -0
  394. package/src/types/onboarding-context.ts +2 -0
  395. package/src/usage/attribution.ts +24 -0
  396. package/src/util/canonicalize-identity.ts +12 -3
  397. package/src/util/platform.ts +17 -17
  398. package/src/watcher/__tests__/engine.test.ts +24 -0
  399. package/src/watcher/__tests__/telemetry.test.ts +135 -0
  400. package/src/watcher/engine.ts +7 -0
  401. package/src/watcher/telemetry.ts +74 -0
  402. package/src/workflows/capabilities.test.ts +365 -0
  403. package/src/workflows/capabilities.ts +359 -0
  404. package/src/workflows/deterministic-stringify.ts +27 -0
  405. package/src/workflows/engine-integration.test.ts +656 -0
  406. package/src/workflows/engine.test.ts +1144 -0
  407. package/src/workflows/engine.ts +1078 -0
  408. package/src/workflows/fanout-load.test.ts +168 -0
  409. package/src/workflows/journal-store.test.ts +369 -0
  410. package/src/workflows/journal-store.ts +470 -0
  411. package/src/workflows/leaf-runner.test.ts +704 -0
  412. package/src/workflows/leaf-runner.ts +589 -0
  413. package/src/workflows/library.test.ts +134 -0
  414. package/src/workflows/library.ts +124 -0
  415. package/src/workflows/run-manager.test.ts +711 -0
  416. package/src/workflows/run-manager.ts +593 -0
  417. package/src/workflows/sandbox-escape.test.ts +339 -0
  418. package/src/workflows/sandbox.test.ts +251 -0
  419. package/src/workflows/sandbox.ts +447 -0
  420. package/src/workspace/adaptive-thinking-repair.ts +33 -11
  421. package/src/workspace/migrations/021-move-signals-to-workspace.ts +1 -1
  422. package/src/workspace/migrations/022-move-hooks-to-workspace.ts +1 -1
  423. package/src/workspace/migrations/026-backfill-install-meta.ts +1 -1
  424. package/src/workspace/migrations/030-seed-pkb-autoinject.ts +2 -1
  425. package/src/workspace/migrations/031-drop-user-md.ts +1 -4
  426. package/src/workspace/migrations/048-remove-workspace-hooks.ts +1 -1
  427. package/src/workspace/migrations/056-release-notes-inference-profile-reordering.ts +5 -2
  428. package/src/workspace/migrations/061-move-backup-key-to-workspace.ts +1 -1
  429. package/src/workspace/migrations/082-backfill-managed-profile-labels.ts +8 -2
  430. package/src/workspace/migrations/097-enable-adaptive-thinking-managed-profiles.ts +41 -14
  431. package/src/workspace/migrations/102-preserve-heartbeat-enabled-for-existing-workspaces.ts +69 -0
  432. package/src/workspace/migrations/103-upgrade-quality-profile-to-opus-4-8.ts +83 -0
  433. package/src/workspace/migrations/104-recheck-adaptive-thinking-model-implied-anthropic.ts +133 -0
  434. package/src/workspace/migrations/registry.ts +6 -0
  435. package/src/workspace/migrations/runner.ts +1 -1
  436. package/tsconfig.json +1 -1
  437. package/src/__tests__/guardian-action-copy-generator.test.ts +0 -200
  438. package/src/__tests__/guardian-action-grant-mint-consume.test.ts +0 -579
  439. package/src/__tests__/guardian-action-store.test.ts +0 -106
  440. package/src/daemon/guardian-action-generators.ts +0 -71
  441. package/src/memory/guardian-action-store.ts +0 -484
  442. package/src/runtime/guardian-action-grant-minter.ts +0 -150
@@ -18,26 +18,28 @@ import { PROVIDER_CATALOG } from "../model-catalog.js";
18
18
  * Schema-accepted variants (runtime rejects with a clear "not yet shipped" error):
19
19
  * - service_account: service-account credentials (Vertex AI, Bedrock).
20
20
  */
21
- export const AuthSchema = z.discriminatedUnion("type", [
22
- z.object({
23
- type: z.literal("api_key"),
24
- credential: z.string().min(1),
25
- }),
26
- z.object({
27
- type: z.literal("platform"),
28
- }),
29
- z.object({
30
- type: z.literal("none"),
31
- }),
32
- z.object({
33
- type: z.literal("oauth_subscription"),
34
- credential: z.string().min(1),
35
- }),
36
- z.object({
37
- type: z.literal("service_account"),
38
- credential: z.string().min(1),
39
- }),
40
- ]);
21
+ export const AuthSchema = z
22
+ .discriminatedUnion("type", [
23
+ z.object({
24
+ type: z.literal("api_key"),
25
+ credential: z.string().min(1),
26
+ }),
27
+ z.object({
28
+ type: z.literal("platform"),
29
+ }),
30
+ z.object({
31
+ type: z.literal("none"),
32
+ }),
33
+ z.object({
34
+ type: z.literal("oauth_subscription"),
35
+ credential: z.string().min(1),
36
+ }),
37
+ z.object({
38
+ type: z.literal("service_account"),
39
+ credential: z.string().min(1),
40
+ }),
41
+ ])
42
+ .meta({ id: "Auth" });
41
43
 
42
44
  export type Auth = z.infer<typeof AuthSchema>;
43
45
 
@@ -75,42 +77,46 @@ export const VALID_CONNECTION_PROVIDERS: readonly string[] =
75
77
 
76
78
  export type ConnectionProvider = string;
77
79
 
78
- export const ConnectionProviderSchema = z.enum(
79
- VALID_CONNECTION_PROVIDERS as readonly [string, ...string[]],
80
- );
80
+ export const ConnectionProviderSchema = z
81
+ .enum(VALID_CONNECTION_PROVIDERS as readonly [string, ...string[]])
82
+ .meta({ id: "ConnectionProvider" });
81
83
 
82
84
  // ---------------------------------------------------------------------------
83
85
  // Per-connection model entries (openai-compatible)
84
86
  // ---------------------------------------------------------------------------
85
87
 
86
- export const ConnectionModelSchema = z.object({
87
- id: z.string().min(1),
88
- displayName: z.string().min(1).optional(),
89
- });
88
+ export const ConnectionModelSchema = z
89
+ .object({
90
+ id: z.string().min(1),
91
+ displayName: z.string().min(1).optional(),
92
+ })
93
+ .meta({ id: "ConnectionModel" });
90
94
  export type ConnectionModel = z.infer<typeof ConnectionModelSchema>;
91
95
 
92
96
  // ---------------------------------------------------------------------------
93
97
  // Full connection shape used by CRUD layer
94
98
  // ---------------------------------------------------------------------------
95
99
 
96
- export const ProviderConnectionSchema = z.object({
97
- name: z.string().min(1),
98
- provider: ConnectionProviderSchema,
99
- auth: AuthSchema,
100
- label: z.string().min(1).nullable(),
101
- baseUrl: z.string().url().nullable(),
102
- models: z.array(ConnectionModelSchema).nullable(),
103
- createdAt: z.number().int(),
104
- updatedAt: z.number().int(),
105
- /**
106
- * Whether this row is a Vellum-managed connection (`anthropic-managed`,
107
- * `openai-managed`, `gemini-managed`). Derived from
108
- * `MANAGED_CONNECTION_NAMES` in `connections.ts` at serialize time; the
109
- * DB column does not exist. Clients use this to render the read-only
110
- * "Vellum" badge + view-only editor and to disable the delete affordance
111
- * without mirroring the canonical name list locally.
112
- */
113
- isManaged: z.boolean(),
114
- });
100
+ export const ProviderConnectionSchema = z
101
+ .object({
102
+ name: z.string().min(1),
103
+ provider: ConnectionProviderSchema,
104
+ auth: AuthSchema,
105
+ label: z.string().min(1).nullable(),
106
+ baseUrl: z.string().url().nullable(),
107
+ models: z.array(ConnectionModelSchema).nullable(),
108
+ createdAt: z.number().int(),
109
+ updatedAt: z.number().int(),
110
+ /**
111
+ * Whether this row is a Vellum-managed connection (`anthropic-managed`,
112
+ * `openai-managed`, `gemini-managed`). Derived from
113
+ * `MANAGED_CONNECTION_NAMES` in `connections.ts` at serialize time; the
114
+ * DB column does not exist. Clients use this to render the read-only
115
+ * "Vellum" badge + view-only editor and to disable the delete affordance
116
+ * without mirroring the canonical name list locally.
117
+ */
118
+ isManaged: z.boolean(),
119
+ })
120
+ .meta({ id: "ProviderConnection" });
115
121
 
116
122
  export type ProviderConnection = z.infer<typeof ProviderConnectionSchema>;
@@ -13,7 +13,7 @@ const PROVIDER_MODEL_INTENTS: Record<string, Record<ModelIntent, string>> = {
13
13
  anthropic: {
14
14
  balanced: "claude-sonnet-4-6",
15
15
  "latency-optimized": "claude-haiku-4-5-20251001",
16
- "quality-optimized": "claude-fable-5",
16
+ "quality-optimized": "claude-opus-4-8",
17
17
  "vision-optimized": "claude-opus-4-6",
18
18
  },
19
19
  openai: {
@@ -43,7 +43,7 @@ const PROVIDER_MODEL_INTENTS: Record<string, Record<ModelIntent, string>> = {
43
43
  openrouter: {
44
44
  balanced: "anthropic/claude-sonnet-4.6",
45
45
  "latency-optimized": "anthropic/claude-haiku-4.5",
46
- "quality-optimized": "anthropic/claude-fable-5",
46
+ "quality-optimized": "anthropic/claude-opus-4.8",
47
47
  "vision-optimized": "anthropic/claude-opus-4.6",
48
48
  },
49
49
  };
@@ -0,0 +1,105 @@
1
+ import { describe, expect, test } from "bun:test";
2
+
3
+ import {
4
+ coerceObjectParamsToJsonString,
5
+ decodeCoercedObjectArgs,
6
+ } from "../coerce-object-args.js";
7
+
8
+ const SKILL_EXECUTE_SCHEMA = {
9
+ type: "object",
10
+ properties: {
11
+ tool: { type: "string", description: "The skill tool name" },
12
+ input: { type: "object", description: "Tool-specific parameters" },
13
+ activity: { type: "string", description: "Progress update" },
14
+ },
15
+ required: ["tool", "input", "activity"],
16
+ };
17
+
18
+ describe("coerceObjectParamsToJsonString", () => {
19
+ test("rewrites object params to strings and reports the keys", () => {
20
+ const { parameters, objectKeys } =
21
+ coerceObjectParamsToJsonString(SKILL_EXECUTE_SCHEMA);
22
+
23
+ expect(objectKeys).toEqual(["input"]);
24
+ const props = (
25
+ parameters as { properties: Record<string, { type: string }> }
26
+ ).properties;
27
+ expect(props.input.type).toBe("string");
28
+ // Scalars are left untouched.
29
+ expect(props.tool.type).toBe("string");
30
+ expect(props.activity.type).toBe("string");
31
+ // The required list is preserved.
32
+ expect((parameters as { required: string[] }).required).toEqual([
33
+ "tool",
34
+ "input",
35
+ "activity",
36
+ ]);
37
+ });
38
+
39
+ test("leaves array and scalar params alone (no coercion)", () => {
40
+ const schema = {
41
+ type: "object",
42
+ properties: {
43
+ query: { type: "string" },
44
+ sources: { type: "array", items: { type: "string" } },
45
+ },
46
+ };
47
+ const { parameters, objectKeys } = coerceObjectParamsToJsonString(schema);
48
+ expect(objectKeys).toEqual([]);
49
+ expect(parameters).toEqual(schema);
50
+ });
51
+
52
+ test("handles type unions that include object", () => {
53
+ const schema = {
54
+ type: "object",
55
+ properties: { data: { type: ["object", "null"] } },
56
+ };
57
+ const { objectKeys } = coerceObjectParamsToJsonString(schema);
58
+ expect(objectKeys).toEqual(["data"]);
59
+ });
60
+ });
61
+
62
+ describe("decodeCoercedObjectArgs", () => {
63
+ test("round-trips a JSON-string object back into an object", () => {
64
+ const { objectKeys } = coerceObjectParamsToJsonString(SKILL_EXECUTE_SCHEMA);
65
+ // What minimax would send once `input` is a string param.
66
+ const onWire = {
67
+ tool: "app_delete",
68
+ input: '{"app_id": "5ee3d2d5-46e8-4a79-928f-00a7471d340b"}',
69
+ activity: "Delete placeholder",
70
+ };
71
+ const { input, failedKey } = decodeCoercedObjectArgs(onWire, objectKeys);
72
+ expect(failedKey).toBeUndefined();
73
+ expect(input.input).toEqual({
74
+ app_id: "5ee3d2d5-46e8-4a79-928f-00a7471d340b",
75
+ });
76
+ // Scalars pass through unchanged.
77
+ expect(input.tool).toBe("app_delete");
78
+ });
79
+
80
+ test("empty string decodes to an empty object", () => {
81
+ const { input } = decodeCoercedObjectArgs({ input: "" }, ["input"]);
82
+ expect(input.input).toEqual({});
83
+ });
84
+
85
+ test("is idempotent when the value is already an object", () => {
86
+ const args = { input: { app_id: "x" } };
87
+ const { input, failedKey } = decodeCoercedObjectArgs(args, ["input"]);
88
+ expect(failedKey).toBeUndefined();
89
+ expect(input.input).toEqual({ app_id: "x" });
90
+ });
91
+
92
+ test("reports failedKey on invalid JSON instead of throwing", () => {
93
+ const { failedKey } = decodeCoercedObjectArgs(
94
+ { input: "{not valid json" },
95
+ ["input"],
96
+ );
97
+ expect(failedKey).toBe("input");
98
+ });
99
+
100
+ test("no-op when there are no coerced keys", () => {
101
+ const args = { tool: "x", input: "{}" };
102
+ const { input } = decodeCoercedObjectArgs(args, []);
103
+ expect(input).toEqual(args);
104
+ });
105
+ });
@@ -18,7 +18,14 @@ import {
18
18
  ContextOverflowError,
19
19
  extractOverflowTokensFromMessage,
20
20
  } from "../types.js";
21
- import { wrapUnparseableToolArgs } from "../unparseable-tool-args.js";
21
+ import {
22
+ isUnparseableToolArgs,
23
+ wrapUnparseableToolArgs,
24
+ } from "../unparseable-tool-args.js";
25
+ import {
26
+ coerceObjectParamsToJsonString,
27
+ decodeCoercedObjectArgs,
28
+ } from "./coerce-object-args.js";
22
29
 
23
30
  /**
24
31
  * Detect OpenAI-compatible context-overflow signals on an `OpenAI.APIError`.
@@ -199,6 +206,12 @@ export interface OpenAIChatCompletionsProviderOptions {
199
206
  * content or tool_calls must be set`. See {@link
200
207
  * EMPTY_ASSISTANT_TURN_PLACEHOLDER}. */
201
208
  backfillEmptyAssistantContent?: boolean;
209
+ /** Present object-typed tool params to the model as JSON-string params and
210
+ * decode them back to objects on the response. Works around models whose
211
+ * function-call serialization collapses nested objects to `{}` (observed
212
+ * with minimax-m3 on Fireworks). Off by default; scalars/arrays unaffected.
213
+ * See {@link coerceObjectParamsToJsonString}. */
214
+ coerceObjectArgsToJsonString?: boolean;
202
215
  }
203
216
 
204
217
  /** Wire-level reasoning_effort values. The OpenAI SDK type doesn't include
@@ -312,6 +325,7 @@ export class OpenAIChatCompletionsProvider implements Provider {
312
325
  | "reasoning_content"
313
326
  | undefined;
314
327
  private backfillEmptyAssistantContent: boolean;
328
+ private coerceObjectArgsToJsonString: boolean;
315
329
 
316
330
  constructor(
317
331
  apiKey: string,
@@ -337,6 +351,8 @@ export class OpenAIChatCompletionsProvider implements Provider {
337
351
  this.assistantReasoningField = options.assistantReasoningField;
338
352
  this.backfillEmptyAssistantContent =
339
353
  options.backfillEmptyAssistantContent ?? false;
354
+ this.coerceObjectArgsToJsonString =
355
+ options.coerceObjectArgsToJsonString ?? false;
340
356
  }
341
357
 
342
358
  async sendMessage(
@@ -355,6 +371,11 @@ export class OpenAIChatCompletionsProvider implements Provider {
355
371
  | Record<string, string>
356
372
  | undefined;
357
373
 
374
+ // Per-tool keys whose object schemas were rewritten to JSON strings for the
375
+ // wire, to be decoded back on the response. Empty unless
376
+ // `coerceObjectArgsToJsonString` is enabled.
377
+ const coercedObjectKeys = new Map<string, string[]>();
378
+
358
379
  try {
359
380
  const openaiMessages = this.toOpenAIMessages(messages, systemPrompt);
360
381
 
@@ -398,14 +419,24 @@ export class OpenAIChatCompletionsProvider implements Provider {
398
419
  }
399
420
 
400
421
  if (tools && tools.length > 0) {
401
- params.tools = tools.map((t) => ({
402
- type: "function" as const,
403
- function: {
404
- name: t.name,
405
- description: t.description,
406
- parameters: t.input_schema as OpenAI.FunctionParameters,
407
- },
408
- }));
422
+ params.tools = tools.map((t) => {
423
+ let parameters = t.input_schema as OpenAI.FunctionParameters;
424
+ if (this.coerceObjectArgsToJsonString) {
425
+ const coerced = coerceObjectParamsToJsonString(t.input_schema);
426
+ parameters = coerced.parameters as OpenAI.FunctionParameters;
427
+ if (coerced.objectKeys.length > 0) {
428
+ coercedObjectKeys.set(t.name, coerced.objectKeys);
429
+ }
430
+ }
431
+ return {
432
+ type: "function" as const,
433
+ function: {
434
+ name: t.name,
435
+ description: t.description,
436
+ parameters,
437
+ },
438
+ };
439
+ });
409
440
 
410
441
  // Honor a caller-supplied tool_choice (e.g. `{ type: "none" }` to force
411
442
  // a text-only answer, or `{ type: "tool", name }` for a forced call).
@@ -638,6 +669,13 @@ export class OpenAIChatCompletionsProvider implements Provider {
638
669
  } catch {
639
670
  input = wrapUnparseableToolArgs(tc.args);
640
671
  }
672
+ const objectKeys = coercedObjectKeys.get(tc.name);
673
+ if (objectKeys && !isUnparseableToolArgs(input)) {
674
+ const decoded = decodeCoercedObjectArgs(input, objectKeys);
675
+ input = decoded.failedKey
676
+ ? wrapUnparseableToolArgs(tc.args)
677
+ : decoded.input;
678
+ }
641
679
  content.push({
642
680
  type: "tool_use",
643
681
  id: tc.id,
@@ -0,0 +1,104 @@
1
+ /**
2
+ * Some OpenAI-compatible models (notably minimax-m3 served via Fireworks)
3
+ * collapse nested *object*-typed tool arguments to `{}` during their
4
+ * function-call serialization, while scalars and arrays survive intact. The
5
+ * model reasons correctly about the intended fields, but the object value is
6
+ * lost on the wire before it reaches us as valid-but-empty JSON, so there is
7
+ * nothing to recover at parse time.
8
+ *
9
+ * The workaround presents every top-level object-typed parameter to the model
10
+ * as a `string` it fills with JSON (strings survive the serializer), then
11
+ * decodes that string back into an object before dispatch. The transform is a
12
+ * matched pair — {@link coerceObjectParamsToJsonString} on the outbound schema
13
+ * and {@link decodeCoercedObjectArgs} on the inbound tool-call arguments — and
14
+ * is gated to the affected providers so other models keep native object
15
+ * passthrough.
16
+ */
17
+
18
+ interface ObjectSchemaLike {
19
+ type?: unknown;
20
+ properties?: Record<string, { type?: unknown; description?: string }>;
21
+ }
22
+
23
+ function isObjectTyped(prop: { type?: unknown }): boolean {
24
+ if (prop?.type === "object") return true;
25
+ if (Array.isArray(prop?.type) && prop.type.includes("object")) return true;
26
+ return false;
27
+ }
28
+
29
+ export interface CoercedToolSchema {
30
+ /** The tool's JSON schema with object params rewritten to JSON-string params. */
31
+ parameters: Record<string, unknown>;
32
+ /** Top-level param keys that were object-typed and are now JSON strings. */
33
+ objectKeys: string[];
34
+ }
35
+
36
+ /**
37
+ * Rewrite top-level `type: "object"` properties of a tool's input schema into
38
+ * `type: "string"` params the model fills with JSON. Returns the rewritten
39
+ * schema plus the list of converted keys so the inbound side can reverse it.
40
+ * Non-object params (scalars, arrays) are passed through unchanged. Arrays and
41
+ * deeper-nested objects are intentionally left alone — only top-level object
42
+ * params are observed to collapse.
43
+ */
44
+ export function coerceObjectParamsToJsonString(
45
+ inputSchema: unknown,
46
+ ): CoercedToolSchema {
47
+ const schema = inputSchema as ObjectSchemaLike | undefined;
48
+ if (!schema || schema.type !== "object" || !schema.properties) {
49
+ return {
50
+ parameters: (inputSchema as Record<string, unknown>) ?? {},
51
+ objectKeys: [],
52
+ };
53
+ }
54
+
55
+ const objectKeys: string[] = [];
56
+ const properties: Record<string, unknown> = {};
57
+ for (const [key, prop] of Object.entries(schema.properties)) {
58
+ if (isObjectTyped(prop)) {
59
+ objectKeys.push(key);
60
+ properties[key] = {
61
+ type: "string",
62
+ description:
63
+ `${prop.description ?? ""}\n\nPass this argument as a JSON object encoded as a string, e.g. "{\\"key\\": \\"value\\"}".`.trim(),
64
+ };
65
+ } else {
66
+ properties[key] = prop;
67
+ }
68
+ }
69
+
70
+ return { parameters: { ...schema, properties }, objectKeys };
71
+ }
72
+
73
+ /**
74
+ * Reverse {@link coerceObjectParamsToJsonString}: for each coerced key whose
75
+ * value arrived as a string, JSON-parse it back into an object. Values that are
76
+ * already objects (e.g. replayed conversation history, or a model that sent the
77
+ * object natively) are left untouched, so the decode is idempotent. An empty
78
+ * string decodes to `{}`. If a non-empty string fails to parse, the key name is
79
+ * returned in `failedKey` so the caller can surface a retryable error instead of
80
+ * silently dispatching garbage.
81
+ */
82
+ export function decodeCoercedObjectArgs(
83
+ input: Record<string, unknown>,
84
+ objectKeys: string[],
85
+ ): { input: Record<string, unknown>; failedKey?: string } {
86
+ if (objectKeys.length === 0) return { input };
87
+
88
+ const result: Record<string, unknown> = { ...input };
89
+ for (const key of objectKeys) {
90
+ const value = result[key];
91
+ if (typeof value !== "string") continue;
92
+ const trimmed = value.trim();
93
+ if (trimmed === "") {
94
+ result[key] = {};
95
+ continue;
96
+ }
97
+ try {
98
+ result[key] = JSON.parse(trimmed);
99
+ } catch {
100
+ return { input: result, failedKey: key };
101
+ }
102
+ }
103
+ return { input: result };
104
+ }
@@ -197,22 +197,25 @@ function normalizeSendMessageOptions(
197
197
  delete nextConfig.usageAttributionHeaders;
198
198
  delete nextConfig.usageTracking;
199
199
 
200
- // `overrideProfile` and `selectionSeed` are routing/resolution-time concerns
201
- // (consumed by the resolver below and `CallSiteRoutingProvider`'s provider
202
- // selection); neither is a wire-format field. Strip unconditionally so they
203
- // never leak into provider request bodies even when callers set them without
204
- // a `callSite`.
200
+ // `overrideProfile`, `forceOverrideProfile`, and `selectionSeed` are
201
+ // routing/resolution-time concerns (consumed by the resolver below and
202
+ // `CallSiteRoutingProvider`'s provider selection); none is a wire-format
203
+ // field. Strip unconditionally so they never leak into provider request
204
+ // bodies even when callers set them without a `callSite`.
205
205
  delete nextConfig.overrideProfile;
206
+ delete nextConfig.forceOverrideProfile;
206
207
  delete nextConfig.selectionSeed;
207
208
 
208
209
  if (config.callSite !== undefined) {
209
210
  const resolved = resolveCallSiteConfig(config.callSite, getConfig().llm, {
210
211
  overrideProfile: config.overrideProfile,
212
+ forceOverrideProfile: config.forceOverrideProfile,
211
213
  selectionSeed: config.selectionSeed,
212
214
  });
213
215
  const attribution = resolveUsageAttribution({
214
216
  callSite: config.callSite,
215
217
  overrideProfile: config.overrideProfile,
218
+ forceOverrideProfile: config.forceOverrideProfile,
216
219
  selectionSeed: config.selectionSeed,
217
220
  });
218
221
 
@@ -182,6 +182,16 @@ export interface SendMessageConfig {
182
182
  * silently fall through.
183
183
  */
184
184
  overrideProfile?: string;
185
+ /**
186
+ * When true, the resolver floats `overrideProfile` above the call-site
187
+ * layers (named site profile + call-site override) for non-main-agent call
188
+ * sites — see `ResolveCallSiteOpts.forceOverrideProfile`. Used by callers
189
+ * that must run a background call site under a specific conversation's
190
+ * inference profile (e.g. fork-based memory retrospectives). A
191
+ * resolution/routing-time concern only; stripped before any provider wire
192
+ * request.
193
+ */
194
+ forceOverrideProfile?: boolean;
185
195
  /**
186
196
  * Per-conversation seed for deterministic `mix`-profile expansion. The agent
187
197
  * loop sets this to the conversation id so every resolver call this send