@vellumai/assistant 0.11.2 → 0.11.3-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 (720) hide show
  1. package/ARCHITECTURE.md +58 -3
  2. package/docs/trusted-contact-access.md +9 -5
  3. package/docs/vellum-doctor.md +195 -0
  4. package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/channels.ts +39 -0
  5. package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/ingress.ts +10 -0
  6. package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/remote-web-pairing.ts +6 -0
  7. package/node_modules/@vellumai/ces-client/src/index.ts +1 -0
  8. package/node_modules/@vellumai/ces-client/src/rpc-client.ts +19 -2
  9. package/node_modules/@vellumai/environments/package.json +2 -1
  10. package/node_modules/@vellumai/environments/src/__tests__/cloud-assistant-hub-url.test.ts +38 -0
  11. package/node_modules/@vellumai/environments/src/__tests__/install-layout.test.ts +98 -0
  12. package/node_modules/@vellumai/environments/src/__tests__/package-boundary.test.ts +5 -5
  13. package/node_modules/@vellumai/environments/src/index.ts +17 -6
  14. package/node_modules/@vellumai/environments/src/install-layout.ts +49 -0
  15. package/node_modules/@vellumai/environments/src/seeds.ts +29 -0
  16. package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/channels.ts +39 -0
  17. package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/ingress.ts +10 -0
  18. package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/remote-web-pairing.ts +6 -0
  19. package/node_modules/@vellumai/gateway-client/src/index.ts +4 -0
  20. package/node_modules/@vellumai/gateway-client/src/verification-session-contract.ts +91 -6
  21. package/node_modules/@vellumai/ipc-server-utils/src/endpoint.test.ts +36 -0
  22. package/node_modules/@vellumai/ipc-server-utils/src/endpoint.ts +142 -0
  23. package/node_modules/@vellumai/ipc-server-utils/src/index.ts +2 -0
  24. package/node_modules/@vellumai/ipc-server-utils/src/listen-options.ts +3 -0
  25. package/node_modules/@vellumai/ipc-server-utils/src/socket-watchdog.test.ts +15 -1
  26. package/node_modules/@vellumai/ipc-server-utils/src/socket-watchdog.ts +17 -2
  27. package/node_modules/@vellumai/service-contracts/src/channels.ts +39 -0
  28. package/node_modules/@vellumai/service-contracts/src/ingress.ts +10 -0
  29. package/node_modules/@vellumai/service-contracts/src/remote-web-pairing.ts +6 -0
  30. package/node_modules/@vellumai/slack-text/src/index.ts +16 -2
  31. package/openapi.yaml +589 -30
  32. package/package.json +1 -1
  33. package/src/__tests__/agent-image-optimize.test.ts +4 -4
  34. package/src/__tests__/agent-loop-mutable-latest-user-message.test.ts +113 -66
  35. package/src/__tests__/agent-loop-override-profile.test.ts +38 -48
  36. package/src/__tests__/agent-loop-subagent-billing-headers.test.ts +166 -0
  37. package/src/__tests__/agent-wake-disk-pressure-callsite.test.ts +1 -0
  38. package/src/__tests__/agent-wake-override-profile.test.ts +1 -0
  39. package/src/__tests__/answered-question-persistence.test.ts +240 -0
  40. package/src/__tests__/anthropic-provider.test.ts +165 -30
  41. package/src/__tests__/app-control-flow.test.ts +9 -8
  42. package/src/__tests__/assistant-event-hub.test.ts +48 -0
  43. package/src/__tests__/attachments-store-heic-normalize.test.ts +16 -12
  44. package/src/__tests__/attachments-store.test.ts +130 -68
  45. package/src/__tests__/attachments.test.ts +36 -36
  46. package/src/__tests__/background-workers-disk-pressure.test.ts +1 -1
  47. package/src/__tests__/call-start-guardian-guard.test.ts +18 -4
  48. package/src/__tests__/canned-reply-release.test.ts +86 -0
  49. package/src/__tests__/channel-guardian.test.ts +59 -29
  50. package/src/__tests__/channel-readiness-telegram-remote.test.ts +154 -0
  51. package/src/__tests__/channel-setup-panel-ack.test.ts +6 -5
  52. package/src/__tests__/client-os-metadata-persistence.test.ts +103 -4
  53. package/src/__tests__/compactor-image-manifest-trust.test.ts +1 -1
  54. package/src/__tests__/consult-deadline.test.ts +65 -0
  55. package/src/__tests__/conversation-agent-loop.test.ts +438 -2
  56. package/src/__tests__/conversation-disk-view-integration.test.ts +5 -1
  57. package/src/__tests__/conversation-disk-view.test.ts +4 -4
  58. package/src/__tests__/conversation-error.test.ts +22 -3
  59. package/src/__tests__/conversation-fork-crud.test.ts +5 -1
  60. package/src/__tests__/conversation-fork-referential.test.ts +352 -0
  61. package/src/__tests__/conversation-fork-retrospective.test.ts +5 -1
  62. package/src/__tests__/conversation-group-tools.test.ts +32 -2
  63. package/src/__tests__/conversation-inference-profile-route.test.ts +27 -1
  64. package/src/__tests__/conversation-initial-prompt.test.ts +51 -22
  65. package/src/__tests__/conversation-key-store-bootstrap-cleanup.test.ts +119 -0
  66. package/src/__tests__/conversation-key-store-origin.test.ts +105 -0
  67. package/src/__tests__/conversation-list-recency-ordering.test.ts +104 -0
  68. package/src/__tests__/conversation-origin-at-creation.test.ts +153 -0
  69. package/src/__tests__/conversation-origin-channel-filter.test.ts +95 -0
  70. package/src/__tests__/conversation-placement-promotion.test.ts +172 -0
  71. package/src/__tests__/conversation-process-app-control-preactivation.test.ts +123 -0
  72. package/src/__tests__/conversation-queue.test.ts +433 -1
  73. package/src/__tests__/conversation-retry-route.test.ts +3 -1
  74. package/src/__tests__/conversation-routes-hidden-queue.test.ts +64 -1
  75. package/src/__tests__/conversation-store-ephemeral.test.ts +4 -0
  76. package/src/__tests__/conversation-store.test.ts +26 -10
  77. package/src/__tests__/conversation-summarize-route.test.ts +1 -1
  78. package/src/__tests__/conversation-surfaces-action-delivery.test.ts +15 -6
  79. package/src/__tests__/conversation-surfaces-activation-emit.test.ts +6 -5
  80. package/src/__tests__/conversation-surfaces-app-control.test.ts +12 -11
  81. package/src/__tests__/conversation-surfaces-app-open.test.ts +11 -5
  82. package/src/__tests__/conversation-surfaces-data-persist.test.ts +5 -4
  83. package/src/__tests__/conversation-surfaces-history-restored-completion.test.ts +967 -0
  84. package/src/__tests__/conversation-surfaces-persisted-info.test.ts +312 -0
  85. package/src/__tests__/conversation-surfaces-queued-emit.test.ts +6 -5
  86. package/src/__tests__/conversation-surfaces-standalone-payloads.test.ts +12 -8
  87. package/src/__tests__/conversation-surfaces-standalone.test.ts +21 -11
  88. package/src/__tests__/conversation-surfaces-state-update.test.ts +7 -11
  89. package/src/__tests__/conversation-surfaces-table-action.test.ts +7 -6
  90. package/src/__tests__/conversation-surfaces-task-progress.test.ts +11 -6
  91. package/src/__tests__/conversation-tool-setup-app-refresh.test.ts +9 -6
  92. package/src/__tests__/conversation-tool-setup-attribution.test.ts +70 -7
  93. package/src/__tests__/conversation-wait-for-idle.test.ts +6 -11
  94. package/src/__tests__/credential-prompt-route.test.ts +57 -7
  95. package/src/__tests__/credential-security-invariants.test.ts +1 -0
  96. package/src/__tests__/cu-unified-flow.test.ts +89 -28
  97. package/src/__tests__/custom-group-listing-visibility.test.ts +1 -1
  98. package/src/__tests__/db-schedule-syntax-migration.test.ts +4 -1
  99. package/src/__tests__/discord-access-request-privacy.test.ts +366 -0
  100. package/src/__tests__/discord-requester-notice-privacy.test.ts +87 -0
  101. package/src/__tests__/disk-pressure-guard.test.ts +55 -55
  102. package/src/__tests__/disk-pressure-routes.test.ts +10 -10
  103. package/src/__tests__/disk-pressure-tools.test.ts +2 -2
  104. package/src/__tests__/disk-usage.test.ts +35 -20
  105. package/src/__tests__/document-sync-tags.test.ts +379 -0
  106. package/src/__tests__/document-update-default-surface.test.ts +28 -0
  107. package/src/__tests__/dynamic-skill-background-guard.test.ts +2 -2
  108. package/src/__tests__/emit-signal-routing-intent.test.ts +3 -4
  109. package/src/__tests__/ensure-conversation-exists.test.ts +8 -5
  110. package/src/__tests__/events-dev-bypass-actor.test.ts +183 -1
  111. package/src/__tests__/evict-conversations-for-reload.test.ts +90 -0
  112. package/src/__tests__/file-list-tool.test.ts +12 -10
  113. package/src/__tests__/file-ops-service.test.ts +151 -44
  114. package/src/__tests__/file-read-container-boundary.test.ts +137 -0
  115. package/src/__tests__/file-read-tool.test.ts +21 -9
  116. package/src/__tests__/file-url-path-guard.test.ts +83 -0
  117. package/src/__tests__/filesystem-tools.test.ts +82 -66
  118. package/src/__tests__/guardian-outbound-http.test.ts +8 -8
  119. package/src/__tests__/helpers/gateway-verification-sessions-stub.ts +14 -1
  120. package/src/__tests__/helpers/hub-clients.ts +42 -0
  121. package/src/__tests__/helpers/mock-conversation.ts +46 -0
  122. package/src/__tests__/helpers/verification-sessions-ipc-sim.ts +61 -57
  123. package/src/__tests__/history-repair.test.ts +112 -12
  124. package/src/__tests__/host-bash-proxy.test.ts +10 -5
  125. package/src/__tests__/host-bash-routes.test.ts +67 -0
  126. package/src/__tests__/host-browser-proxy.test.ts +2 -2
  127. package/src/__tests__/host-cu-proxy.test.ts +3 -7
  128. package/src/__tests__/host-file-proxy.test.ts +3 -7
  129. package/src/__tests__/host-file-read-tool.test.ts +5 -1
  130. package/src/__tests__/host-transfer-proxy-targeted.test.ts +6 -4
  131. package/src/__tests__/http-conversation-lineage.test.ts +2 -5
  132. package/src/__tests__/http-user-message-parity.test.ts +44 -0
  133. package/src/__tests__/identity-routes.test.ts +13 -13
  134. package/src/__tests__/image-conversion.test.ts +29 -26
  135. package/src/__tests__/image-source-path-reinject.test.ts +2 -2
  136. package/src/__tests__/inference-profile-session-handler.test.ts +95 -0
  137. package/src/__tests__/inference-profile-session-ipc.test.ts +25 -0
  138. package/src/__tests__/introduction-card-resolver.test.ts +6 -1
  139. package/src/__tests__/list-messages-attachments.test.ts +56 -32
  140. package/src/__tests__/list-messages-background-tool-completion.test.ts +4 -4
  141. package/src/__tests__/list-messages-client-message-id.test.ts +2 -2
  142. package/src/__tests__/list-messages-hidden-metadata.test.ts +16 -16
  143. package/src/__tests__/list-messages-page-latest.test.ts +61 -61
  144. package/src/__tests__/list-messages-provider-error.test.ts +6 -6
  145. package/src/__tests__/list-messages-queued.test.ts +49 -6
  146. package/src/__tests__/list-messages-system-card.test.ts +4 -4
  147. package/src/__tests__/list-messages-tool-merge.test.ts +9 -9
  148. package/src/__tests__/llm-resolver-override-or-default.test.ts +141 -0
  149. package/src/__tests__/llm-usage-store.test.ts +55 -0
  150. package/src/__tests__/manual-token-reconciliation.test.ts +45 -0
  151. package/src/__tests__/mcp-tool-annotations-risk.test.ts +133 -0
  152. package/src/__tests__/media-stream-server-integration.test.ts +36 -0
  153. package/src/__tests__/migration-export-http.test.ts +1 -1
  154. package/src/__tests__/migration-import-commit-http.test.ts +1 -1
  155. package/src/__tests__/migration-import-preflight-http.test.ts +1 -1
  156. package/src/__tests__/migration-validate-http.test.ts +1 -1
  157. package/src/__tests__/mtime-cache.test.ts +65 -3
  158. package/src/__tests__/notification-platform-adapter.test.ts +104 -0
  159. package/src/__tests__/notification-schedule-notify-dedup.test.ts +1 -1
  160. package/src/__tests__/oauth-provider-profiles.test.ts +4 -2
  161. package/src/__tests__/oauth-provider-seed-logos.test.ts +65 -0
  162. package/src/__tests__/oauth-provider-serializer.test.ts +17 -0
  163. package/src/__tests__/oauth-providers-routes.test.ts +1 -0
  164. package/src/__tests__/openai-responses-prompt-cache.test.ts +5 -2
  165. package/src/__tests__/path-policy.test.ts +164 -1
  166. package/src/__tests__/persist-media-references.test.ts +2 -2
  167. package/src/__tests__/persist-unsendable-image-downscale.test.ts +5 -5
  168. package/src/__tests__/persist-unsendable-image.test.ts +23 -23
  169. package/src/__tests__/persona-resolver.test.ts +154 -0
  170. package/src/__tests__/plugin-api-webhook-url.test.ts +170 -0
  171. package/src/__tests__/plugin-import-boundary-guard.test.ts +6 -0
  172. package/src/__tests__/processing-flag-persist-failure.test.ts +15 -10
  173. package/src/__tests__/profile-availability-incomplete.test.ts +113 -0
  174. package/src/__tests__/profiler-routes.test.ts +13 -13
  175. package/src/__tests__/prompt-cache-cross-turn-stability.test.ts +593 -0
  176. package/src/__tests__/proxy-approval-callback.test.ts +3 -3
  177. package/src/__tests__/queued-message-cancel-and-steer.test.ts +189 -0
  178. package/src/__tests__/queued-message-steer-actor-scoping.test.ts +164 -0
  179. package/src/__tests__/resolve-trust-class.test.ts +20 -0
  180. package/src/__tests__/run-conversation-turn-persistence.test.ts +1 -1
  181. package/src/__tests__/run-due-schedules.test.ts +174 -0
  182. package/src/__tests__/runtime-attachment-metadata.test.ts +15 -7
  183. package/src/__tests__/scaffold-managed-skill-tool.test.ts +166 -0
  184. package/src/__tests__/schedule-routes.test.ts +730 -8
  185. package/src/__tests__/schedule-store.test.ts +540 -0
  186. package/src/__tests__/schedule-tools.test.ts +103 -0
  187. package/src/__tests__/scheduler-wake.test.ts +5 -0
  188. package/src/__tests__/secret-routes-platform-proxy.test.ts +29 -1
  189. package/src/__tests__/secure-keys.test.ts +91 -0
  190. package/src/__tests__/server-history-render.test.ts +97 -7
  191. package/src/__tests__/size-guard.test.ts +8 -8
  192. package/src/__tests__/skill-docs-sync-guard.test.ts +3 -2
  193. package/src/__tests__/skill-tool-factory.test.ts +247 -0
  194. package/src/__tests__/skills-install-staging.test.ts +15 -6
  195. package/src/__tests__/slack-edit-ordering-characterization.test.ts +103 -0
  196. package/src/__tests__/slack-inbound-verification.test.ts +4 -1
  197. package/src/__tests__/slack-mention-binding-invariance.test.ts +103 -0
  198. package/src/__tests__/slack-mention-provider-leak-audit.test.ts +135 -0
  199. package/src/__tests__/starter-task-flow.test.ts +5 -4
  200. package/src/__tests__/steer-on-enqueue-question.test.ts +3 -2
  201. package/src/__tests__/stt-language-catalog-parity.test.ts +38 -0
  202. package/src/__tests__/subagent-allowlist-validation.test.ts +26 -4
  203. package/src/__tests__/subagent-cron-run-attribution.test.ts +301 -0
  204. package/src/__tests__/subagent-disposal.test.ts +10 -0
  205. package/src/__tests__/subagent-fork-notifications.test.ts +10 -0
  206. package/src/__tests__/subagent-fork-prompt-role.test.ts +85 -11
  207. package/src/__tests__/subagent-fork-spawn.test.ts +15 -7
  208. package/src/__tests__/subagent-manager-notify.test.ts +148 -0
  209. package/src/__tests__/subagent-notify-parent.test.ts +1 -1
  210. package/src/__tests__/subagent-persistence.test.ts +141 -0
  211. package/src/__tests__/subagent-role-registry.test.ts +172 -63
  212. package/src/__tests__/subagent-role-resolution.test.ts +88 -0
  213. package/src/__tests__/subagent-spawn-and-await.test.ts +48 -0
  214. package/src/__tests__/subagent-spawn-tool-fork.test.ts +111 -10
  215. package/src/__tests__/subagent-terminal-message.test.ts +88 -1
  216. package/src/__tests__/subagent-tool-gate-mode.test.ts +225 -10
  217. package/src/__tests__/subagent-tools.test.ts +1901 -108
  218. package/src/__tests__/surface-completion-compaction-boundary.test.ts +124 -0
  219. package/src/__tests__/surface-completion-in-flight-snapshot.test.ts +298 -0
  220. package/src/__tests__/system-prompt.test.ts +82 -7
  221. package/src/__tests__/tool-grant-request-escalation.test.ts +40 -1
  222. package/src/__tests__/tool-result-spool.test.ts +74 -18
  223. package/src/__tests__/tool-side-effects-documents.test.ts +174 -0
  224. package/src/__tests__/tool-side-effects-hook-failures.test.ts +129 -0
  225. package/src/__tests__/tools-audio-read.test.ts +10 -10
  226. package/src/__tests__/tools-get-route.test.ts +85 -12
  227. package/src/__tests__/tools-list-cli.test.ts +171 -0
  228. package/src/__tests__/top-level-renderer.test.ts +54 -1
  229. package/src/__tests__/trusted-contact-inline-approval-integration.test.ts +68 -1
  230. package/src/__tests__/trusted-contact-multichannel.test.ts +4 -1
  231. package/src/__tests__/turn-tail-chain.test.ts +135 -0
  232. package/src/__tests__/ui-choice-copy-surfaces.test.ts +6 -4
  233. package/src/__tests__/ui-visual-surface.test.ts +5 -4
  234. package/src/__tests__/ui-work-result-surface.test.ts +5 -4
  235. package/src/__tests__/unified-turn-context-visible-app.test.ts +7 -8
  236. package/src/__tests__/usage-routes.test.ts +62 -0
  237. package/src/__tests__/verification-outbound-delivery.test.ts +176 -0
  238. package/src/__tests__/visible-app-context.test.ts +20 -4
  239. package/src/__tests__/web-search-history.test.ts +96 -0
  240. package/src/__tests__/workspace-migration-139-clear-renamed-cost-profile-label.test.ts +6 -6
  241. package/src/__tests__/workspace-migration-140-repair-seed-pinned-memory-v3-live.test.ts +330 -0
  242. package/src/acp/failure-error.ts +3 -5
  243. package/src/agent/attachments.ts +26 -24
  244. package/src/agent/history-repair/history-repair.ts +27 -26
  245. package/src/agent/image-optimize.ts +6 -6
  246. package/src/agent/loop.ts +40 -22
  247. package/src/agent/message-types.ts +3 -3
  248. package/src/api/constants/document-tools.ts +55 -0
  249. package/src/api/events/question-answered.ts +53 -0
  250. package/src/api/events/tool-result.ts +7 -0
  251. package/src/api/index.ts +13 -0
  252. package/src/api/responses/conversation-message.ts +11 -0
  253. package/src/api/surfaces-oauth-connect.test.ts +96 -0
  254. package/src/api/surfaces.ts +36 -8
  255. package/src/approvals/guardian-channel-delivery.ts +83 -0
  256. package/src/approvals/guardian-expiry-notifier.ts +13 -11
  257. package/src/approvals/guardian-request-resolvers.ts +222 -135
  258. package/src/calls/__tests__/voice-session-bridge.test.ts +50 -0
  259. package/src/calls/media-stream-server.ts +9 -1
  260. package/src/calls/voice-session-bridge.ts +38 -0
  261. package/src/channels/__tests__/gateway-verification-sessions.test.ts +3 -3
  262. package/src/channels/config.ts +7 -2
  263. package/src/channels/gateway-verification-sessions.ts +7 -2
  264. package/src/channels/types.ts +7 -1
  265. package/src/cli/commands/__tests__/cache.test.ts +7 -4
  266. package/src/cli/commands/__tests__/cli-test-harness.ts +82 -0
  267. package/src/cli/commands/__tests__/conversations-wake.test.ts +51 -6
  268. package/src/cli/commands/__tests__/inference-models.test.ts +14 -0
  269. package/src/cli/commands/__tests__/inference-profiles.test.ts +44 -1
  270. package/src/cli/commands/__tests__/inference-providers.test.ts +7 -0
  271. package/src/cli/commands/__tests__/plugins.test.ts +692 -0
  272. package/src/cli/commands/__tests__/schedules.test.ts +136 -37
  273. package/src/cli/commands/conversations.ts +7 -3
  274. package/src/cli/commands/credentials.help.ts +7 -0
  275. package/src/cli/commands/inference-profiles.ts +9 -3
  276. package/src/cli/commands/inference.help.ts +16 -2
  277. package/src/cli/commands/mcp.help.ts +4 -2
  278. package/src/cli/commands/memory/memory-retrospective.ts +18 -0
  279. package/src/cli/commands/notifications.ts +3 -6
  280. package/src/cli/commands/oauth/connect-surface-guidance.test.ts +16 -0
  281. package/src/cli/commands/oauth/connect-surface-guidance.ts +25 -7
  282. package/src/cli/commands/oauth/connect.test.ts +26 -1
  283. package/src/cli/commands/oauth/connect.ts +4 -1
  284. package/src/cli/commands/oauth/index.help.ts +3 -1
  285. package/src/cli/commands/oauth/providers.ts +3 -0
  286. package/src/cli/commands/plugins.help.ts +25 -2
  287. package/src/cli/commands/plugins.ts +289 -10
  288. package/src/cli/commands/schedules.help.ts +14 -12
  289. package/src/cli/commands/schedules.ts +19 -5
  290. package/src/cli/commands/tools.help.ts +4 -2
  291. package/src/cli/commands/tools.ts +38 -0
  292. package/src/cli/lib/__tests__/inspect-plugin.test.ts +1 -0
  293. package/src/cli/lib/__tests__/install-from-github.test.ts +79 -0
  294. package/src/cli/lib/__tests__/install-from-platform.test.ts +47 -1
  295. package/src/cli/lib/__tests__/plugin-surfaces.test.ts +86 -7
  296. package/src/cli/lib/__tests__/upgrade-plugin.test.ts +122 -0
  297. package/src/cli/lib/install-from-github.ts +61 -0
  298. package/src/cli/lib/install-from-platform.ts +13 -2
  299. package/src/cli/lib/plugin-surfaces.ts +124 -4
  300. package/src/cli/lib/upgrade-plugin.ts +18 -0
  301. package/src/cli.ts +1 -1
  302. package/src/config/__tests__/deployment-context-defaults.test.ts +73 -0
  303. package/src/config/__tests__/memory-retrospective-schema.test.ts +40 -0
  304. package/src/config/__tests__/plugin-updates-schema.test.ts +43 -0
  305. package/src/config/__tests__/profile-tool-support.test.ts +131 -0
  306. package/src/config/bundled-skills/AGENTS.md +1 -1
  307. package/src/config/bundled-skills/app-builder/SKILL.md +1 -1
  308. package/src/config/bundled-skills/app-builder/references/RESPONSIVE.md +3 -3
  309. package/src/config/bundled-skills/schedule/SKILL.md +5 -1
  310. package/src/config/bundled-skills/schedule/TOOLS.json +2 -2
  311. package/src/config/bundled-skills/settings/TOOLS.json +2 -2
  312. package/src/config/bundled-skills/settings/tools/voice-config-update.ts +1 -0
  313. package/src/config/bundled-skills/subagent/SKILL.md +53 -22
  314. package/src/config/bundled-skills/subagent/TOOLS.json +14 -13
  315. package/src/config/bundled-skills/visualize/SKILL.md +1 -1
  316. package/src/config/default-profile-catalog.ts +6 -2
  317. package/src/config/feature-flag-registry.json +25 -17
  318. package/src/config/llm-resolver.ts +45 -15
  319. package/src/config/loader.ts +70 -1
  320. package/src/config/profile-tool-support.ts +110 -0
  321. package/src/config/sanitize-for-transfer.ts +11 -0
  322. package/src/config/schema.ts +4 -0
  323. package/src/config/schemas/__tests__/stt.test.ts +20 -5
  324. package/src/config/schemas/channels.ts +13 -0
  325. package/src/config/schemas/memory-retrospective.ts +25 -0
  326. package/src/config/schemas/plugin-updates.ts +55 -0
  327. package/src/config/schemas/services.ts +8 -4
  328. package/src/config/schemas/stt.ts +17 -5
  329. package/src/config/schemas/timeouts.ts +1 -1
  330. package/src/contacts/__tests__/guardian-delivery-reader.test.ts +16 -0
  331. package/src/contacts/guardian-delivery-reader.ts +29 -2
  332. package/src/context/compactor.ts +4 -4
  333. package/src/context/post-turn-tool-result-truncation.ts +32 -16
  334. package/src/context/strip-injections.ts +42 -0
  335. package/src/context/tool-result-spool.ts +32 -18
  336. package/src/credential-execution/ces-runtime.ts +3 -0
  337. package/src/credential-execution/client.ts +22 -1
  338. package/src/credential-execution/process-manager.test.ts +99 -0
  339. package/src/credential-execution/process-manager.ts +62 -16
  340. package/src/daemon/__tests__/abort-null-controller.test.ts +141 -5
  341. package/src/daemon/__tests__/conversation-surfaces-launch.test.ts +8 -12
  342. package/src/daemon/__tests__/install-assistant-command.test.ts +234 -0
  343. package/src/daemon/__tests__/turn-tail-assistant-reply-notify.test.ts +2 -4
  344. package/src/daemon/__tests__/turn-tail-deleted-conversation.test.ts +20 -29
  345. package/src/daemon/channel-ui-capability.ts +4 -4
  346. package/src/daemon/conversation-agent-loop-handlers.ts +117 -61
  347. package/src/daemon/conversation-agent-loop.ts +318 -148
  348. package/src/daemon/conversation-attachments.ts +1 -1
  349. package/src/daemon/conversation-error.ts +8 -1
  350. package/src/daemon/conversation-initial-prompt.ts +16 -38
  351. package/src/daemon/conversation-lifecycle.ts +154 -32
  352. package/src/daemon/conversation-messaging.ts +106 -43
  353. package/src/daemon/conversation-process.ts +131 -33
  354. package/src/daemon/conversation-queue-manager.ts +10 -0
  355. package/src/daemon/conversation-runtime-assembly.ts +9 -4
  356. package/src/daemon/conversation-store.ts +52 -3
  357. package/src/daemon/conversation-surface-state.ts +9 -0
  358. package/src/daemon/conversation-surfaces.ts +403 -210
  359. package/src/daemon/conversation-tool-setup.ts +56 -7
  360. package/src/daemon/conversation-turn-finalize.ts +134 -89
  361. package/src/daemon/conversation.ts +111 -27
  362. package/src/daemon/disk-pressure-guard-lifecycle.ts +2 -2
  363. package/src/daemon/disk-pressure-guard.ts +3 -3
  364. package/src/daemon/doordash-steps.ts +5 -5
  365. package/src/daemon/external-plugins-bootstrap.ts +7 -1
  366. package/src/daemon/handlers/config-channels.ts +48 -5
  367. package/src/daemon/handlers/conversation-history.ts +1 -1
  368. package/src/daemon/handlers/conversations.ts +62 -17
  369. package/src/daemon/handlers/shared.ts +12 -0
  370. package/src/daemon/handlers/skills.ts +11 -1
  371. package/src/daemon/identity-helpers.ts +38 -10
  372. package/src/daemon/install-assistant-command.ts +322 -0
  373. package/src/daemon/lifecycle.ts +14 -3
  374. package/src/daemon/message-types/conversations.ts +6 -7
  375. package/src/daemon/message-types/sync.ts +1 -0
  376. package/src/daemon/persist-media-references.ts +23 -14
  377. package/src/daemon/process-message.ts +6 -6
  378. package/src/daemon/tool-setup-types.ts +19 -122
  379. package/src/daemon/tool-side-effects.ts +17 -94
  380. package/src/daemon/trust-context-types.ts +39 -0
  381. package/src/daemon/trust-context.ts +14 -6
  382. package/src/daemon/turn-tail-chain.ts +73 -0
  383. package/src/daemon/web-search-history.ts +47 -33
  384. package/src/heartbeat/heartbeat-service.ts +6 -6
  385. package/src/ipc/__tests__/socket-path.test.ts +21 -0
  386. package/src/ipc/assistant-server.ts +17 -10
  387. package/src/ipc/cli-client.ts +10 -1
  388. package/src/ipc/routes/__tests__/documents-sync-ipc-routes.test.ts +56 -0
  389. package/src/ipc/routes/documents-sync-ipc-routes.ts +42 -0
  390. package/src/ipc/socket-cleanup.ts +9 -7
  391. package/src/ipc/socket-path.ts +4 -96
  392. package/src/live-voice/__tests__/live-voice-archive.test.ts +16 -16
  393. package/src/live-voice/__tests__/live-voice-attach-image.test.ts +209 -0
  394. package/src/live-voice/__tests__/live-voice-connection.test.ts +20 -0
  395. package/src/live-voice/__tests__/live-voice-integration.test.ts +7 -5
  396. package/src/live-voice/__tests__/live-voice-session-telemetry.test.ts +265 -0
  397. package/src/live-voice/__tests__/live-voice-stt.test.ts +6 -1
  398. package/src/live-voice/__tests__/live-voice-vad.test.ts +35 -15
  399. package/src/live-voice/__tests__/protocol.test.ts +52 -0
  400. package/src/live-voice/__tests__/runtime-websocket-shell.test.ts +3 -0
  401. package/src/live-voice/live-voice-archive.ts +13 -13
  402. package/src/live-voice/live-voice-connection.ts +7 -1
  403. package/src/live-voice/live-voice-photo.ts +169 -0
  404. package/src/live-voice/live-voice-session.ts +108 -4
  405. package/src/live-voice/protocol.ts +89 -1
  406. package/src/mcp/client.ts +10 -0
  407. package/src/messaging/providers/__tests__/callback-routing.test.ts +5 -1
  408. package/src/messaging/providers/__tests__/transport-dispatch.test.ts +66 -4
  409. package/src/messaging/providers/callback-routing.ts +1 -0
  410. package/src/messaging/providers/discord/api.ts +312 -0
  411. package/src/messaging/providers/discord/dm-delivery.test.ts +204 -0
  412. package/src/messaging/providers/discord/render.test.ts +204 -0
  413. package/src/messaging/providers/discord/render.ts +195 -0
  414. package/src/messaging/providers/discord/send.test.ts +179 -0
  415. package/src/messaging/providers/discord/send.ts +212 -0
  416. package/src/messaging/providers/discord/transport.ts +81 -0
  417. package/src/messaging/providers/index.ts +3 -1
  418. package/src/messaging/providers/retry-policy.test.ts +247 -0
  419. package/src/messaging/providers/retry-policy.ts +206 -0
  420. package/src/messaging/providers/slack/mention-source.test.ts +285 -0
  421. package/src/messaging/providers/slack/mention-source.ts +287 -0
  422. package/src/messaging/providers/telegram-bot/api.ts +47 -130
  423. package/src/messaging/providers/whatsapp/api.ts +47 -136
  424. package/src/monitoring/__tests__/db-integrity-sample.test.ts +4 -1
  425. package/src/monitoring/__tests__/plugin-auto-update.test.ts +332 -0
  426. package/src/monitoring/db-integrity-sample.ts +6 -1
  427. package/src/monitoring/plugin-auto-update.ts +359 -0
  428. package/src/monitoring/resource-sampler.ts +21 -5
  429. package/src/monitoring/worker.ts +13 -0
  430. package/src/notifications/__tests__/assistant-reply-producer.test.ts +375 -2
  431. package/src/notifications/__tests__/broadcaster.test.ts +37 -1
  432. package/src/notifications/__tests__/copy-composer.test.ts +165 -0
  433. package/src/notifications/__tests__/emit-signal-pipeline-failure.test.ts +205 -0
  434. package/src/notifications/__tests__/notification-utils.test.ts +249 -0
  435. package/src/notifications/adapters/platform.ts +31 -2
  436. package/src/notifications/assistant-reply-producer.ts +79 -32
  437. package/src/notifications/broadcaster.ts +30 -10
  438. package/src/notifications/copy-composer.ts +69 -0
  439. package/src/notifications/emit-signal.ts +82 -15
  440. package/src/notifications/events-store.ts +10 -2
  441. package/src/notifications/home-feed-side-effect.ts +6 -3
  442. package/src/notifications/notification-utils.ts +173 -5
  443. package/src/notifications/signal.ts +12 -0
  444. package/src/oauth/AGENTS.md +4 -2
  445. package/src/oauth/byo-connection.ts +6 -1
  446. package/src/oauth/credential-token-resolver.ts +3 -10
  447. package/src/oauth/manual-token-connection.ts +29 -64
  448. package/src/oauth/manual-token-providers.ts +62 -0
  449. package/src/oauth/provider-serializer.ts +13 -0
  450. package/src/oauth/seed-providers.ts +62 -11
  451. package/src/onboarding/onboarding-events-store.ts +91 -1
  452. package/src/permissions/question-prompter.test.ts +4 -0
  453. package/src/permissions/question-prompter.ts +21 -4
  454. package/src/persistence/__tests__/conversation-lineage.test.ts +248 -0
  455. package/src/persistence/attachments-store.ts +15 -15
  456. package/src/persistence/conversation-attention-store.ts +30 -10
  457. package/src/persistence/conversation-bootstrap.ts +17 -0
  458. package/src/persistence/conversation-crud.ts +363 -97
  459. package/src/persistence/conversation-group-migration.ts +1 -1
  460. package/src/persistence/conversation-key-store.ts +38 -10
  461. package/src/persistence/conversation-lineage.ts +226 -0
  462. package/src/persistence/conversation-plugin-facade.ts +28 -3
  463. package/src/persistence/conversation-queries.ts +207 -33
  464. package/src/persistence/conversation-types.ts +107 -1
  465. package/src/persistence/jobs-store.ts +32 -8
  466. package/src/persistence/llm-usage-store.ts +22 -0
  467. package/src/persistence/migrations/361-normalize-managed-connection-rows.test.ts +168 -0
  468. package/src/persistence/migrations/361-normalize-managed-connection-rows.ts +76 -0
  469. package/src/persistence/migrations/362-add-conversation-subagent-kind.test.ts +187 -0
  470. package/src/persistence/migrations/362-add-conversation-subagent-kind.ts +93 -0
  471. package/src/persistence/migrations/363-backfill-schedule-inference-profile.test.ts +229 -0
  472. package/src/persistence/migrations/363-backfill-schedule-inference-profile.ts +127 -0
  473. package/src/persistence/migrations/364-add-schedule-source-key.test.ts +120 -0
  474. package/src/persistence/migrations/364-add-schedule-source-key.ts +43 -0
  475. package/src/persistence/migrations/365-add-conversation-fork-strategy.ts +27 -0
  476. package/src/persistence/schema/conversations.ts +32 -0
  477. package/src/persistence/schema/infrastructure.ts +3 -0
  478. package/src/persistence/steps.ts +30 -0
  479. package/src/persistence/subagent-store.ts +148 -6
  480. package/src/platform/managed-speech.test.ts +20 -0
  481. package/src/platform/managed-speech.ts +9 -0
  482. package/src/plugin-api/index.ts +8 -0
  483. package/src/plugin-api/webhook-url.ts +151 -0
  484. package/src/plugins/__tests__/installed-plugin-dirs.test.ts +82 -0
  485. package/src/plugins/__tests__/source-fingerprint.test.ts +5 -1
  486. package/src/plugins/collect-source-versions.ts +4 -27
  487. package/src/plugins/defaults/exploration-drift/hooks/post-tool-use.ts +5 -5
  488. package/src/plugins/defaults/exploration-drift/package.json +1 -1
  489. package/src/plugins/defaults/image-recovery/hooks/post-model-call.ts +4 -2
  490. package/src/plugins/defaults/image-recovery/recover.ts +67 -57
  491. package/src/plugins/defaults/index.ts +1 -1
  492. package/src/plugins/defaults/memory/__tests__/conversation-queries.test.ts +64 -18
  493. package/src/plugins/defaults/memory/__tests__/jobs-worker-outcome-contract.test.ts +242 -0
  494. package/src/plugins/defaults/memory/__tests__/jobs-worker-retrospective-sweep.test.ts +24 -1
  495. package/src/plugins/defaults/memory/__tests__/memory-retrospective-enqueue.test.ts +48 -3
  496. package/src/plugins/defaults/memory/__tests__/memory-retrospective-job.test.ts +511 -19
  497. package/src/plugins/defaults/memory/__tests__/memory-retrospective-prompt.test.ts +39 -3
  498. package/src/plugins/defaults/memory/__tests__/memory-retrospective-provider-path.test.ts +493 -0
  499. package/src/plugins/defaults/memory/__tests__/memory-retrospective-wake-chain.test.ts +5 -4
  500. package/src/plugins/defaults/memory/__tests__/memory-tier-boundary-guard.test.ts +5 -1
  501. package/src/plugins/defaults/memory/job-handlers.ts +87 -5
  502. package/src/plugins/defaults/memory/jobs-worker.ts +60 -6
  503. package/src/plugins/defaults/memory/memory-retrospective-constants.ts +11 -0
  504. package/src/plugins/defaults/memory/memory-retrospective-enqueue.ts +29 -0
  505. package/src/plugins/defaults/memory/memory-retrospective-job.ts +342 -60
  506. package/src/plugins/defaults/memory/memory-retrospective-prompt.ts +23 -3
  507. package/src/plugins/defaults/memory/substrate/consolidation-lock.ts +9 -4
  508. package/src/plugins/defaults/memory/substrate/sweep-job.ts +7 -0
  509. package/src/plugins/defaults/memory/v1/graph/injection.ts +1 -1
  510. package/src/plugins/defaults/memory/v2/router.ts +8 -0
  511. package/src/plugins/defaults/turn-context/unified-turn-context.ts +1 -2
  512. package/src/plugins/external-plugin-loader.ts +17 -8
  513. package/src/plugins/installed-plugin-dirs.ts +101 -0
  514. package/src/plugins/mtime-cache.ts +34 -22
  515. package/src/plugins/types.ts +69 -8
  516. package/src/prompts/persona-resolver.ts +6 -2
  517. package/src/prompts/system-prompt.ts +72 -17
  518. package/src/providers/__tests__/inference.test.ts +40 -0
  519. package/src/providers/__tests__/preflight-resolved-config.test.ts +58 -1
  520. package/src/providers/__tests__/retry-callsite.test.ts +67 -0
  521. package/src/providers/__tests__/vellum-connection-routing.test.ts +42 -11
  522. package/src/providers/anthropic/client.ts +112 -95
  523. package/src/providers/connection-resolution.ts +35 -1
  524. package/src/providers/gemini/client.ts +5 -5
  525. package/src/providers/inference/adapter-factory.ts +16 -10
  526. package/src/providers/inference/auth.ts +16 -0
  527. package/src/providers/inference/connection-availability.ts +164 -12
  528. package/src/providers/inference/connections.ts +25 -1
  529. package/src/providers/media-resolve.ts +21 -11
  530. package/src/providers/model-catalog.ts +15 -0
  531. package/src/providers/openai/chat-completions-provider.ts +4 -4
  532. package/src/providers/openai/responses-provider.ts +12 -25
  533. package/src/providers/registry.ts +9 -4
  534. package/src/providers/retry.ts +25 -0
  535. package/src/providers/server-tool-pairing.ts +134 -0
  536. package/src/providers/speech-to-text/__tests__/resolve.test.ts +187 -6
  537. package/src/providers/speech-to-text/deepgram.ts +1 -0
  538. package/src/providers/speech-to-text/resolve.ts +80 -10
  539. package/src/providers/speech-to-text/vellum-managed.ts +7 -1
  540. package/src/providers/speech-to-text/vellum-speech-relay-connection.ts +3 -1
  541. package/src/providers/types.ts +26 -6
  542. package/src/providers/vellum-model-routing.ts +6 -7
  543. package/src/runtime/AGENTS.md +2 -0
  544. package/src/runtime/__tests__/agent-wake.test.ts +16 -16
  545. package/src/runtime/__tests__/background-job-runner.test.ts +0 -144
  546. package/src/runtime/__tests__/desktop-presence.test.ts +162 -0
  547. package/src/runtime/agent-wake.ts +63 -24
  548. package/src/runtime/assistant-event-hub.ts +87 -12
  549. package/src/runtime/auth/__tests__/guard-tests.test.ts +2 -0
  550. package/src/runtime/auth/__tests__/scopes.test.ts +20 -0
  551. package/src/runtime/auth/same-actor.ts +56 -8
  552. package/src/runtime/auth/scopes.ts +12 -0
  553. package/src/runtime/auth/types.ts +2 -0
  554. package/src/runtime/background-job-runner.ts +5 -27
  555. package/src/runtime/channel-readiness-service.ts +59 -3
  556. package/src/runtime/channel-readiness-types.ts +18 -0
  557. package/src/runtime/desktop-presence.ts +69 -0
  558. package/src/runtime/http-server.ts +12 -0
  559. package/src/runtime/local-actor-identity.ts +11 -9
  560. package/src/runtime/migrations/__tests__/vbundle-symlink-streaming-importer.test.ts +0 -0
  561. package/src/runtime/routes/__tests__/channel-verification-routes.test.ts +1 -1
  562. package/src/runtime/routes/__tests__/client-routes.test.ts +186 -14
  563. package/src/runtime/routes/__tests__/conversation-list-routes.test.ts +547 -1
  564. package/src/runtime/routes/__tests__/conversation-management-routes.test.ts +162 -7
  565. package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +14 -10
  566. package/src/runtime/routes/__tests__/inference-profiles-routes.test.ts +340 -3
  567. package/src/runtime/routes/__tests__/llm-call-sites-routes.test.ts +28 -1
  568. package/src/runtime/routes/__tests__/plugins-routes.test.ts +173 -1
  569. package/src/runtime/routes/__tests__/question-routes.test.ts +64 -0
  570. package/src/runtime/routes/__tests__/retrospective-routes.test.ts +20 -0
  571. package/src/runtime/routes/__tests__/skill-history-route.test.ts +116 -0
  572. package/src/runtime/routes/__tests__/wake-conversation-routes.test.ts +60 -2
  573. package/src/runtime/routes/archive-utils.ts +16 -12
  574. package/src/runtime/routes/attachment-routes.ts +13 -6
  575. package/src/runtime/routes/canned-reply-release.ts +54 -0
  576. package/src/runtime/routes/channel-verification-routes.ts +2 -27
  577. package/src/runtime/routes/chatgpt-subscription-auth-routes.ts +5 -0
  578. package/src/runtime/routes/client-routes.ts +114 -2
  579. package/src/runtime/routes/conversation-cli-routes.ts +10 -12
  580. package/src/runtime/routes/conversation-list-routes.ts +59 -10
  581. package/src/runtime/routes/conversation-management-routes.ts +47 -18
  582. package/src/runtime/routes/conversation-query-routes.ts +32 -53
  583. package/src/runtime/routes/conversation-routes.ts +360 -293
  584. package/src/runtime/routes/credential-prompt-routes.ts +23 -2
  585. package/src/runtime/routes/debug-routes.ts +4 -1
  586. package/src/runtime/routes/default-provider-routes.ts +7 -11
  587. package/src/runtime/routes/documents-routes.ts +44 -1
  588. package/src/runtime/routes/events-routes.ts +28 -1
  589. package/src/runtime/routes/host-app-control-routes.ts +2 -0
  590. package/src/runtime/routes/host-bash-routes.ts +2 -0
  591. package/src/runtime/routes/host-browser-routes.ts +2 -0
  592. package/src/runtime/routes/host-cu-routes.ts +2 -0
  593. package/src/runtime/routes/host-file-routes.ts +2 -0
  594. package/src/runtime/routes/host-transfer-routes.ts +4 -0
  595. package/src/runtime/routes/identity-routes.ts +6 -6
  596. package/src/runtime/routes/inbound-message-handler.ts +5 -5
  597. package/src/runtime/routes/inbound-stages/acl-enforcement.ts +7 -3
  598. package/src/runtime/routes/inbound-stages/background-dispatch.ts +14 -0
  599. package/src/runtime/routes/inbound-stages/bootstrap-intercept.ts +2 -2
  600. package/src/runtime/routes/inbound-stages/guardian-activation-intercept.test.ts +14 -11
  601. package/src/runtime/routes/inbound-stages/guardian-activation-intercept.ts +14 -17
  602. package/src/runtime/routes/inference-profile-availability-guard.ts +171 -0
  603. package/src/runtime/routes/inference-profile-session-handler.ts +26 -2
  604. package/src/runtime/routes/inference-profile-session-routes.ts +2 -1
  605. package/src/runtime/routes/inference-profiles-routes.ts +152 -73
  606. package/src/runtime/routes/inference-provider-connection-routes.ts +0 -16
  607. package/src/runtime/routes/llm-call-sites-routes.ts +13 -0
  608. package/src/runtime/routes/log-export-routes.ts +1 -1
  609. package/src/runtime/routes/notification-routes.ts +0 -14
  610. package/src/runtime/routes/oauth-providers.ts +1 -0
  611. package/src/runtime/routes/plugins-routes.ts +106 -1
  612. package/src/runtime/routes/profiler-routes.ts +4 -2
  613. package/src/runtime/routes/question-routes.ts +28 -10
  614. package/src/runtime/routes/retrospective-routes.ts +9 -3
  615. package/src/runtime/routes/schedule-routes.ts +210 -12
  616. package/src/runtime/routes/secret-routes.ts +40 -23
  617. package/src/runtime/routes/settings-routes.ts +41 -13
  618. package/src/runtime/routes/skills-routes.ts +79 -0
  619. package/src/runtime/routes/ui-snapshot-routes.ts +1 -0
  620. package/src/runtime/routes/wake-conversation-routes.ts +23 -0
  621. package/src/runtime/services/__tests__/conversation-serializer.test.ts +1 -0
  622. package/src/runtime/services/conversation-serializer.ts +24 -9
  623. package/src/runtime/sync/documents-sidecar-publish.test.ts +101 -0
  624. package/src/runtime/sync/resource-sync-events.ts +79 -1
  625. package/src/runtime/sync/worker-daemon-notify.test.ts +34 -0
  626. package/src/runtime/sync/worker-daemon-notify.ts +44 -6
  627. package/src/runtime/verification-outbound-actions.ts +250 -222
  628. package/src/runtime/verification-templates.ts +41 -30
  629. package/src/schedule/__tests__/plugin-schedule-declarations.test.ts +614 -0
  630. package/src/schedule/__tests__/plugin-schedule-reconciler.test.ts +1066 -0
  631. package/src/schedule/inference-profile.ts +64 -3
  632. package/src/schedule/plugin-schedule-declarations.ts +518 -0
  633. package/src/schedule/plugin-schedule-reconciler.ts +605 -0
  634. package/src/schedule/plugin-schedules-gate.ts +23 -0
  635. package/src/schedule/schedule-store.ts +520 -26
  636. package/src/schedule/scheduler.ts +38 -0
  637. package/src/security/credential-backend.ts +51 -4
  638. package/src/security/encrypted-store.ts +64 -42
  639. package/src/security/secure-keys.ts +55 -7
  640. package/src/skills/catalog-install.ts +20 -7
  641. package/src/skills/inline-command-runner.ts +2 -7
  642. package/src/skills/skill-history.test.ts +269 -0
  643. package/src/skills/skill-history.ts +250 -0
  644. package/src/skills/skillssh-registry.ts +1 -1
  645. package/src/stt/daemon-batch-transcriber.ts +8 -6
  646. package/src/subagent/__tests__/consult-prompt.test.ts +25 -0
  647. package/src/subagent/consult-prompt.ts +3 -1
  648. package/src/subagent/consult-transcript.ts +1 -1
  649. package/src/subagent/manager.ts +304 -44
  650. package/src/subagent/progress-events.ts +35 -0
  651. package/src/subagent/role-resolution.ts +88 -0
  652. package/src/subagent/types.ts +302 -67
  653. package/src/subagent/validate-allowlists.ts +3 -4
  654. package/src/telegram/__tests__/webhook-health.test.ts +73 -1
  655. package/src/telegram/webhook-health.ts +101 -3
  656. package/src/telemetry/__tests__/telemetry-event-fixtures.ts +2 -0
  657. package/src/telemetry/activation-funnel.ts +6 -1
  658. package/src/telemetry/live-voice-funnel.ts +75 -0
  659. package/src/telemetry/telemetry-event-sources.ts +6 -0
  660. package/src/telemetry/telemetry-wire-source.json +1 -1
  661. package/src/telemetry/telemetry-wire.generated.ts +2 -0
  662. package/src/telemetry/types.ts +23 -4
  663. package/src/telemetry/usage-telemetry-reporter.test.ts +4 -0
  664. package/src/tools/__tests__/subagent-workflow-tool-input-schemas.test.ts +1 -1
  665. package/src/tools/ask-question/ask-question-tool.test.ts +118 -3
  666. package/src/tools/ask-question/ask-question-tool.ts +56 -2
  667. package/src/tools/browser/browser-execution.ts +1 -1
  668. package/src/tools/calls/call-start.ts +8 -5
  669. package/src/tools/conversation-groups/move_to_group.ts +10 -12
  670. package/src/tools/document/document-tool.ts +11 -4
  671. package/src/tools/execution-timeout.ts +9 -2
  672. package/src/tools/executor.ts +7 -6
  673. package/src/tools/filesystem/edit.ts +1 -1
  674. package/src/tools/filesystem/list.ts +1 -1
  675. package/src/tools/filesystem/read.ts +7 -13
  676. package/src/tools/filesystem/search.ts +23 -21
  677. package/src/tools/filesystem/write.ts +4 -1
  678. package/src/tools/host-filesystem/edit.ts +7 -2
  679. package/src/tools/host-filesystem/read.ts +21 -8
  680. package/src/tools/host-filesystem/write.ts +10 -2
  681. package/src/tools/host-terminal/host-shell.ts +2 -2
  682. package/src/tools/mcp/mcp-tool-factory.ts +25 -1
  683. package/src/tools/permission-checker.ts +8 -3
  684. package/src/tools/schedule/delete.ts +13 -1
  685. package/src/tools/schedule/list.ts +14 -3
  686. package/src/tools/schedule/update.ts +56 -33
  687. package/src/tools/shared/filesystem/audio-read.ts +6 -4
  688. package/src/tools/shared/filesystem/file-ops-service.ts +176 -116
  689. package/src/tools/shared/filesystem/image-read.ts +12 -12
  690. package/src/tools/shared/filesystem/path-policy.ts +70 -5
  691. package/src/tools/shared/filesystem/size-guard.ts +6 -6
  692. package/src/tools/shared/input-misuse.ts +72 -0
  693. package/src/tools/skills/scaffold-managed.ts +82 -0
  694. package/src/tools/skills/skill-tool-factory.ts +14 -1
  695. package/src/tools/subagent/consult-deadline.ts +12 -7
  696. package/src/tools/subagent/message.ts +3 -1
  697. package/src/tools/subagent/read.ts +66 -4
  698. package/src/tools/subagent/spawn.ts +500 -78
  699. package/src/tools/tool-approval-handler.ts +45 -4
  700. package/src/tools/types.ts +22 -3
  701. package/src/tools/ui-surface/surface-shape-docs.ts +1 -1
  702. package/src/tools/ui-surface/visual-validation.ts +2 -0
  703. package/src/usage/__tests__/subagent-attribution.test.ts +109 -0
  704. package/src/usage/subagent-attribution.ts +108 -0
  705. package/src/util/__tests__/sqlite-retry.test.ts +112 -0
  706. package/src/util/abort-reasons.ts +16 -0
  707. package/src/util/ansi.ts +44 -0
  708. package/src/util/disk-usage.ts +17 -13
  709. package/src/util/image-conversion.ts +20 -13
  710. package/src/util/schedule-source-key.ts +23 -0
  711. package/src/util/worker-process.ts +7 -1
  712. package/src/workspace/git-service.ts +33 -9
  713. package/src/workspace/migrations/140-repair-seed-pinned-memory-v3-live.ts +260 -0
  714. package/src/workspace/migrations/141-stt-english-default-to-multilingual.ts +111 -0
  715. package/src/workspace/migrations/__tests__/141-stt-english-default-to-multilingual.test.ts +126 -0
  716. package/src/workspace/migrations/registry.ts +4 -0
  717. package/src/workspace/top-level-renderer.ts +15 -2
  718. package/src/__tests__/tool-side-effects-slack-dm.test.ts +0 -357
  719. package/src/daemon/install-symlink.ts +0 -207
  720. package/src/notifications/deferred-emit.ts +0 -147
