@vellumai/assistant 0.10.9 → 0.10.10-dev.202607162206.d08e98e

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 (456) hide show
  1. package/ARCHITECTURE.md +1 -1
  2. package/Dockerfile +8 -0
  3. package/docs/activation-funnel-telemetry.md +13 -7
  4. package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/package.json +1 -0
  5. package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/__tests__/redacted-credential.test.ts +200 -0
  6. package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/redacted-credential.ts +226 -0
  7. package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/package.json +1 -0
  8. package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/__tests__/redacted-credential.test.ts +200 -0
  9. package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/redacted-credential.ts +226 -0
  10. package/node_modules/@vellumai/service-contracts/package.json +1 -0
  11. package/node_modules/@vellumai/service-contracts/src/__tests__/redacted-credential.test.ts +200 -0
  12. package/node_modules/@vellumai/service-contracts/src/redacted-credential.ts +226 -0
  13. package/openapi.yaml +530 -107
  14. package/package.json +1 -1
  15. package/scripts/generate-openapi.ts +8 -0
  16. package/src/__tests__/activation-early-marking.test.ts +6 -5
  17. package/src/__tests__/agent-loop-override-profile.test.ts +22 -25
  18. package/src/__tests__/agent-wake-override-profile.test.ts +21 -48
  19. package/src/__tests__/app-builder-tool-scripts.test.ts +0 -1
  20. package/src/__tests__/app-bundler.test.ts +4 -12
  21. package/src/__tests__/app-executors.test.ts +2 -49
  22. package/src/__tests__/app-routes-csp.test.ts +114 -146
  23. package/src/__tests__/auth-fallback-events-store.test.ts +8 -1
  24. package/src/__tests__/build-persisted-content.test.ts +96 -0
  25. package/src/__tests__/bundle-scanner.test.ts +27 -1
  26. package/src/__tests__/call-controller.test.ts +291 -0
  27. package/src/__tests__/call-site-routing-connection-auto-resolve.test.ts +165 -0
  28. package/src/__tests__/chat-credential-redaction.test.ts +1395 -0
  29. package/src/__tests__/chat-reveal-guard-priming.test.ts +791 -0
  30. package/src/__tests__/compaction.benchmark.test.ts +2 -1
  31. package/src/__tests__/compactor-image-manifest-trust.test.ts +50 -0
  32. package/src/__tests__/config-loader-backfill.test.ts +9 -4
  33. package/src/__tests__/config-schema-cmd.test.ts +10 -11
  34. package/src/__tests__/config-schema.test.ts +190 -257
  35. package/src/__tests__/conversation-agent-loop-fatal-cleanup.test.ts +208 -0
  36. package/src/__tests__/conversation-agent-loop-overflow.test.ts +5 -19
  37. package/src/__tests__/conversation-agent-loop.test.ts +22 -9
  38. package/src/__tests__/conversation-error.test.ts +31 -0
  39. package/src/__tests__/conversation-load-history-repair.test.ts +110 -0
  40. package/src/__tests__/conversation-process-callsite.test.ts +12 -19
  41. package/src/__tests__/conversation-routes-slash-commands.test.ts +7 -21
  42. package/src/__tests__/conversation-summarize-route.test.ts +36 -44
  43. package/src/__tests__/conversation-surfaces-action-delivery.test.ts +1 -0
  44. package/src/__tests__/conversation-surfaces-activation-emit.test.ts +7 -7
  45. package/src/__tests__/conversation-tool-setup-app-refresh.test.ts +0 -1
  46. package/src/__tests__/conversation-tool-setup-attribution.test.ts +0 -1
  47. package/src/__tests__/conversation-usage.test.ts +4 -12
  48. package/src/__tests__/credential-routes.test.ts +250 -0
  49. package/src/__tests__/credential-security-invariants.test.ts +3 -0
  50. package/src/__tests__/db-migration-rollback.test.ts +22 -0
  51. package/src/__tests__/empty-response-hook.test.ts +186 -1
  52. package/src/__tests__/external-plugin-loader.test.ts +11 -5
  53. package/src/__tests__/heartbeat-service.test.ts +0 -28
  54. package/src/__tests__/host-shell-tool.test.ts +2 -0
  55. package/src/__tests__/inactive-tool-error-messages.test.ts +2 -2
  56. package/src/__tests__/inference-no-mode-boot-e2e.test.ts +52 -8
  57. package/src/__tests__/internal-telemetry-routes.test.ts +23 -4
  58. package/src/__tests__/invite-routes-http.test.ts +12 -16
  59. package/src/__tests__/list-all-apps.test.ts +0 -4
  60. package/src/__tests__/llm-context-resolution.test.ts +32 -56
  61. package/src/__tests__/llm-request-log-turn-query.test.ts +109 -0
  62. package/src/__tests__/llm-resolver-override-or-default.test.ts +3 -52
  63. package/src/__tests__/llm-resolver.test.ts +342 -602
  64. package/src/__tests__/llm-schema.test.ts +79 -37
  65. package/src/__tests__/max-tokens-continue-hook.test.ts +19 -0
  66. package/src/__tests__/media-stream-output.test.ts +259 -3
  67. package/src/__tests__/media-stream-server-integration.test.ts +22 -1
  68. package/src/__tests__/media-stream-stt-session.test.ts +47 -0
  69. package/src/__tests__/memory-jobs-worker-cleanup-cadence.test.ts +33 -0
  70. package/src/__tests__/memory-recall-log-store.test.ts +47 -13
  71. package/src/__tests__/mock-gateway-ipc.ts +46 -1
  72. package/src/__tests__/mtime-cache.test.ts +61 -0
  73. package/src/__tests__/navigate-settings-tab.test.ts +2 -0
  74. package/src/__tests__/normalize-onboarding.test.ts +33 -0
  75. package/src/__tests__/onboarding-persona-write.test.ts +26 -0
  76. package/src/__tests__/plugin-api-resolve-credential.test.ts +140 -0
  77. package/src/__tests__/plugin-app-serve-routes.test.ts +166 -14
  78. package/src/__tests__/plugin-import-boundary-reverse-guard.test.ts +2 -0
  79. package/src/__tests__/post-turn-tool-result-truncation.test.ts +38 -0
  80. package/src/__tests__/provider-commit-message-generator.test.ts +27 -20
  81. package/src/__tests__/provider-connections-backfill.test.ts +138 -0
  82. package/src/__tests__/provider-platform-proxy-integration.test.ts +10 -31
  83. package/src/__tests__/provider-registry-ollama.test.ts +8 -18
  84. package/src/__tests__/provider-send-message-override-profile.test.ts +23 -23
  85. package/src/__tests__/provider-usage-tracking.test.ts +9 -19
  86. package/src/__tests__/prune-old-conversations-job.test.ts +12 -0
  87. package/src/__tests__/published-app-updater.test.ts +22 -16
  88. package/src/__tests__/registry.test.ts +5 -16
  89. package/src/__tests__/retry-openrouter-only-normalization.test.ts +22 -23
  90. package/src/__tests__/retry-thinking-adaptive-only.test.ts +43 -48
  91. package/src/__tests__/retry-thinking-tool-choice.test.ts +57 -66
  92. package/src/__tests__/retry-verbosity-normalization.test.ts +24 -23
  93. package/src/__tests__/reveal-success-registry.test.ts +123 -0
  94. package/src/__tests__/run-conversation-turn-persistence.test.ts +130 -0
  95. package/src/__tests__/secret-fixtures.ts +9 -0
  96. package/src/__tests__/server-history-render.test.ts +28 -0
  97. package/src/__tests__/skills.test.ts +9 -4
  98. package/src/__tests__/slack-share-routes.test.ts +0 -1
  99. package/src/__tests__/stt-stream-session.test.ts +6 -5
  100. package/src/__tests__/subagent-call-site-routing.test.ts +69 -95
  101. package/src/__tests__/subagent-disposal.test.ts +2 -0
  102. package/src/__tests__/subagent-fork-notifications.test.ts +2 -0
  103. package/src/__tests__/subagent-fork-spawn.test.ts +2 -0
  104. package/src/__tests__/subagent-manager-notify.test.ts +2 -0
  105. package/src/__tests__/subagent-role-registry.test.ts +37 -0
  106. package/src/__tests__/subagent-spawn-and-await.test.ts +1 -0
  107. package/src/__tests__/subagent-terminal-message.test.ts +50 -0
  108. package/src/__tests__/subagent-tool-gate-mode.test.ts +78 -5
  109. package/src/__tests__/surface-completion-nudge-hook.test.ts +19 -0
  110. package/src/__tests__/telemetry-routes.test.ts +99 -19
  111. package/src/__tests__/tool-audit.test.ts +34 -4
  112. package/src/__tests__/tool-executor-lifecycle-events.test.ts +1 -1
  113. package/src/__tests__/tool-profiler.test.ts +72 -1
  114. package/src/__tests__/tool-result-spool.test.ts +49 -4
  115. package/src/__tests__/tool-side-effects-slack-dm.test.ts +0 -1
  116. package/src/__tests__/ui-channel-variants.test.ts +108 -0
  117. package/src/__tests__/ui-shape-teaching.test.ts +255 -0
  118. package/src/__tests__/usage-attribution.test.ts +18 -41
  119. package/src/__tests__/user-plugin-loader.test.ts +4 -4
  120. package/src/__tests__/voice-config-update.test.ts +46 -0
  121. package/src/__tests__/voice-session-bridge.test.ts +216 -84
  122. package/src/__tests__/workspace-migration-131-drop-web-fetch-mode.test.ts +120 -0
  123. package/src/agent/loop.ts +4 -3
  124. package/src/api/events/open-conversation.test.ts +64 -0
  125. package/src/api/events/open-conversation.ts +33 -0
  126. package/src/api/index.ts +6 -0
  127. package/src/api/responses/conversation-message.ts +5 -0
  128. package/src/apps/app-store.ts +25 -35
  129. package/src/bundler/app-bundler.ts +34 -48
  130. package/src/bundler/app-compiler.ts +39 -4
  131. package/src/bundler/bundle-scanner.ts +13 -0
  132. package/src/bundler/manifest.ts +1 -1
  133. package/src/calls/__tests__/voice-session-bridge.test.ts +47 -0
  134. package/src/calls/call-constants.ts +5 -0
  135. package/src/calls/call-controller.ts +100 -32
  136. package/src/calls/call-transport.ts +9 -0
  137. package/src/calls/media-stream-output.ts +107 -4
  138. package/src/calls/media-stream-server.ts +29 -9
  139. package/src/calls/media-stream-stt-session.ts +7 -1
  140. package/src/calls/media-turn-detector.ts +11 -1
  141. package/src/calls/voice-session-bridge.ts +84 -63
  142. package/src/cli/commands/__tests__/inference-providers.test.ts +270 -33
  143. package/src/cli/commands/__tests__/notifications.test.ts +24 -3
  144. package/src/cli/commands/config.help.ts +6 -6
  145. package/src/cli/commands/credentials.help.ts +13 -0
  146. package/src/cli/commands/credentials.ts +6 -1
  147. package/src/cli/commands/email.help.ts +7 -0
  148. package/src/cli/commands/email.ts +35 -1
  149. package/src/cli/commands/inference-providers.ts +168 -107
  150. package/src/cli/commands/inference.help.ts +118 -46
  151. package/src/cli/commands/memory/index.help.ts +23 -0
  152. package/src/cli/commands/memory/nodes.ts +146 -0
  153. package/src/cli/commands/notifications.help.ts +10 -10
  154. package/src/cli/commands/oauth/connect-surface-guidance.test.ts +40 -0
  155. package/src/cli/commands/oauth/connect-surface-guidance.ts +54 -0
  156. package/src/cli/commands/oauth/connect.test.ts +126 -0
  157. package/src/cli/commands/oauth/connect.ts +29 -6
  158. package/src/cli/commands/oauth/index.help.ts +7 -1
  159. package/src/cli/commands/oauth/status.test.ts +69 -3
  160. package/src/cli/commands/oauth/status.ts +50 -12
  161. package/src/cli/commands/plugins.help.ts +13 -2
  162. package/src/cli/commands/plugins.ts +61 -7
  163. package/src/cli/commands/telemetry.help.ts +13 -0
  164. package/src/cli/commands/telemetry.ts +45 -5
  165. package/src/cli/lib/__tests__/plugin-catalog-local.test.ts +8 -2
  166. package/src/cli/lib/__tests__/upgrade-plugin.test.ts +213 -9
  167. package/src/cli/lib/bundled-marketplace.json +93 -36
  168. package/src/cli/lib/inspect-plugin.ts +5 -1
  169. package/src/cli/lib/install-from-github.ts +68 -4
  170. package/src/cli/lib/plugin-catalog-local.ts +6 -2
  171. package/src/cli/lib/plugin-fingerprint.ts +3 -3
  172. package/src/cli/lib/upgrade-plugin.ts +236 -35
  173. package/src/config/__tests__/default-profile-catalog.test.ts +8 -12
  174. package/src/config/__tests__/plugin-resident-skill-discovery.test.ts +137 -0
  175. package/src/config/__tests__/profile-materialization.test.ts +1 -88
  176. package/src/config/bundled-skills/AGENTS.md +3 -30
  177. package/src/config/bundled-skills/app-builder/SKILL.md +5 -3
  178. package/src/config/bundled-skills/app-builder/TOOLS.json +23 -0
  179. package/src/config/bundled-skills/app-builder/tools/app-open.ts +32 -0
  180. package/src/config/bundled-skills/messaging/tools/messaging-send.ts +1 -1
  181. package/src/config/bundled-skills/phone-calls/references/CONFIG.md +7 -7
  182. package/src/config/bundled-skills/schedule/SKILL.md +1 -1
  183. package/src/config/bundled-skills/schedule/TOOLS.json +1 -1
  184. package/src/config/bundled-skills/settings/TOOLS.json +3 -1
  185. package/src/config/bundled-skills/settings/tools/navigate-settings-tab.ts +2 -0
  186. package/src/config/bundled-skills/settings/tools/voice-config-update.ts +18 -10
  187. package/src/config/bundled-skills/subagent/SKILL.md +2 -0
  188. package/src/config/bundled-skills/subagent/TOOLS.json +1 -1
  189. package/src/config/call-site-defaults.ts +3 -3
  190. package/src/config/feature-flag-registry.json +24 -39
  191. package/src/config/llm-resolver.ts +88 -475
  192. package/src/config/profile-materialization.ts +11 -11
  193. package/src/config/schema.ts +93 -127
  194. package/src/config/schemas/__tests__/live-voice.test.ts +24 -6
  195. package/src/config/schemas/__tests__/stt.test.ts +31 -3
  196. package/src/config/schemas/live-voice.ts +10 -4
  197. package/src/config/schemas/llm.ts +52 -15
  198. package/src/config/schemas/memory-lifecycle.ts +1 -1
  199. package/src/config/schemas/memory-retrospective.ts +8 -0
  200. package/src/config/schemas/memory-v2.ts +2 -2
  201. package/src/config/schemas/memory-v3.ts +1 -1
  202. package/src/config/schemas/services.ts +6 -3
  203. package/src/config/schemas/stt.ts +38 -21
  204. package/src/config/schemas/tts.ts +13 -18
  205. package/src/config/skills.ts +31 -21
  206. package/src/context/compactor.ts +44 -7
  207. package/src/context/post-turn-tool-result-truncation.ts +4 -2
  208. package/src/context/tool-result-spool.ts +26 -5
  209. package/src/conversations/__tests__/message-consolidation.test.ts +48 -0
  210. package/src/conversations/message-consolidation.ts +22 -2
  211. package/src/daemon/__tests__/conversation-tool-setup.test.ts +5 -12
  212. package/src/daemon/app-source-watcher.ts +17 -23
  213. package/src/daemon/chat-credential-redaction.ts +1365 -0
  214. package/src/daemon/conversation-agent-loop-handlers.ts +627 -34
  215. package/src/daemon/conversation-agent-loop.ts +71 -31
  216. package/src/daemon/conversation-error.ts +36 -14
  217. package/src/daemon/conversation-process.ts +22 -0
  218. package/src/daemon/conversation-store.ts +35 -0
  219. package/src/daemon/conversation-surfaces.ts +25 -25
  220. package/src/daemon/conversation-tool-setup.ts +33 -7
  221. package/src/daemon/conversation.ts +55 -5
  222. package/src/daemon/handlers/shared.ts +33 -2
  223. package/src/daemon/lifecycle.ts +4 -4
  224. package/src/daemon/message-types/conversations.ts +6 -17
  225. package/src/daemon/providers-setup.ts +8 -0
  226. package/src/daemon/tool-setup-types.ts +7 -1
  227. package/src/daemon/wake-conversation-ops.ts +10 -1
  228. package/src/hooks/types.ts +9 -0
  229. package/src/ipc/__tests__/email-ipc.test.ts +90 -0
  230. package/src/ipc/gateway-client.test.ts +59 -0
  231. package/src/ipc/gateway-client.ts +70 -27
  232. package/src/live-voice/__tests__/live-voice-events.test.ts +14 -2
  233. package/src/live-voice/__tests__/live-voice-integration.test.ts +116 -2
  234. package/src/live-voice/__tests__/live-voice-vad.test.ts +804 -13
  235. package/src/live-voice/__tests__/protocol.test.ts +122 -0
  236. package/src/live-voice/live-voice-session.ts +443 -40
  237. package/src/live-voice/protocol.ts +143 -1
  238. package/src/monitoring/__tests__/plugin-source-watch.test.ts +3 -1
  239. package/src/monitoring/plugin-source-watch.ts +3 -62
  240. package/src/notifications/README.md +1 -1
  241. package/src/permissions/checker.ts +8 -4
  242. package/src/persistence/__tests__/db-init-migrations-ok.test.ts +26 -0
  243. package/src/persistence/conversation-crud.ts +104 -2
  244. package/src/persistence/db-init.ts +14 -3
  245. package/src/persistence/job-handlers/cleanup.ts +15 -7
  246. package/src/persistence/llm-request-log-store.ts +88 -52
  247. package/src/persistence/migrations/298-move-memory-jobs-to-memory-db.ts +7 -31
  248. package/src/persistence/migrations/305-drop-contact-acl-columns.ts +3 -2
  249. package/src/persistence/migrations/326-move-injection-events-to-memory-db.ts +8 -34
  250. package/src/persistence/migrations/336-move-memory-v2-activation-logs-to-memory-db.ts +90 -0
  251. package/src/persistence/migrations/337-move-memory-recall-logs-to-memory-db.ts +114 -0
  252. package/src/persistence/migrations/338-move-memory-v3-selections-to-memory-db.ts +84 -0
  253. package/src/persistence/migrations/339-move-activation-sessions-to-memory-db.ts +52 -0
  254. package/src/persistence/migrations/__tests__/run-migrations.test.ts +155 -0
  255. package/src/persistence/migrations/helpers/relocation.ts +44 -1
  256. package/src/persistence/migrations/run-migrations.ts +25 -1
  257. package/src/persistence/schema/infrastructure.ts +9 -0
  258. package/src/persistence/schema/memory-core.ts +3 -0
  259. package/src/persistence/schema/memory-injection.ts +2 -0
  260. package/src/persistence/steps.ts +36 -0
  261. package/src/platform/client.test.ts +1 -44
  262. package/src/platform/client.ts +10 -20
  263. package/src/platform/consent-cache.test.ts +89 -23
  264. package/src/platform/consent-cache.ts +71 -33
  265. package/src/plugin-api/constants.ts +12 -0
  266. package/src/plugin-api/conversation-turn.ts +37 -14
  267. package/src/plugin-api/index.ts +11 -1
  268. package/src/plugin-api/resolve-credential.ts +75 -0
  269. package/src/plugin-api/vision-support.test.ts +8 -19
  270. package/src/plugin-api/vision-support.ts +26 -17
  271. package/src/plugins/collect-source-versions.ts +77 -0
  272. package/src/plugins/defaults/compaction/compact.ts +6 -0
  273. package/src/plugins/defaults/compaction/window-manager.ts +9 -0
  274. package/src/plugins/defaults/empty-response/hooks/post-model-call.ts +18 -24
  275. package/src/plugins/defaults/empty-response/hooks/user-prompt-submit.ts +38 -0
  276. package/src/plugins/defaults/empty-response/refusal-quarantine.ts +99 -0
  277. package/src/plugins/defaults/image-fallback/__tests__/caption-cache-persistence.test.ts +7 -3
  278. package/src/plugins/defaults/index.ts +8 -1
  279. package/src/plugins/defaults/max-tokens-continue/hooks/post-model-call.ts +4 -1
  280. package/src/plugins/defaults/memory/__tests__/activation-session-store.test.ts +48 -6
  281. package/src/plugins/defaults/memory/__tests__/db-memory-attach.test.ts +28 -22
  282. package/src/plugins/defaults/memory/__tests__/memory-log-stores-degraded.test.ts +148 -0
  283. package/src/plugins/defaults/memory/__tests__/memory-retrospective-job.test.ts +80 -8
  284. package/src/plugins/defaults/memory/__tests__/memory-retrospective-prompt.test.ts +202 -0
  285. package/src/plugins/defaults/memory/__tests__/memory-v2-activation-log-store.test.ts +64 -25
  286. package/src/plugins/defaults/memory/__tests__/memory-v2-concept-frequency.test.ts +24 -11
  287. package/src/plugins/defaults/memory/__tests__/prompt-override.test.ts +70 -6
  288. package/src/plugins/defaults/memory/__tests__/table-relocation.test.ts +278 -0
  289. package/src/plugins/defaults/memory/activation-session-store.ts +27 -20
  290. package/src/plugins/defaults/memory/context-search/sources/memory-v2.ts +2 -9
  291. package/src/plugins/defaults/memory/context-search/sources/workspace.ts +1 -8
  292. package/src/plugins/defaults/memory/graph/retriever.test.ts +6 -6
  293. package/src/plugins/defaults/memory/graph/store.ts +114 -0
  294. package/src/plugins/defaults/memory/graph/tool-handlers.ts +36 -1
  295. package/src/plugins/defaults/memory/graph/tools.ts +47 -13
  296. package/src/plugins/defaults/memory/graph-topology/build-memory-graph.ts +16 -35
  297. package/src/plugins/defaults/memory/jobs-worker.ts +10 -1
  298. package/src/plugins/defaults/memory/memory-db.ts +3 -2
  299. package/src/plugins/defaults/memory/memory-recall-log-store.ts +129 -66
  300. package/src/plugins/defaults/memory/memory-retrospective-constants.ts +8 -0
  301. package/src/plugins/defaults/memory/memory-retrospective-job.ts +11 -122
  302. package/src/plugins/defaults/memory/memory-retrospective-prompt.ts +216 -0
  303. package/src/plugins/defaults/memory/memory-v2-activation-log-store.ts +84 -46
  304. package/src/plugins/defaults/memory/memory-v2-concept-frequency.ts +42 -32
  305. package/src/plugins/defaults/memory/path-containment.ts +21 -0
  306. package/src/plugins/defaults/memory/prompt-override.ts +51 -13
  307. package/src/plugins/defaults/memory/tools.test.ts +34 -0
  308. package/src/plugins/defaults/memory/tools.ts +12 -2
  309. package/src/plugins/defaults/memory/v2/__tests__/harness-compare.test.ts +19 -15
  310. package/src/plugins/defaults/memory/v2/__tests__/harness-oracle.test.ts +24 -19
  311. package/src/plugins/defaults/memory/v2/__tests__/harness-replay-input.test.ts +19 -15
  312. package/src/plugins/defaults/memory/v2/__tests__/injection.test.ts +22 -2
  313. package/src/plugins/defaults/memory/v2/__tests__/prompts-consolidation.test.ts +5 -3
  314. package/src/plugins/defaults/memory/v2/harness/oracle.ts +59 -41
  315. package/src/plugins/defaults/memory/v2/harness/replay-input.ts +29 -25
  316. package/src/plugins/defaults/memory/v2/migration.ts +46 -16
  317. package/src/plugins/defaults/memory/v2/prompts/consolidation.ts +4 -0
  318. package/src/plugins/defaults/memory/v3/__tests__/carry-integration.test.ts +17 -15
  319. package/src/plugins/defaults/memory/v3/__tests__/gate.test.ts +24 -22
  320. package/src/plugins/defaults/memory/v3/__tests__/injection.test.ts +9 -5
  321. package/src/plugins/defaults/memory/v3/__tests__/orchestrate.test.ts +277 -10
  322. package/src/plugins/defaults/memory/v3/__tests__/selection-log-store.test.ts +57 -5
  323. package/src/plugins/defaults/memory/v3/__tests__/shadow-integration.test.ts +9 -7
  324. package/src/plugins/defaults/memory/v3/__tests__/shadow-plugin.test.ts +38 -7
  325. package/src/plugins/defaults/memory/v3/hot-set.test.ts +36 -18
  326. package/src/plugins/defaults/memory/v3/hot-set.ts +12 -14
  327. package/src/plugins/defaults/memory/v3/learned-edges.test.ts +47 -27
  328. package/src/plugins/defaults/memory/v3/learned-edges.ts +12 -14
  329. package/src/plugins/defaults/memory/v3/orchestrate.ts +139 -22
  330. package/src/plugins/defaults/memory/v3/prune.test.ts +10 -3
  331. package/src/plugins/defaults/memory/v3/prune.ts +17 -11
  332. package/src/plugins/defaults/memory/v3/selection-log-store.ts +23 -11
  333. package/src/plugins/defaults/memory/v3/shadow-plugin.ts +70 -53
  334. package/src/plugins/defaults/surface-completion-nudge/hooks/post-model-call.ts +9 -6
  335. package/src/plugins/external-plugin-loader.ts +15 -15
  336. package/src/plugins/mtime-cache.ts +47 -0
  337. package/src/plugins/pipeline.ts +9 -1
  338. package/src/plugins/plugin-execution-context.ts +44 -0
  339. package/src/plugins/plugin-tree-walk.ts +31 -24
  340. package/src/plugins/source-fingerprint.ts +3 -4
  341. package/src/prompts/normalize-onboarding.ts +12 -0
  342. package/src/prompts/persona-resolver.ts +8 -0
  343. package/src/prompts/templates/BOOTSTRAP-ACTIVATION-RAIL.md +1 -1
  344. package/src/providers/__tests__/dispatch-connection-routing.test.ts +6 -9
  345. package/src/providers/__tests__/registry-native-web-search.test.ts +4 -10
  346. package/src/providers/__tests__/retry-callsite.test.ts +235 -215
  347. package/src/providers/__tests__/satellite-connection-routing.test.ts +8 -16
  348. package/src/providers/atlascloud/client.ts +10 -49
  349. package/src/providers/baseten/client.ts +43 -0
  350. package/src/providers/call-site-routing.ts +26 -13
  351. package/src/providers/connection-resolution.ts +9 -11
  352. package/src/providers/fetch-provider-catalog.ts +4 -2
  353. package/src/providers/inference/__tests__/adapter-factory-openai-compatible.test.ts +29 -4
  354. package/src/providers/inference/__tests__/connection-availability-keyless.test.ts +78 -0
  355. package/src/providers/inference/adapter-factory.ts +27 -6
  356. package/src/providers/inference/auth.ts +29 -1
  357. package/src/providers/inference/backfill.ts +61 -10
  358. package/src/providers/inference/connection-availability.ts +3 -2
  359. package/src/providers/inference/resolve-auth.ts +9 -1
  360. package/src/providers/model-catalog.ts +37 -0
  361. package/src/providers/openai/__tests__/api-error-normalization.test.ts +24 -2
  362. package/src/providers/openai/api-key-validation.ts +70 -0
  363. package/src/providers/retry.ts +2 -0
  364. package/src/providers/types.ts +11 -4
  365. package/src/providers/vellum-model-routing.test.ts +26 -0
  366. package/src/providers/vellum-model-routing.ts +30 -0
  367. package/src/providers/voice-error-copy.ts +47 -0
  368. package/src/runtime/agent-wake.ts +40 -24
  369. package/src/runtime/for-chat-mint-registry.ts +118 -0
  370. package/src/runtime/reveal-nonce.ts +49 -0
  371. package/src/runtime/reveal-success-registry.ts +306 -0
  372. package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +88 -3
  373. package/src/runtime/routes/__tests__/inference-provider-connection-routes.test.ts +161 -30
  374. package/src/runtime/routes/__tests__/migration-vellum-metadata-reconcile.test.ts +7 -0
  375. package/src/runtime/routes/__tests__/schedule-worker-routes.test.ts +15 -0
  376. package/src/runtime/routes/app-management-routes.ts +152 -45
  377. package/src/runtime/routes/app-routes.ts +16 -77
  378. package/src/runtime/routes/canned-message-complete.ts +16 -16
  379. package/src/runtime/routes/conversation-management-routes.ts +17 -14
  380. package/src/runtime/routes/conversation-query-routes.ts +54 -6
  381. package/src/runtime/routes/conversation-routes.ts +48 -2
  382. package/src/runtime/routes/credential-routes.ts +116 -3
  383. package/src/runtime/routes/email-routes.ts +17 -1
  384. package/src/runtime/routes/inbound-stages/transcribe-audio.test.ts +33 -5
  385. package/src/runtime/routes/inbound-stages/transcribe-audio.ts +6 -5
  386. package/src/runtime/routes/inference-provider-connection-routes.ts +77 -28
  387. package/src/runtime/routes/inference-send-routes.ts +1 -1
  388. package/src/runtime/routes/internal-telemetry-routes.ts +7 -6
  389. package/src/runtime/routes/plugins-routes.ts +37 -6
  390. package/src/runtime/routes/publish-routes.ts +15 -18
  391. package/src/runtime/routes/schedule-worker-routes.ts +9 -0
  392. package/src/runtime/routes/secret-routes.ts +10 -0
  393. package/src/runtime/routes/telemetry-routes.ts +149 -45
  394. package/src/schedule/__tests__/schedule-timezone.test.ts +101 -0
  395. package/src/schedule/__tests__/worker-watchdog.test.ts +209 -0
  396. package/src/schedule/schedule-store.ts +12 -1
  397. package/src/schedule/schedule-timezone.ts +63 -0
  398. package/src/schedule/scheduler.ts +100 -6
  399. package/src/schedule/worker-control.ts +19 -0
  400. package/src/security/auth-fallback-events-store.ts +7 -6
  401. package/src/security/secret-scanner.ts +26 -1
  402. package/src/services/published-app-updater.ts +6 -11
  403. package/src/stt/stt-stream-session.ts +36 -16
  404. package/src/subagent/manager.ts +34 -8
  405. package/src/telemetry/AGENTS.md +30 -1
  406. package/src/telemetry/__tests__/config-setting-snapshot.test.ts +18 -0
  407. package/src/telemetry/__tests__/outbox-test-harness.ts +5 -3
  408. package/src/telemetry/config-setting-snapshot.ts +44 -10
  409. package/src/telemetry/telemetry-event-sources.test.ts +124 -30
  410. package/src/telemetry/telemetry-event-sources.ts +137 -83
  411. package/src/telemetry/telemetry-events-outbox.test.ts +46 -1
  412. package/src/telemetry/telemetry-events-outbox.ts +60 -9
  413. package/src/telemetry/telemetry-wire-source.json +1 -1
  414. package/src/telemetry/telemetry-wire-validation.ts +39 -2
  415. package/src/telemetry/telemetry-wire.generated.ts +8 -0
  416. package/src/telemetry/tool-audit.ts +15 -9
  417. package/src/telemetry/tool-executed-events-store.test.ts +1 -1
  418. package/src/telemetry/turn-events-store.ts +20 -0
  419. package/src/telemetry/types.ts +45 -12
  420. package/src/telemetry/usage-telemetry-reporter.test.ts +294 -25
  421. package/src/telemetry/usage-telemetry-reporter.ts +117 -18
  422. package/src/telemetry/watchdog-direct-emit.test.ts +11 -3
  423. package/src/telemetry/watchdog-direct-emit.ts +11 -6
  424. package/src/tools/apps/executors.ts +11 -36
  425. package/src/tools/executor.ts +14 -1
  426. package/src/tools/host-terminal/host-shell.ts +6 -2
  427. package/src/tools/network/__tests__/web-fetch-firecrawl.test.ts +1 -1
  428. package/src/tools/network/__tests__/web-fetch-metadata.test.ts +25 -0
  429. package/src/tools/network/web-fetch.ts +6 -2
  430. package/src/tools/skills/sandbox-runner.ts +5 -2
  431. package/src/tools/subagent/spawn.ts +7 -11
  432. package/src/tools/terminal/shell.ts +5 -2
  433. package/src/tools/tool-manifest.ts +0 -2
  434. package/src/tools/tool-profiler.ts +37 -6
  435. package/src/tools/ui-surface/channel-variants.ts +101 -0
  436. package/src/tools/ui-surface/definitions.ts +23 -67
  437. package/src/tools/ui-surface/surface-shape-docs.ts +229 -0
  438. package/src/tts/__tests__/provider-adapters.test.ts +18 -0
  439. package/src/tts/provider-catalog.ts +2 -4
  440. package/src/tts/providers/deepgram-provider.ts +10 -1
  441. package/src/types/onboarding-context.ts +8 -0
  442. package/src/usage/attribution.ts +18 -112
  443. package/src/{config/bundled-skills/messaging/tools/gmail-mime-helpers.ts → util/mime-type.ts} +4 -1
  444. package/src/util/provider-error-patterns.ts +7 -1
  445. package/src/util/worker-process.ts +105 -0
  446. package/src/watcher/__tests__/telemetry.test.ts +17 -4
  447. package/src/watcher/telemetry.ts +5 -5
  448. package/src/workspace/migrations/131-drop-web-fetch-mode.ts +61 -0
  449. package/src/workspace/migrations/registry.ts +2 -0
  450. package/src/workspace/provider-commit-message-generator.ts +6 -4
  451. package/src/__tests__/app-open-proxy.test.ts +0 -67
  452. package/src/onboarding/onboarding-research-events-store.test.ts +0 -230
  453. package/src/onboarding/onboarding-research-events-store.ts +0 -104
  454. package/src/runtime/routes/assets/vellum-design-system.css +0 -2236
  455. package/src/tools/apps/definitions.ts +0 -73
  456. package/src/tools/apps/open-proxy.ts +0 -43