@@ -13,7 +13,6 @@
13
13
  * conversions of the same image (or daemon restarts) skip the sips call.
14
14
  */
15
15
 
16
- import { execFileSync } from "node:child_process";
17
16
  import { createHash } from "node:crypto";
18
17
  import {
19
18
  closeSync,
@@ -101,10 +100,10 @@ export interface ConvertToJpegOptions {
101
100
  quality?: number;
102
101
  }
103
102
 
104
- function runSips(
103
+ async function runSips(
105
104
  inputBytes: Uint8Array,
106
105
  options: ConvertToJpegOptions,
107
- ): Buffer | null {
106
+ ): Promise<Buffer | null> {
108
107
  const stamp = `${Date.now()}-${uuid().slice(0, 8)}`;
109
108
  const srcPath = join(tmpdir(), `vellum-img-opt-${stamp}-src`);
110
109
  const outPath = join(tmpdir(), `vellum-img-opt-${stamp}-out.jpg`);
@@ -133,7 +132,15 @@ function runSips(
133
132
  "--out",
134
133
  outPath,
135
134
  );
136
- execFileSync("sips", args, { stdio: "pipe", timeout: 15_000 });
135
+ const proc = Bun.spawn(["sips", ...args], {
136
+ stdout: "ignore",
137
+ stderr: "ignore",
138
+ timeout: 15_000,
139
+ });
140
+ await proc.exited;
141
+ if (proc.exitCode !== 0) {
142
+ return null;
143
+ }
137
144
  return readFileSync(outPath) as Buffer;
138
145
  } catch {
139
146
  return null;
@@ -155,10 +162,10 @@ function runSips(
155
162
  * Convert an image to JPEG, optionally downscaling. Returns null when
156
163
  * conversion is unavailable (non-macOS) or fails; callers keep the original.
157
164
  */
158
- export function convertImageToJpeg(
165
+ export async function convertImageToJpeg(
159
166
  bytes: Uint8Array,
160
167
  options: ConvertToJpegOptions = {},
161
- ): Buffer | null {
168
+ ): Promise<Buffer | null> {
162
169
  const hash = createHash("sha256").update(bytes).digest("hex").slice(0, 16);
163
170
  // Options qualify the key so full-resolution storage conversions and
164
171
  // resized transport conversions of the same source never collide.
@@ -173,7 +180,7 @@ export function convertImageToJpeg(
173
180
  return cached;
174
181
  }
175
182
 
176
- const converted = runSips(bytes, options);
183
+ const converted = await runSips(bytes, options);
177
184
  if (!converted) {
178
185
  return null;
179
186
  }
@@ -346,12 +353,12 @@ export interface NormalizedImageBytes {
346
353
  * {@link jpegFilenameFor} when `converted` is true — a MIME-only correction
347
354
  * sets `converted: false` and keeps the filename.
348
355
  */
349
- export function normalizeImageBytes(
356
+ export async function normalizeImageBytes(
350
357
  mimeType: string,
351
358
  bytes: Uint8Array,
352
- ): NormalizedImageBytes {
359
+ ): Promise<NormalizedImageBytes> {
353
360
  if (isHeifImage(bytes)) {
354
- const converted = convertImageToJpeg(bytes, {
361
+ const converted = await convertImageToJpeg(bytes, {
355
362
  quality: STORAGE_JPEG_QUALITY,
356
363
  });
357
364
  if (!converted) {
@@ -376,14 +383,14 @@ export interface NormalizedImageBase64 {
376
383
  * Base64 variant of {@link normalizeImageBytes}. Sniffs the decoded head
377
384
  * first so non-HEIF payloads skip the full decode.
378
385
  */
379
- export function normalizeImageBase64(
386
+ export async function normalizeImageBase64(
380
387
  mimeType: string,
381
388
  dataBase64: string,
382
- ): NormalizedImageBase64 {
389
+ ): Promise<NormalizedImageBase64> {
383
390
  // 16 base64 chars decode to the 12 bytes the ftyp/signature sniffs need.
384
391
  const head = Buffer.from(dataBase64.slice(0, 16), "base64");
385
392
  if (isHeifImage(head)) {
386
- const normalized = normalizeImageBytes(
393
+ const normalized = await normalizeImageBytes(
387
394
  mimeType,
388
395
  Buffer.from(dataBase64, "base64"),
389
396
  );
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Plugin-declared schedules record their provenance in the `source_key`
3
+ * column as `plugin:<pluginName>/<scheduleName>`. The format is minted by
4
+ * `pluginScheduleSourceKey` in schedule/plugin-schedule-declarations.ts;
5
+ * this leaf parser lets display surfaces (CLI tables, tool output) derive
6
+ * the owning plugin without importing the daemon's schedule modules.
7
+ */
8
+
9
+ const PLUGIN_SOURCE_KEY_RE = /^plugin:([^/]+)\//;
10
+
11
+ /**
12
+ * Display attribution for a schedule row: the owning plugin's name, the raw
13
+ * key when it does not match the format so provenance is never silently
14
+ * dropped, or null when the row is imperative.
15
+ */
16
+ export function describeScheduleSource(
17
+ sourceKey: string | null | undefined,
18
+ ): string | null {
19
+ if (!sourceKey) {
20
+ return null;
21
+ }
22
+ return PLUGIN_SOURCE_KEY_RE.exec(sourceKey)?.[1] ?? sourceKey;
23
+ }
@@ -17,6 +17,7 @@ import {
17
17
  unlinkSync,
18
18
  } from "node:fs";
19
19
  import { dirname } from "node:path";
20
+ import { fileURLToPath } from "node:url";
20
21
 
21
22
  import { getCurrentLogFilePath } from "./logger.js";
22
23
  import { workerMemoryEnv } from "./worker-memory.js";
@@ -209,8 +210,13 @@ export async function spawnWorkerProcess(args: {
209
210
 
210
211
  // Workers are latency-insensitive, so run them in bun's small-heap mode
211
212
  // with a RAM-size hint sized to the container — see worker-memory.ts.
213
+ //
214
+ // `fileURLToPath`, not `.pathname`: a URL's pathname is percent-encoded, so
215
+ // an install path containing a space (every macOS desktop install lives
216
+ // under "Application Support") would reach bun as "Application%20Support"
217
+ // and the entry would not be found.
212
218
  const child = Bun.spawn({
213
- cmd: ["bun", "--smol", "run", args.entry.pathname],
219
+ cmd: ["bun", "--smol", "run", fileURLToPath(args.entry)],
214
220
  env: workerMemoryEnv(),
215
221
  stdio: ["ignore", "ignore", stderrFd],
216
222
  detached,
@@ -1,4 +1,4 @@
1
- import { execFile, spawn, spawnSync } from "node:child_process";
1
+ import { execFile, spawn } from "node:child_process";
2
2
  import {
3
3
  chmodSync,
4
4
  existsSync,
@@ -413,18 +413,19 @@ export class WorkspaceGitService {
413
413
  * open, we leave it alone. If no process holds it, it's stale (crashed
414
414
  * process) and safe to remove.
415
415
  */
416
- private cleanStaleLockFile(): void {
416
+ private async cleanStaleLockFile(): Promise<void> {
417
417
  const lockPath = join(this.workspaceDir, ".git", "index.lock");
418
418
  if (!existsSync(lockPath)) {
419
419
  return;
420
420
  }
421
421
 
422
422
  try {
423
- const result = spawnSync("lsof", ["-t", lockPath], {
423
+ // lsof exits non-zero when no process holds the file, which rejects the
424
+ // promise and falls through to removal below.
425
+ const { stdout } = await execFileAsync("lsof", ["-t", lockPath], {
424
426
  timeout: 3000,
425
- stdio: ["ignore", "pipe", "ignore"],
426
427
  });
427
- if (result.status === 0 && result.stdout?.length > 0) {
428
+ if (stdout.length > 0) {
428
429
  log.debug("index.lock held by an active process, skipping removal");
429
430
  return;
430
431
  }
@@ -490,7 +491,7 @@ export class WorkspaceGitService {
490
491
 
491
492
  // Clean up stale lock files before any git operations.
492
493
  if (existsSync(gitDir)) {
493
- this.cleanStaleLockFile();
494
+ await this.cleanStaleLockFile();
494
495
  }
495
496
 
496
497
  if (existsSync(gitDir)) {
@@ -657,7 +658,7 @@ export class WorkspaceGitService {
657
658
  await this.ensureInitialized();
658
659
 
659
660
  await this.mutex.withLock(async () => {
660
- this.cleanStaleLockFile();
661
+ await this.cleanStaleLockFile();
661
662
 
662
663
  // Stage all changes (minus oversized files)
663
664
  await this.stageAllLocked();
@@ -734,7 +735,7 @@ export class WorkspaceGitService {
734
735
 
735
736
  try {
736
737
  const result = await this.mutex.withLock(async () => {
737
- this.cleanStaleLockFile();
738
+ await this.cleanStaleLockFile();
738
739
 
739
740
  // Re-check breaker under lock: a queued call that started before the
740
741
  // breaker opened should not proceed with expensive git work now that
@@ -1991,6 +1992,29 @@ export class WorkspaceGitService {
1991
1992
  return this.execGit(args);
1992
1993
  }
1993
1994
 
1995
+ /**
1996
+ * Run a read-only git command WITHOUT initializing the repository.
1997
+ *
1998
+ * `runReadOnlyGit` is read-only in the sense that its git command does not
1999
+ * write, but it still awaits `ensureInitialized()`, which will create the
2000
+ * repository, write `.gitignore` and the hooks directory, make the initial
2001
+ * commit, and schedule a history compaction when the workspace is not yet a
2002
+ * repo. That makes it unusable from an HTTP GET, which
2003
+ * `src/runtime/AGENTS.md` requires to be side-effect-free.
2004
+ *
2005
+ * This variant skips initialization entirely and throws when the workspace
2006
+ * is not a repository, so a caller that merely wants to *observe* history
2007
+ * can degrade instead of bringing a repo into existence.
2008
+ */
2009
+ async runReadOnlyGitWithoutInit(
2010
+ args: string[],
2011
+ ): Promise<{ stdout: string; stderr: string }> {
2012
+ if (!existsSync(join(this.workspaceDir, ".git"))) {
2013
+ throw new Error("Workspace is not a git repository");
2014
+ }
2015
+ return this.execGit(args);
2016
+ }
2017
+
1994
2018
  /**
1995
2019
  * Run a sequence of git commands atomically under the workspace mutex.
1996
2020
  * Use this for write operations that need serialization with other
@@ -2003,7 +2027,7 @@ export class WorkspaceGitService {
2003
2027
  ): Promise<void> {
2004
2028
  await this.ensureInitialized();
2005
2029
  await this.mutex.withLock(async () => {
2006
- this.cleanStaleLockFile();
2030
+ await this.cleanStaleLockFile();
2007
2031
  await fn((args) => {
2008
2032
  // Intercept commit commands to enforce hook hardening.
2009
2033
  if (args[0] === "commit") {
@@ -0,0 +1,260 @@
1
+ import { existsSync, readdirSync, readFileSync, writeFileSync } from "node:fs";
2
+ import { join } from "node:path";
3
+ import { Database } from "bun:sqlite";
4
+
5
+ import type { MigrationRunContext, WorkspaceMigration } from "./types.js";
6
+
7
+ /**
8
+ * Repair assistants stuck on `memory.v3.live = false` by the first-launch
9
+ * seed race fixed in #39847: the seed used to persist the schema default
10
+ * (`false`) ~200ms before migration 105 ran, and 105's `"live" in v3Config`
11
+ * guard then bailed: a brand-new workspace that should have come up on
12
+ * memory-v3 ran v2 forever, with no signal to the user. 105 is checkpointed
13
+ * as applied and never re-runs, so nothing self-heals the stamped value.
14
+ *
15
+ * The same stamp lands on an EXISTING v3 assistant whose config.json is
16
+ * quarantined (corrupt JSON → renamed to `config.json.corrupt-<ts>.json`)
17
+ * and reseeded: the reseed silently demotes it to v2.
18
+ *
19
+ * Flips `live` to `true` only when the `false` on disk is attributable to
20
+ * the seed, never over a deliberate opt-out:
21
+ *
22
+ * - Quarantine carry-forward: the newest quarantined config next to
23
+ * config.json says `live: true`, meaning the assistant WAS on v3 before the
24
+ * reseed, so the current `false` is the seed's stamp.
25
+ * - Seed-race repair: the workspace was created during the migration-105 era
26
+ * (105 completed in the first-boot sweep, i.e. at effectively the same
27
+ * time as the workspace's earliest checkpoint), the archived hatch-time
28
+ * overlay (`default-config.json`) records no `live: false` opt-out, AND
29
+ * memory-v3 never ran (no `memory_v3_selections` rows in either database,
30
+ * including the mid-drain `__relocating` staging name). A deliberate
31
+ * opt-out on such a workspace requires flipping v3 off before its first
32
+ * selection is logged; a workspace that DID run v3 and now reads `false`
33
+ * is treated as an opt-out and left alone.
34
+ *
35
+ * Everything else is skipped: `live` absent means a pre-105 assistant on the
36
+ * deliberate manual-upgrade path (tier movement stays user-initiated), while
37
+ * new workspaces are migration 105's job, where a post-#39847 hatch-time
38
+ * `memory.v3.live=false` override merges after migrations and must win.
39
+ */
40
+
41
+ const MIGRATION_105_ID = "105-enable-memory-v3-live-for-new-workspaces";
42
+
43
+ /** How close migration 105's checkpoint must be to the workspace's earliest
44
+ * checkpoint to count as "completed in the first-boot sweep". The sweep
45
+ * itself takes seconds; the margin absorbs a crash-restart mid-sweep. */
46
+ const FIRST_BOOT_SWEEP_TOLERANCE_MS = 60 * 60 * 1000;
47
+
48
+ function isPlainObject(value: unknown): value is Record<string, unknown> {
49
+ return typeof value === "object" && value !== null && !Array.isArray(value);
50
+ }
51
+
52
+ /** `memory.v3.live` from a parsed config-shaped value, or undefined. */
53
+ function readMemoryV3Live(config: unknown): unknown {
54
+ if (!isPlainObject(config)) {
55
+ return undefined;
56
+ }
57
+ const memory = config.memory;
58
+ if (!isPlainObject(memory)) {
59
+ return undefined;
60
+ }
61
+ const v3 = memory.v3;
62
+ if (!isPlainObject(v3)) {
63
+ return undefined;
64
+ }
65
+ return v3.live;
66
+ }
67
+
68
+ /**
69
+ * Whether the newest quarantined config (`config.json.corrupt-*.json`)
70
+ * carried `memory.v3.live: true`. Quarantine filenames embed a
71
+ * filesystem-safe ISO timestamp, so a lexicographic sort is chronological.
72
+ * A quarantined file is usually truncated JSON; every config writer
73
+ * serializes via `JSON.stringify(config, null, 2)` and `memory.v3.live` is
74
+ * the only config key named `"live"`, so when parsing fails a literal
75
+ * `"live": true` match is unambiguous.
76
+ */
77
+ function quarantinedConfigSaidLiveTrue(workspaceDir: string): boolean {
78
+ try {
79
+ const entries = readdirSync(workspaceDir)
80
+ .filter(
81
+ (name) =>
82
+ name.startsWith("config.json.corrupt-") && name.endsWith(".json"),
83
+ )
84
+ .sort();
85
+ const newest = entries[entries.length - 1];
86
+ if (!newest) {
87
+ return false;
88
+ }
89
+ const raw = readFileSync(join(workspaceDir, newest), "utf-8");
90
+ try {
91
+ return readMemoryV3Live(JSON.parse(raw)) === true;
92
+ } catch {
93
+ return /"live"\s*:\s*true/.test(raw);
94
+ }
95
+ } catch {
96
+ return false;
97
+ }
98
+ }
99
+
100
+ /**
101
+ * Whether migration 105 completed in this workspace's first-boot sweep,
102
+ * i.e. the workspace was CREATED in the era where 105 should have flipped it
103
+ * to v3. On an older workspace 105 arrives in an upgrade sweep long after
104
+ * the earliest checkpoint, so the gap exceeds the tolerance.
105
+ */
106
+ function wasCreatedInMigration105Era(workspaceDir: string): boolean {
107
+ try {
108
+ const raw = readFileSync(
109
+ join(workspaceDir, "data", ".workspace-migrations.json"),
110
+ "utf-8",
111
+ );
112
+ const parsed: unknown = JSON.parse(raw);
113
+ if (!isPlainObject(parsed) || !isPlainObject(parsed.applied)) {
114
+ return false;
115
+ }
116
+ let earliest = Number.POSITIVE_INFINITY;
117
+ let applied105 = Number.NaN;
118
+ for (const [id, entry] of Object.entries(parsed.applied)) {
119
+ if (!isPlainObject(entry) || typeof entry.appliedAt !== "string") {
120
+ continue;
121
+ }
122
+ const at = Date.parse(entry.appliedAt);
123
+ if (!Number.isFinite(at)) {
124
+ continue;
125
+ }
126
+ earliest = Math.min(earliest, at);
127
+ if (id === MIGRATION_105_ID) {
128
+ applied105 = at;
129
+ }
130
+ }
131
+ return (
132
+ Number.isFinite(applied105) &&
133
+ Number.isFinite(earliest) &&
134
+ applied105 - earliest < FIRST_BOOT_SWEEP_TOLERANCE_MS
135
+ );
136
+ } catch {
137
+ return false;
138
+ }
139
+ }
140
+
141
+ /**
142
+ * Whether a hatch-time default workspace overlay recorded a deliberate
143
+ * memory-v3 opt-out. `mergeDefaultWorkspaceConfig()` archives the consumed
144
+ * overlay to `default-config.json` next to config.json; an overlay carrying
145
+ * `memory.v3.live: false` merged AFTER workspace migrations, so the
146
+ * persisted `false` is the user's hatch-time choice, not the seed's stamp,
147
+ * even though the workspace otherwise fingerprints as a seed-race victim
148
+ * (born in the 105 era, no v3 selection rows).
149
+ */
150
+ function hatchOverlayOptedOut(workspaceDir: string): boolean {
151
+ try {
152
+ const raw = readFileSync(
153
+ join(workspaceDir, "default-config.json"),
154
+ "utf-8",
155
+ );
156
+ return readMemoryV3Live(JSON.parse(raw)) === false;
157
+ } catch {
158
+ return false;
159
+ }
160
+ }
161
+
162
+ /**
163
+ * Whether memory-v3 ever ran a selection on this workspace. The
164
+ * `memory_v3_selections` table lives in `assistant-memory.db` after DB
165
+ * migration 338 and in `assistant.db` before it; both are checked, along
166
+ * with the `__relocating` staging name the relocation renames the source to
167
+ * mid-drain (a crash there leaves the rows only under the staging name). A
168
+ * database file that exists but cannot be read counts as "may have run v3"
169
+ * so an unreadable database never causes an opt-out to be overridden.
170
+ */
171
+ function memoryV3EverRan(workspaceDir: string): boolean {
172
+ const tables = ["memory_v3_selections", "memory_v3_selections__relocating"];
173
+ for (const file of ["assistant-memory.db", "assistant.db"]) {
174
+ const dbPath = join(workspaceDir, "data", "db", file);
175
+ if (!existsSync(dbPath)) {
176
+ continue;
177
+ }
178
+ let db: Database | null = null;
179
+ try {
180
+ db = new Database(dbPath, { readonly: true });
181
+ for (const table of tables) {
182
+ const exists = db
183
+ .query(
184
+ `SELECT name FROM sqlite_master WHERE type = 'table' AND name = ?`,
185
+ )
186
+ .get(table);
187
+ if (!exists) {
188
+ continue;
189
+ }
190
+ const row = db.query(`SELECT 1 FROM "${table}" LIMIT 1`).get();
191
+ if (row) {
192
+ return true;
193
+ }
194
+ }
195
+ } catch {
196
+ return true;
197
+ } finally {
198
+ db?.close();
199
+ }
200
+ }
201
+ return false;
202
+ }
203
+
204
+ export const repairSeedPinnedMemoryV3LiveMigration: WorkspaceMigration = {
205
+ id: "140-repair-seed-pinned-memory-v3-live",
206
+ description:
207
+ "Re-flip memory.v3.live=true for assistants the pre-#39847 first-launch seed pinned to false (seed-race and quarantine-reseed victims), leaving deliberate opt-outs alone",
208
+
209
+ run(workspaceDir: string, ctx?: MigrationRunContext): void {
210
+ // New workspaces are migration 105's job, and a post-#39847 hatch-time
211
+ // memory.v3.live=false override (merged after migrations) must win.
212
+ if (ctx?.isNewWorkspace) {
213
+ return;
214
+ }
215
+
216
+ const configPath = join(workspaceDir, "config.json");
217
+ if (!existsSync(configPath)) {
218
+ return;
219
+ }
220
+ let config: Record<string, unknown>;
221
+ try {
222
+ const parsed: unknown = JSON.parse(readFileSync(configPath, "utf-8"));
223
+ if (!isPlainObject(parsed)) {
224
+ return;
225
+ }
226
+ config = parsed;
227
+ } catch {
228
+ return;
229
+ }
230
+
231
+ // Only a persisted explicit `false` is repairable: `true` is healthy and
232
+ // an absent leaf is a pre-105 assistant on the manual-upgrade path.
233
+ if (readMemoryV3Live(config) !== false) {
234
+ return;
235
+ }
236
+
237
+ if (!quarantinedConfigSaidLiveTrue(workspaceDir)) {
238
+ if (!wasCreatedInMigration105Era(workspaceDir)) {
239
+ return;
240
+ }
241
+ if (hatchOverlayOptedOut(workspaceDir)) {
242
+ return;
243
+ }
244
+ if (memoryV3EverRan(workspaceDir)) {
245
+ return;
246
+ }
247
+ }
248
+
249
+ // Narrow-write: only the one leaf changes; the shape was validated above.
250
+ const memory = config.memory as Record<string, unknown>;
251
+ const v3 = memory.v3 as Record<string, unknown>;
252
+ v3.live = true;
253
+ writeFileSync(configPath, JSON.stringify(config, null, 2) + "\n");
254
+ },
255
+
256
+ down(_workspaceDir: string): void {
257
+ // Forward-only: cannot distinguish this repair from a user's explicit
258
+ // choice after the fact (mirrors migration 105).
259
+ },
260
+ };
@@ -0,0 +1,111 @@
1
+ import { existsSync, readFileSync, writeFileSync } from "node:fs";
2
+ import { join } from "node:path";
3
+
4
+ import type { WorkspaceMigration } from "./types.js";
5
+
6
+ /**
7
+ * Put existing assistants on code-switching by writing
8
+ * `services.stt.language: "multi"` on Deepgram and the managed relay,
9
+ * covering both an accepted English default and a language that was never
10
+ * set at all.
11
+ *
12
+ * Normally rewriting a value the user set would be off limits. It is safe
13
+ * here because of when the field appeared: `services.stt.language` did not
14
+ * exist before the language picker shipped (#39546, released 2026-07-31), and
15
+ * on these two providers that picker never offered English and Multilingual
16
+ * as peers. The only English row was the default row, labelled "English
17
+ * (default)". So an `"en"` on Deepgram or the managed relay records "I
18
+ * accepted the default", not "I want English rather than code-switching", and
19
+ * the default it accepted has since changed.
20
+ *
21
+ * Scoped deliberately:
22
+ *
23
+ * - Only `"en"` and absent. Any other language is a real choice from a list
24
+ * where it was the only way to get that language.
25
+ * - Only Deepgram and the managed relay (`mode: "managed"` routes there
26
+ * while `provider` holds the bring-your-own restore value). xAI has
27
+ * offered an explicit English row since the picker shipped, so an `"en"`
28
+ * there is deliberate, and code-switching is a Deepgram mode its adapter
29
+ * drops anyway.
30
+ *
31
+ * Idempotent: an assistant already on `"multi"` (or anything else) is left
32
+ * alone, so re-running changes nothing.
33
+ */
34
+ const MULTILINGUAL = "multi";
35
+ const ENGLISH = "en";
36
+ const MULTI_DEFAULT_PROVIDERS = new Set(["deepgram", "vellum"]);
37
+
38
+ export const sttEnglishDefaultToMultilingualMigration: WorkspaceMigration = {
39
+ id: "141-stt-english-default-to-multilingual",
40
+ description:
41
+ "Move services.stt.language from the accepted English default to multilingual on Deepgram and managed speech",
42
+ run(workspaceDir: string): void {
43
+ if (process.env.VELLUM_DEFAULT_WORKSPACE_CONFIG_PATH) {
44
+ return;
45
+ }
46
+
47
+ const configPath = join(workspaceDir, "config.json");
48
+ if (!existsSync(configPath)) {
49
+ return;
50
+ }
51
+
52
+ let config: Record<string, unknown> = {};
53
+ try {
54
+ const raw = JSON.parse(readFileSync(configPath, "utf-8"));
55
+ if (!raw || typeof raw !== "object" || Array.isArray(raw)) {
56
+ return;
57
+ }
58
+ config = raw as Record<string, unknown>;
59
+ } catch {
60
+ return;
61
+ }
62
+
63
+ const services = readObject(config.services);
64
+ if (services === null) {
65
+ return;
66
+ }
67
+ const stt = readObject(services.stt);
68
+ if (stt === null) {
69
+ return;
70
+ }
71
+ // Two states move, and only these two. An accepted English default, and
72
+ // an absent language.
73
+ //
74
+ // Absent has to be handled here rather than left to the schema default,
75
+ // because the config route serves the RAW config: the parsed default
76
+ // never reaches the web, so the settings UI would keep reading an absent
77
+ // language as English while live transcription used multilingual. Writing
78
+ // it makes the two agree.
79
+ const language = stt.language;
80
+ if (language !== ENGLISH && language !== undefined) {
81
+ return;
82
+ }
83
+
84
+ // A legacy managed-mode config routes to the relay while `provider` still
85
+ // holds the bring-your-own value it would restore, so mode wins.
86
+ const provider =
87
+ stt.mode === "managed"
88
+ ? "vellum"
89
+ : typeof stt.provider === "string"
90
+ ? stt.provider
91
+ : "deepgram";
92
+ if (!MULTI_DEFAULT_PROVIDERS.has(provider)) {
93
+ return;
94
+ }
95
+
96
+ stt.language = MULTILINGUAL;
97
+ services.stt = stt;
98
+ config.services = services;
99
+ writeFileSync(configPath, JSON.stringify(config, null, 2) + "\n");
100
+ },
101
+ down(_workspaceDir: string): void {
102
+ // Forward-only.
103
+ },
104
+ };
105
+
106
+ function readObject(value: unknown): Record<string, unknown> | null {
107
+ if (value === null || typeof value !== "object" || Array.isArray(value)) {
108
+ return null;
109
+ }
110
+ return value as Record<string, unknown>;
111
+ }