@@ -10,18 +10,18 @@ import type { LLMConfigBase, ProfileEntry } from "./schemas/llm.js";
10
10
 
11
11
  /**
12
12
  * Materializes a partial custom profile into a complete, standalone
13
- * override: what `resolveCallSiteConfig` produces when this profile is the
14
- * only profile layer above `llm.default`. Replicates the deep-merge
15
- * resolver's non-obvious rules exactly:
13
+ * override by filling absent fields from `dflt` (the workspace's default
14
+ * base config the legacy raw `llm.default` blob when one is still on
15
+ * disk, otherwise `LLMConfigBase` schema defaults). Single-winner
16
+ * resolution never merges one profile's fields into another, so a custom
17
+ * profile must carry its own provider and model to be a usable selection
18
+ * target — materialization is what completes it. Rules:
16
19
  *
17
- * - Non-null default `temperature`/`topP` ARE inherited winning-profile
18
- * scoping only blocks one profile's sampling from leaking under another;
19
- * `llm.default`'s base sampling stands when no profile opts in. Null
20
- * defaults are skipped (same resolved result, no noise). `logitBias` is
21
- * NEVER inherited the resolver deletes non-profile values post-merge.
22
- * - A model-only profile gets the provider `withImpliedProviders` would
23
- * stamp: the default provider when it serves the model, else the model's
24
- * catalog owner.
20
+ * - Non-null base `temperature`/`topP` ARE inherited; null values are
21
+ * skipped (same resolved result, no noise). `logitBias` is NEVER
22
+ * inherited it is profile-opt-in only.
23
+ * - A model-only profile gets the provider the catalog implies: the base
24
+ * provider when it serves the model, else the model's catalog owner.
25
25
  *
26
26
  * Mix profiles (no config fields, schema-enforced) and managed profiles
27
27
  * (bodies owned by the code catalog) pass through untouched. Idempotent,
@@ -60,135 +60,101 @@ import { ToolsConfigSchema } from "./schemas/tools.js";
60
60
  import { WorkflowsConfigSchema } from "./schemas/workflows.js";
61
61
  import { WorkspaceGitConfigSchema } from "./schemas/workspace-git.js";
62
62
 
63
- export const AssistantConfigSchema = z
64
- .object({
65
- services: ServicesSchema.default(ServicesSchema.parse({})),
66
- memory: MemoryConfigSchema.default(MemoryConfigSchema.parse({})),
67
- monitoring: MonitoringConfigSchema.default(
68
- MonitoringConfigSchema.parse({}),
63
+ export const AssistantConfigSchema = z.object({
64
+ services: ServicesSchema.default(ServicesSchema.parse({})),
65
+ memory: MemoryConfigSchema.default(MemoryConfigSchema.parse({})),
66
+ monitoring: MonitoringConfigSchema.default(MonitoringConfigSchema.parse({})),
67
+ migrations: MigrationsConfigSchema.default(MigrationsConfigSchema.parse({})),
68
+ dataDir: z
69
+ .string({ error: "dataDir must be a string" })
70
+ .default(getDataDir())
71
+ .describe("Directory for storing assistant data (database, logs, etc.)"),
72
+ timeouts: TimeoutConfigSchema.default(TimeoutConfigSchema.parse({})),
73
+ rateLimit: RateLimitConfigSchema.default(RateLimitConfigSchema.parse({})),
74
+ secretDetection: SecretDetectionConfigSchema.default(
75
+ SecretDetectionConfigSchema.parse({}),
76
+ ),
77
+ auditLog: AuditLogConfigSchema.default(AuditLogConfigSchema.parse({})),
78
+ logFile: LogFileConfigSchema.default(
79
+ LogFileConfigSchema.parse({ dir: getDataDir() + "/logs" }),
80
+ ),
81
+ // Unified LLM configuration block. The unique source of truth for
82
+ // provider/model/maxTokens/effort/speed/temperature/thinking/contextWindow
83
+ // and pricing overrides for every call site in the assistant.
84
+ //
85
+ // Default values live on each leaf inside `LLMSchema` (see
86
+ // `schemas/llm.ts`), so `LLMSchema.parse({})` returns a fully-populated
87
+ // object. This matches the pattern used by sibling schemas above and
88
+ // ensures the loader's leaf-deletion recovery path can repair a partially
89
+ // invalid `llm` block without falling back to `cloneDefaultConfig()`.
90
+ llm: LLMSchema.default(LLMSchema.parse({})),
91
+ llmRequestLogs: LlmRequestLogsConfigSchema,
92
+ filing: FilingConfigSchema.default(FilingConfigSchema.parse({})),
93
+ heartbeat: HeartbeatConfigSchema.default(HeartbeatConfigSchema.parse({})),
94
+ hostBrowser: HostBrowserConfigSchema.default(
95
+ HostBrowserConfigSchema.parse({}),
96
+ ),
97
+ conversations: ConversationsConfigSchema.default(
98
+ ConversationsConfigSchema.parse({}),
99
+ ),
100
+ journal: JournalConfigSchema.default(JournalConfigSchema.parse({})),
101
+ backup: BackupConfigSchema.default(BackupConfigSchema.parse({})),
102
+ mcp: McpConfigSchema.default(McpConfigSchema.parse({})),
103
+ acp: AcpConfigSchema.default(AcpConfigSchema.parse({})),
104
+ skills: SkillsConfigSchema.default(SkillsConfigSchema.parse({})),
105
+ workspaceGit: WorkspaceGitConfigSchema.default(
106
+ WorkspaceGitConfigSchema.parse({}),
107
+ ),
108
+ compaction: CompactionConfigSchema.default(CompactionConfigSchema.parse({})),
109
+ compactionLogs: CompactionLogsConfigSchema,
110
+ twilio: TwilioConfigSchema.default(TwilioConfigSchema.parse({})),
111
+ calls: CallsConfigSchema.default(CallsConfigSchema.parse({})),
112
+ liveVoice: LiveVoiceConfigSchema.default(LiveVoiceConfigSchema.parse({})),
113
+ whatsapp: WhatsAppConfigSchema.default(WhatsAppConfigSchema.parse({})),
114
+ telegram: TelegramConfigSchema.default(TelegramConfigSchema.parse({})),
115
+ slack: SlackConfigSchema.default(SlackConfigSchema.parse({})),
116
+ a2a: A2AConfigSchema.default(A2AConfigSchema.parse({})),
117
+ ingress: IngressConfigSchema,
118
+ platform: PlatformConfigSchema.default(PlatformConfigSchema.parse({})),
119
+ daemon: DaemonConfigSchema.default(DaemonConfigSchema.parse({})),
120
+ notifications: NotificationsConfigSchema.default(
121
+ NotificationsConfigSchema.parse({}),
122
+ ),
123
+ ui: UiConfigSchema.default(UiConfigSchema.parse({})),
124
+ tools: ToolsConfigSchema.default(ToolsConfigSchema.parse({})),
125
+ workflows: WorkflowsConfigSchema.default(WorkflowsConfigSchema.parse({})),
126
+ // Per-plugin config blocks keyed by plugin name. The schema is intentionally
127
+ // permissive — each plugin's manifest supplies its own validator which the
128
+ // plugin bootstrap (`external-plugins-bootstrap.ts`) runs against the raw
129
+ // block under `plugins.<name>` before handing the parsed result to the
130
+ // plugin's `init()`. Keeping this open here means adding a new plugin does
131
+ // not require a core-schema change, while invalid configs still surface
132
+ // through the plugin's own validator at bootstrap.
133
+ plugins: z
134
+ .record(z.string(), z.unknown())
135
+ .optional()
136
+ .describe(
137
+ "Per-plugin configuration keyed by plugin name. Validated downstream by each plugin's manifest.config validator at bootstrap.",
69
138
  ),
70
- migrations: MigrationsConfigSchema.default(
71
- MigrationsConfigSchema.parse({}),
139
+ legacyTelemetryOptOut: z
140
+ .boolean()
141
+ .optional()
142
+ .describe(
143
+ "Fail-closed telemetry marker: set for a workspace that had an explicit local usage-data opt-out before telemetry moved to platform share_analytics consent. While set, usage telemetry stays disabled regardless of platform consent.",
72
144
  ),
73
- dataDir: z
74
- .string({ error: "dataDir must be a string" })
75
- .default(getDataDir())
76
- .describe("Directory for storing assistant data (database, logs, etc.)"),
77
- timeouts: TimeoutConfigSchema.default(TimeoutConfigSchema.parse({})),
78
- rateLimit: RateLimitConfigSchema.default(RateLimitConfigSchema.parse({})),
79
- secretDetection: SecretDetectionConfigSchema.default(
80
- SecretDetectionConfigSchema.parse({}),
145
+ legacyDiagnosticsOptOut: z
146
+ .boolean()
147
+ .optional()
148
+ .describe(
149
+ "Fail-closed diagnostics marker: set for a workspace that had an explicit local sendDiagnostics opt-out before crash reporting moved to platform share_diagnostics consent. While set, Sentry stays disabled regardless of platform consent.",
81
150
  ),
82
- auditLog: AuditLogConfigSchema.default(AuditLogConfigSchema.parse({})),
83
- logFile: LogFileConfigSchema.default(
84
- LogFileConfigSchema.parse({ dir: getDataDir() + "/logs" }),
85
- ),
86
- // Unified LLM configuration block. The unique source of truth for
87
- // provider/model/maxTokens/effort/speed/temperature/thinking/contextWindow
88
- // and pricing overrides for every call site in the assistant.
89
- //
90
- // Default values live on each leaf inside `LLMSchema` (see
91
- // `schemas/llm.ts`), so `LLMSchema.parse({})` returns a fully-populated
92
- // object. This matches the pattern used by sibling schemas above and
93
- // ensures the loader's leaf-deletion recovery path can repair a partially
94
- // invalid `llm` block without falling back to `cloneDefaultConfig()`.
95
- llm: LLMSchema.default(LLMSchema.parse({})),
96
- llmRequestLogs: LlmRequestLogsConfigSchema,
97
- filing: FilingConfigSchema.default(FilingConfigSchema.parse({})),
98
- heartbeat: HeartbeatConfigSchema.default(HeartbeatConfigSchema.parse({})),
99
- hostBrowser: HostBrowserConfigSchema.default(
100
- HostBrowserConfigSchema.parse({}),
101
- ),
102
- conversations: ConversationsConfigSchema.default(
103
- ConversationsConfigSchema.parse({}),
104
- ),
105
- journal: JournalConfigSchema.default(JournalConfigSchema.parse({})),
106
- backup: BackupConfigSchema.default(BackupConfigSchema.parse({})),
107
- mcp: McpConfigSchema.default(McpConfigSchema.parse({})),
108
- acp: AcpConfigSchema.default(AcpConfigSchema.parse({})),
109
- skills: SkillsConfigSchema.default(SkillsConfigSchema.parse({})),
110
- workspaceGit: WorkspaceGitConfigSchema.default(
111
- WorkspaceGitConfigSchema.parse({}),
112
- ),
113
- compaction: CompactionConfigSchema.default(
114
- CompactionConfigSchema.parse({}),
115
- ),
116
- compactionLogs: CompactionLogsConfigSchema,
117
- twilio: TwilioConfigSchema.default(TwilioConfigSchema.parse({})),
118
- calls: CallsConfigSchema.default(CallsConfigSchema.parse({})),
119
- liveVoice: LiveVoiceConfigSchema.default(LiveVoiceConfigSchema.parse({})),
120
- whatsapp: WhatsAppConfigSchema.default(WhatsAppConfigSchema.parse({})),
121
- telegram: TelegramConfigSchema.default(TelegramConfigSchema.parse({})),
122
- slack: SlackConfigSchema.default(SlackConfigSchema.parse({})),
123
- a2a: A2AConfigSchema.default(A2AConfigSchema.parse({})),
124
- ingress: IngressConfigSchema,
125
- platform: PlatformConfigSchema.default(PlatformConfigSchema.parse({})),
126
- daemon: DaemonConfigSchema.default(DaemonConfigSchema.parse({})),
127
- notifications: NotificationsConfigSchema.default(
128
- NotificationsConfigSchema.parse({}),
129
- ),
130
- ui: UiConfigSchema.default(UiConfigSchema.parse({})),
131
- tools: ToolsConfigSchema.default(ToolsConfigSchema.parse({})),
132
- workflows: WorkflowsConfigSchema.default(WorkflowsConfigSchema.parse({})),
133
- // Per-plugin config blocks keyed by plugin name. The schema is intentionally
134
- // permissive — each plugin's manifest supplies its own validator which the
135
- // plugin bootstrap (`external-plugins-bootstrap.ts`) runs against the raw
136
- // block under `plugins.<name>` before handing the parsed result to the
137
- // plugin's `init()`. Keeping this open here means adding a new plugin does
138
- // not require a core-schema change, while invalid configs still surface
139
- // through the plugin's own validator at bootstrap.
140
- plugins: z
141
- .record(z.string(), z.unknown())
142
- .optional()
143
- .describe(
144
- "Per-plugin configuration keyed by plugin name. Validated downstream by each plugin's manifest.config validator at bootstrap.",
145
- ),
146
- legacyTelemetryOptOut: z
147
- .boolean()
148
- .optional()
149
- .describe(
150
- "Fail-closed telemetry marker: set for a workspace that had an explicit local usage-data opt-out before telemetry moved to platform share_analytics consent. While set, usage telemetry stays disabled regardless of platform consent.",
151
- ),
152
- legacyDiagnosticsOptOut: z
153
- .boolean()
154
- .optional()
155
- .describe(
156
- "Fail-closed diagnostics marker: set for a workspace that had an explicit local sendDiagnostics opt-out before crash reporting moved to platform share_diagnostics consent. While set, Sentry stays disabled regardless of platform consent.",
157
- ),
158
- maxStepsPerSession: z
159
- .number({ error: "maxStepsPerSession must be a number" })
160
- .int("maxStepsPerSession must be an integer")
161
- .min(1, "maxStepsPerSession must be >= 1")
162
- .max(200, "maxStepsPerSession must be <= 200")
163
- .default(50)
164
- .describe("Maximum number of computer-use steps per session"),
165
- })
166
- .superRefine((config, ctx) => {
167
- const llmContextWindow = config.llm?.default?.contextWindow;
168
- if (
169
- llmContextWindow?.targetBudgetRatio != null &&
170
- llmContextWindow?.compactThreshold != null &&
171
- llmContextWindow.targetBudgetRatio >= llmContextWindow.compactThreshold
172
- ) {
173
- ctx.addIssue({
174
- code: z.ZodIssueCode.custom,
175
- path: ["llm", "default", "contextWindow", "targetBudgetRatio"],
176
- message:
177
- "llm.default.contextWindow.targetBudgetRatio must be less than llm.default.contextWindow.compactThreshold",
178
- });
179
- }
180
- if (
181
- llmContextWindow?.targetBudgetRatio != null &&
182
- llmContextWindow?.summaryBudgetRatio != null &&
183
- llmContextWindow.targetBudgetRatio <= llmContextWindow.summaryBudgetRatio
184
- ) {
185
- ctx.addIssue({
186
- code: z.ZodIssueCode.custom,
187
- path: ["llm", "default", "contextWindow", "targetBudgetRatio"],
188
- message:
189
- "llm.default.contextWindow.targetBudgetRatio must be greater than llm.default.contextWindow.summaryBudgetRatio",
190
- });
191
- }
192
- });
151
+ maxStepsPerSession: z
152
+ .number({ error: "maxStepsPerSession must be a number" })
153
+ .int("maxStepsPerSession must be an integer")
154
+ .min(1, "maxStepsPerSession must be >= 1")
155
+ .max(200, "maxStepsPerSession must be <= 200")
156
+ .default(50)
157
+ .describe("Maximum number of computer-use steps per session"),
158
+ });
193
159
 
194
160
  export type AssistantConfig = z.infer<typeof AssistantConfigSchema>;
@@ -11,9 +11,9 @@ describe("LiveVoiceVadConfigSchema", () => {
11
11
  const parsed = LiveVoiceVadConfigSchema.parse({});
12
12
  expect(parsed).toEqual({
13
13
  speechEnergyThreshold: 800,
14
- silenceThresholdMs: 800,
14
+ silenceThresholdMs: 1200,
15
15
  maxTurnDurationMs: 30_000,
16
- bargeInMinSpeechMs: 60,
16
+ bargeInMinSpeechMs: 250,
17
17
  });
18
18
  });
19
19
 
@@ -64,22 +64,40 @@ describe("LiveVoiceConfigSchema", () => {
64
64
  mode: "open-mic",
65
65
  vad: {
66
66
  speechEnergyThreshold: 800,
67
- silenceThresholdMs: 800,
67
+ silenceThresholdMs: 1200,
68
68
  maxTurnDurationMs: 30_000,
69
- bargeInMinSpeechMs: 60,
69
+ bargeInMinSpeechMs: 250,
70
70
  },
71
71
  maxSessionDurationSeconds: 1800,
72
+ // Off by default: voice turns carry only their transcript, no audio
73
+ // artifacts on the conversation messages (JARVIS-1283).
74
+ archiveAudio: false,
72
75
  });
73
76
  });
74
77
 
78
+ test("archiveAudio can be enabled", () => {
79
+ expect(
80
+ LiveVoiceConfigSchema.parse({ archiveAudio: true }).archiveAudio,
81
+ ).toBe(true);
82
+ });
83
+
84
+ test("rejects a non-boolean archiveAudio", () => {
85
+ const result = LiveVoiceConfigSchema.safeParse({ archiveAudio: "yes" });
86
+ expect(result.success).toBe(false);
87
+ if (!result.success) {
88
+ const msgs = result.error.issues.map((i) => i.message);
89
+ expect(msgs.some((m) => m.includes("liveVoice.archiveAudio"))).toBe(true);
90
+ }
91
+ });
92
+
75
93
  test("accepts overrides", () => {
76
94
  const parsed = LiveVoiceConfigSchema.parse({
77
95
  mode: "ptt",
78
- vad: { silenceThresholdMs: 1200 },
96
+ vad: { silenceThresholdMs: 900 },
79
97
  maxSessionDurationSeconds: 600,
80
98
  });
81
99
  expect(parsed.mode).toBe("ptt");
82
- expect(parsed.vad.silenceThresholdMs).toBe(1200);
100
+ expect(parsed.vad.silenceThresholdMs).toBe(900);
83
101
  // Unspecified vad fields still get defaults
84
102
  expect(parsed.vad.speechEnergyThreshold).toBe(800);
85
103
  expect(parsed.vad.maxTurnDurationMs).toBe(30_000);
@@ -41,6 +41,31 @@ describe("SttServiceSchema", () => {
41
41
  test("VALID_STT_PROVIDERS includes deepgram", () => {
42
42
  expect(VALID_STT_PROVIDERS).toContain("deepgram");
43
43
  });
44
+
45
+ test("normalizes the openai/whisper aliases to openai-whisper", () => {
46
+ expect(SttServiceSchema.parse({ provider: "openai" }).provider).toBe(
47
+ "openai-whisper",
48
+ );
49
+ expect(SttServiceSchema.parse({ provider: "whisper" }).provider).toBe(
50
+ "openai-whisper",
51
+ );
52
+ // Case- and whitespace-tolerant.
53
+ expect(SttServiceSchema.parse({ provider: " OpenAI " }).provider).toBe(
54
+ "openai-whisper",
55
+ );
56
+ });
57
+
58
+ test("a canonical provider is unchanged by the alias preprocessor", () => {
59
+ expect(
60
+ SttServiceSchema.parse({ provider: "openai-whisper" }).provider,
61
+ ).toBe("openai-whisper");
62
+ });
63
+
64
+ test("rejects an unknown provider with a helpful message", () => {
65
+ expect(() => SttServiceSchema.parse({ provider: "nope" })).toThrow(
66
+ /must be one of/,
67
+ );
68
+ });
44
69
  });
45
70
 
46
71
  describe("managed mode", () => {
@@ -61,15 +86,18 @@ describe("managed mode", () => {
61
86
  expect(parsed.provider).toBe("vellum");
62
87
  });
63
88
 
64
- test("rejects provider vellum under your-own mode", () => {
89
+ test("accepts provider vellum regardless of mode", () => {
65
90
  const result = SttServiceSchema.safeParse({
66
91
  mode: "your-own",
67
92
  provider: "vellum",
68
93
  });
69
- expect(result.success).toBe(false);
94
+ expect(result.success).toBe(true);
70
95
  });
71
96
 
72
- test("effectiveSttProvider routes managed mode to vellum and preserves BYOK otherwise", () => {
97
+ test("effectiveSttProvider routes provider vellum and managed mode to vellum, preserves BYOK otherwise", () => {
98
+ expect(effectiveSttProvider({ mode: "your-own", provider: "vellum" })).toBe(
99
+ "vellum",
100
+ );
73
101
  expect(
74
102
  effectiveSttProvider({ mode: "managed", provider: "deepgram" }),
75
103
  ).toBe("vellum");
@@ -20,9 +20,9 @@ export const LiveVoiceVadConfigSchema = z
20
20
  .number({ error: "liveVoice.vad.silenceThresholdMs must be a number" })
21
21
  .int("liveVoice.vad.silenceThresholdMs must be an integer")
22
22
  .positive("liveVoice.vad.silenceThresholdMs must be a positive integer")
23
- .default(800)
23
+ .default(1200)
24
24
  .describe(
25
- "Trailing silence duration (ms) after speech that ends the user's turn",
25
+ "Trailing silence duration (ms) after speech that ends the user's turn — the default 'pause before reply'. Clients may override it per-session via the start frame.",
26
26
  ),
27
27
  maxTurnDurationMs: z
28
28
  .number({ error: "liveVoice.vad.maxTurnDurationMs must be a number" })
@@ -38,9 +38,9 @@ export const LiveVoiceVadConfigSchema = z
38
38
  .nonnegative(
39
39
  "liveVoice.vad.bargeInMinSpeechMs must be a nonnegative integer",
40
40
  )
41
- .default(60)
41
+ .default(250)
42
42
  .describe(
43
- "Sustained speech (ms) required before speech during assistant playback interrupts it; 0 disables the guard",
43
+ "Sustained speech (ms) required before speech during assistant playback interrupts it — the default 'interrupt sensitivity' (higher = harder to interrupt). 0 disables the guard. Clients may override it per-session via the start frame. Raised from 60 so brief TTS bleed through imperfect echo cancellation no longer self-interrupts the assistant.",
44
44
  ),
45
45
  })
46
46
  .describe(
@@ -68,6 +68,12 @@ export const LiveVoiceConfigSchema = z
68
68
  )
69
69
  .default(1800)
70
70
  .describe("Maximum duration of a single live voice session in seconds"),
71
+ archiveAudio: z
72
+ .boolean({ error: "liveVoice.archiveAudio must be a boolean" })
73
+ .default(false)
74
+ .describe(
75
+ "Persist the recorded user + assistant audio of each voice turn as attachments on the conversation messages. Off by default: voice turns carry only their transcribed text, so no audio-file artifacts land in the conversation history. Enable for playback/debugging.",
76
+ ),
71
77
  })
72
78
  .describe(
73
79
  "Live voice (in-app duplex audio) configuration — mic mode, VAD tuning, and session limits",
@@ -31,6 +31,7 @@ export const LLMProvider = z
31
31
  "minimax",
32
32
  "atlascloud",
33
33
  "together",
34
+ "baseten",
34
35
  ])
35
36
  .meta({ id: "LLMProvider" });
36
37
  type LLMProvider = z.infer<typeof LLMProvider>;
@@ -261,14 +262,46 @@ export type ContextWindow = z.infer<typeof ContextWindowSchema>;
261
262
  // injected. Nested `overflowRecovery` likewise uses its fragment view, so a
262
263
  // partial override like `{ overflowRecovery: { maxAttempts: 5 } }` produces
263
264
  // exactly that and nothing else.
264
- const ContextWindowDeepPartialSchema = z.object({
265
- enabled: ContextEnabledSchema.optional(),
266
- maxInputTokens: ContextMaxInputTokensSchema.optional(),
267
- targetBudgetRatio: ContextTargetBudgetRatioSchema.optional(),
268
- compactThreshold: ContextCompactThresholdSchema.optional(),
269
- summaryBudgetRatio: ContextSummaryBudgetRatioSchema.optional(),
270
- overflowRecovery: ContextOverflowRecoveryFragmentSchema.optional(),
271
- });
265
+ //
266
+ // Cross-field ordering (targetBudgetRatio < compactThreshold, and
267
+ // targetBudgetRatio > summaryBudgetRatio) is enforced only when both sides of
268
+ // a pair are present in the same fragment — a partial override merges with
269
+ // lower layers at resolution time, so a lone field can't be judged here.
270
+ const ContextWindowDeepPartialSchema = z
271
+ .object({
272
+ enabled: ContextEnabledSchema.optional(),
273
+ maxInputTokens: ContextMaxInputTokensSchema.optional(),
274
+ targetBudgetRatio: ContextTargetBudgetRatioSchema.optional(),
275
+ compactThreshold: ContextCompactThresholdSchema.optional(),
276
+ summaryBudgetRatio: ContextSummaryBudgetRatioSchema.optional(),
277
+ overflowRecovery: ContextOverflowRecoveryFragmentSchema.optional(),
278
+ })
279
+ .superRefine((cw, ctx) => {
280
+ if (
281
+ cw.targetBudgetRatio != null &&
282
+ cw.compactThreshold != null &&
283
+ cw.targetBudgetRatio >= cw.compactThreshold
284
+ ) {
285
+ ctx.addIssue({
286
+ code: z.ZodIssueCode.custom,
287
+ path: ["targetBudgetRatio"],
288
+ message:
289
+ "contextWindow.targetBudgetRatio must be less than contextWindow.compactThreshold",
290
+ });
291
+ }
292
+ if (
293
+ cw.targetBudgetRatio != null &&
294
+ cw.summaryBudgetRatio != null &&
295
+ cw.targetBudgetRatio <= cw.summaryBudgetRatio
296
+ ) {
297
+ ctx.addIssue({
298
+ code: z.ZodIssueCode.custom,
299
+ path: ["targetBudgetRatio"],
300
+ message:
301
+ "contextWindow.targetBudgetRatio must be greater than contextWindow.summaryBudgetRatio",
302
+ });
303
+ }
304
+ });
272
305
 
273
306
  // ---------------------------------------------------------------------------
274
307
  // OpenRouter provider-routing preferences
@@ -318,10 +351,11 @@ const PricingOverrideSchema = z.object({
318
351
  // ---------------------------------------------------------------------------
319
352
 
320
353
  /**
321
- * Fully specified LLM config. Used for `llm.default` — every knob has a
322
- * schema-level default, so `LLMConfigBase.parse({})` returns a complete
323
- * fallback object. This is essential for the loader's leaf-deletion recovery
324
- * path; see the comment on `ThinkingSchema` above.
354
+ * Fully specified LLM config: every knob has a schema-level default, so
355
+ * `LLMConfigBase.parse({})` returns a complete object. The resolver uses it as
356
+ * the code-owned base every resolved call-site config composes over (see
357
+ * `CODE_DEFAULT_BASE` in `llm-resolver.ts`), and profile materialization
358
+ * completes partial custom profiles against it.
325
359
  */
326
360
  export const LLMConfigBase = z.object({
327
361
  provider: LLMProvider.default("anthropic"),
@@ -510,7 +544,6 @@ const DefaultProviderField = DefaultProviderSchema.optional().catch(undefined);
510
544
 
511
545
  export const LLMSchema = z
512
546
  .object({
513
- default: LLMConfigBase.default(LLMConfigBase.parse({})),
514
547
  profiles: z.record(z.string().min(1), ProfileEntry).default({}),
515
548
  // Presentation-only order for named profiles. The resolver ignores this;
516
549
  // clients use it to render profile pickers consistently.
@@ -551,7 +584,9 @@ export const LLMSchema = z
551
584
  ...DEFAULT_PROFILE_KEYS,
552
585
  ]);
553
586
  for (const [siteId, siteConfig] of Object.entries(config.callSites ?? {})) {
554
- if (siteConfig?.profile == null) continue;
587
+ if (siteConfig?.profile == null) {
588
+ continue;
589
+ }
555
590
  if (!profileNames.has(siteConfig.profile)) {
556
591
  ctx.addIssue({
557
592
  code: "custom",
@@ -596,7 +631,9 @@ export const LLMSchema = z
596
631
  .map(([name]) => name),
597
632
  );
598
633
  for (const [name, profile] of Object.entries(config.profiles ?? {})) {
599
- if (profile?.mix == null) continue;
634
+ if (profile?.mix == null) {
635
+ continue;
636
+ }
600
637
  // (d) A mix must not also carry model config — the resolved config comes
601
638
  // entirely from the chosen constituent.
602
639
  for (const key of MIX_DISALLOWED_CONFIG_KEYS) {
@@ -137,7 +137,7 @@ export const MemoryCleanupConfigSchema = z
137
137
  .nullable()
138
138
  .default(1 * 60 * 60 * 1000)
139
139
  .describe(
140
- "Retention period for LLM request/response logs in milliseconds (null keeps forever, 0 prunes immediately)",
140
+ "Retention period for LLM request/response logs in milliseconds (null or 0 disables pruning / keeps forever)",
141
141
  ),
142
142
  })
143
143
  .describe("Automatic memory cleanup and garbage collection settings");
@@ -57,6 +57,14 @@ export const MemoryRetrospectiveConfigSchema = z
57
57
  .describe(
58
58
  "When true, fork-based retrospectives run under the source conversation's inference profile (which forkConversation copies onto the fork) instead of the call site's default. Provider prompt caches are byte-exact prefix matches scoped per model, and a thinking enable/disable mismatch invalidates the messages cache tier — so reusing the source's cached prefix requires the retrospective to resolve the SAME model/thinking/effort as the conversation's own turns. Falls back to the call site's default when the conversation has no profile or the referenced profile no longer exists.",
59
59
  ),
60
+
61
+ promptPath: z
62
+ .string({ error: "memory.retrospective.promptPath must be a string" })
63
+ .nullable()
64
+ .default(null)
65
+ .describe(
66
+ "Optional path to a file whose contents replace the bundled retrospective fork-instruction prompt. Relative paths resolve under the workspace root; absolute paths and a leading `~/` (expanded to the home directory) are honored only when they still resolve inside the workspace root — a path that lands outside the workspace (including via symlinks) is rejected. The loaded contents may include `{{AVAILABLE_TOOLS_LINE}}`, `{{WINDOW_ANCHOR}}`, `{{ALREADY_REMEMBERED}}`, and `{{SKILL_AUTHORING_SECTION}}`, which are substituted at runtime. If the file is rejected, missing, unreadable, or empty, the bundled prompt is used and a warning is logged.",
67
+ ),
60
68
  })
61
69
  .describe(
62
70
  "Controls the memory-retrospective background pass. Model selection lives under llm.callSites.memoryRetrospective.",
@@ -235,7 +235,7 @@ export const MemoryV2ConfigSchema = z
235
235
  .nullable()
236
236
  .default(null)
237
237
  .describe(
238
- "Optional path to a file whose contents replace the bundled consolidation prompt. Absolute paths are used as-is, a leading `~/` is expanded to the home directory, otherwise the path is resolved under the workspace root. The loaded contents may include `{{CUTOFF}}`, which is substituted with the run's ISO-8601 cutoff timestamp. If the file is missing, unreadable, or empty, the bundled prompt is used and a warning is logged.",
238
+ "Optional path to a file whose contents replace the bundled consolidation prompt. Relative paths resolve under the workspace root; absolute paths and a leading `~/` (expanded to the home directory) are honored only when they still resolve inside the workspace root — a path that lands outside the workspace (including via symlinks) is rejected. The loaded contents may include `{{CUTOFF}}`, which is substituted with the run's ISO-8601 cutoff timestamp. If the file is rejected, missing, unreadable, or empty, the bundled prompt is used and a warning is logged.",
239
239
  ),
240
240
  rerank: z
241
241
  .object({
@@ -306,7 +306,7 @@ export const MemoryV2ConfigSchema = z
306
306
  .nullable()
307
307
  .default(null)
308
308
  .describe(
309
- "Optional path to a file whose contents replace the bundled router prompt. Absolute paths are used as-is, a leading `~/` is expanded to the home directory, otherwise the path is resolved under the workspace root. The loaded contents may include `{{ASSISTANT_NAME}}`, `{{USER_NAME}}`, and `{{PAGE_INDEX}}`, which are substituted at runtime. If the file is missing, unreadable, or empty, the bundled prompt is used and a warning is logged.",
309
+ "Optional path to a file whose contents replace the bundled router prompt. Relative paths resolve under the workspace root; absolute paths and a leading `~/` (expanded to the home directory) are honored only when they still resolve inside the workspace root — a path that lands outside the workspace (including via symlinks) is rejected. The loaded contents may include `{{ASSISTANT_NAME}}`, `{{USER_NAME}}`, and `{{PAGE_INDEX}}`, which are substituted at runtime. If the file is rejected, missing, unreadable, or empty, the bundled prompt is used and a warning is logged.",
310
310
  ),
311
311
  batch_size: z
312
312
  .number()
@@ -394,7 +394,7 @@ export const MemoryV3ConfigSchema = z
394
394
  .nullable()
395
395
  .default(null)
396
396
  .describe(
397
- "Optional path to a file whose contents replace the bundled per-turn selector system prompt (the instructions that tell the selector which candidate pages to keep). Absolute paths are used as-is, a leading `~/` is expanded to the home directory, otherwise the path is resolved under the workspace root. The selector prompt takes no placeholders — the candidate pool is supplied separately as the user message — so the file is used verbatim. If the file is missing, unreadable, empty, or over 1 MiB, the bundled prompt is used and a warning is logged.",
397
+ "Optional path to a file whose contents replace the bundled per-turn selector system prompt (the instructions that tell the selector which candidate pages to keep). Relative paths resolve under the workspace root; absolute paths and a leading `~/` (expanded to the home directory) are honored only when they still resolve inside the workspace root — a path that lands outside the workspace (including via symlinks) is rejected. The selector prompt takes no placeholders — the candidate pool is supplied separately as the user message — so the file is used verbatim. If the file is rejected, missing, unreadable, empty, or over 1 MiB, the bundled prompt is used and a warning is logged.",
398
398
  ),
399
399
  edge: MemoryV3EdgeSchema.default(MemoryV3EdgeSchema.parse({})),
400
400
  entity: MemoryV3EntitySchema.default(MemoryV3EntitySchema.parse({})),
@@ -66,12 +66,15 @@ const WebSearchServiceSchema = BaseServiceSchema.extend({
66
66
  .default("inference-provider-native"),
67
67
  });
68
68
 
69
- const WebFetchServiceSchema = BaseServiceSchema.extend({
69
+ /**
70
+ * Web-fetch carries no `mode`: there is no managed proxy for it, so the only
71
+ * axis is which provider backs the tool.
72
+ */
73
+ const WebFetchServiceSchema = z.object({
70
74
  // Provider that backs the `web_fetch` tool. `default` is the daemon's
71
75
  // built-in HTTP fetch + extract path (no key). BYOK providers (e.g.
72
76
  // `firecrawl`) scrape via their hosted API and reuse the same stored key as
73
- // their web-search counterpart. The `mode` field is inherited from
74
- // `BaseServiceSchema` for symmetry; web-fetch has no managed proxy today.
77
+ // their web-search counterpart.
75
78
  provider: z.enum(VALID_WEB_FETCH_PROVIDERS).default("default"),
76
79
  });
77
80