@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
@@ -10,17 +10,29 @@
10
10
  * (see the external plugin loader's tool walk).
11
11
  * - `skills/<id>/SKILL.md` → a skill owned by the plugin (see the skills
12
12
  * catalog's `discoverPluginResidentSkills`).
13
+ * - `schedules/<name>/` → a declared schedule (see the daemon's plugin
14
+ * schedule declaration parser).
13
15
  *
14
16
  * This module re-derives those same sets so `plugins inspect` can report exactly
15
17
  * what a plugin contributes. Detection is intentionally a self-contained walk of
16
- * the install tree `cli/lib` does not reach into the daemon-internal loader or
17
- * skills catalog but it mirrors their conventions so inspect agrees with what
18
- * the runtime actually loads.
18
+ * the install tree (`cli/lib` does not reach into the daemon-internal loader,
19
+ * skills catalog, or schedule parser), but it mirrors their conventions so
20
+ * inspect agrees with what the runtime actually loads.
19
21
  */
20
22
 
21
- import { existsSync, readdirSync, statSync } from "node:fs";
23
+ import { existsSync, readdirSync, readFileSync, statSync } from "node:fs";
22
24
  import { join } from "node:path";
23
25
 
26
+ /** A schedule the plugin declares under `schedules/`. */
27
+ export interface PluginScheduleSurface {
28
+ /** Schedule name: the declaration directory's name. */
29
+ readonly name: string;
30
+ /** Raw schedule `expression` string from the declaration's config. */
31
+ readonly cadence: string;
32
+ /** `execute` for a markdown prompt entrypoint, `script` for `index.sh`. */
33
+ readonly mode: "execute" | "script";
34
+ }
35
+
24
36
  /** The surfaces an installed plugin contributes, each sorted and de-duplicated. */
25
37
  export interface PluginSurfaces {
26
38
  /** Skill ids shipped at `skills/<id>/SKILL.md`. */
@@ -36,6 +48,18 @@ export interface PluginSurfaces {
36
48
  * importing and executing untrusted plugin code, which inspection avoids.
37
49
  */
38
50
  readonly tools: readonly string[];
51
+ /**
52
+ * Schedules declared under `schedules/`, each a `<name>/` directory with a
53
+ * `config.json` plus exactly one `index.md` (`execute`) or `index.sh`
54
+ * (`script`) entrypoint. This is a display surface, not the arming path:
55
+ * a file directly under `schedules/` and structurally malformed directories
56
+ * (a bad entrypoint set, an empty prompt body, frontmatter in `index.md`, an
57
+ * unreadable config, a missing `expression`) are skipped rather than reported
58
+ * as errors. Detection stays permissive beyond structure: `expression`
59
+ * validity is not checked CLI-side, so a declaration whose expression the
60
+ * daemon rejects is still listed here.
61
+ */
62
+ readonly schedules: readonly PluginScheduleSurface[];
39
63
  }
40
64
 
41
65
  /**
@@ -97,6 +121,101 @@ function listSkillIds(skillsDir: string): string[] {
97
121
  return ids.sort();
98
122
  }
99
123
 
124
+ /** Matches a `---` delimited YAML frontmatter block at the start of a file. */
125
+ const FRONTMATTER_REGEX = /^---\r?\n([\s\S]*?)\r?\n---(?:\r?\n|$)/;
126
+
127
+ /** Read the raw `expression` string from a parsed schedule config, or `null`. */
128
+ function readConfigExpression(config: unknown): string | null {
129
+ if (typeof config !== "object" || config === null || Array.isArray(config)) {
130
+ return null;
131
+ }
132
+ const expression = (config as Record<string, unknown>).expression;
133
+ return typeof expression === "string" && expression.trim() !== ""
134
+ ? expression
135
+ : null;
136
+ }
137
+
138
+ /**
139
+ * Read a `<name>/` directory declaration: `config.json` supplies the cadence
140
+ * and the single `index.md`/`index.sh` entrypoint decides the mode. `null` for
141
+ * anything the loader would refuse (zero or multiple `index.*` entries, an
142
+ * unsupported entrypoint, an `index.md` carrying frontmatter or an empty
143
+ * prompt body, an unreadable config, a missing expression).
144
+ */
145
+ function readDirectorySchedule(
146
+ dirPath: string,
147
+ ): { cadence: string; mode: PluginScheduleSurface["mode"] } | null {
148
+ let children;
149
+ try {
150
+ children = readdirSync(dirPath, { withFileTypes: true });
151
+ } catch {
152
+ return null;
153
+ }
154
+ const entrypoints = children
155
+ .filter((c) => c.isFile() && c.name.startsWith("index."))
156
+ .map((c) => c.name);
157
+ if (entrypoints.length !== 1) {
158
+ return null;
159
+ }
160
+ const entrypoint = entrypoints[0]!;
161
+ if (entrypoint !== "index.md" && entrypoint !== "index.sh") {
162
+ return null;
163
+ }
164
+ if (entrypoint === "index.md") {
165
+ let body: string;
166
+ try {
167
+ body = readFileSync(join(dirPath, "index.md"), "utf8");
168
+ } catch {
169
+ return null;
170
+ }
171
+ // Directory-form config belongs in config.json, so the loader refuses an
172
+ // index.md with frontmatter; it also refuses an empty prompt body.
173
+ if (FRONTMATTER_REGEX.test(body) || !body.trim()) {
174
+ return null;
175
+ }
176
+ }
177
+ let config: unknown;
178
+ try {
179
+ config = JSON.parse(readFileSync(join(dirPath, "config.json"), "utf8"));
180
+ } catch {
181
+ return null;
182
+ }
183
+ const cadence = readConfigExpression(config);
184
+ if (cadence === null) {
185
+ return null;
186
+ }
187
+ return { cadence, mode: entrypoint === "index.md" ? "execute" : "script" };
188
+ }
189
+
190
+ /**
191
+ * List the schedules a plugin declares under `schedules/`, mirroring the
192
+ * declaration parser's directory-only form. A file directly under
193
+ * `schedules/` is not a declaration, so it is skipped. Returns schedules
194
+ * sorted by name; a missing directory yields `[]`.
195
+ */
196
+ function listSchedules(schedulesDir: string): PluginScheduleSurface[] {
197
+ let entries;
198
+ try {
199
+ entries = readdirSync(schedulesDir, { withFileTypes: true });
200
+ } catch {
201
+ return [];
202
+ }
203
+
204
+ const schedules: PluginScheduleSurface[] = [];
205
+ for (const entry of entries) {
206
+ if (entry.name.startsWith(".") || !entry.isDirectory()) {
207
+ continue;
208
+ }
209
+ const parsed = readDirectorySchedule(join(schedulesDir, entry.name));
210
+ if (parsed) {
211
+ schedules.push({ name: entry.name, ...parsed });
212
+ }
213
+ }
214
+ return schedules.sort((a, b) =>
215
+ a.name < b.name ? -1 : a.name > b.name ? 1 : 0,
216
+ );
217
+ }
218
+
100
219
  /**
101
220
  * Detect the {@link PluginSurfaces} an installed plugin contributes by walking
102
221
  * its install tree at `pluginDir`. Surface types with no contributions come
@@ -110,5 +229,6 @@ export function detectPluginSurfaces(pluginDir: string): PluginSurfaces {
110
229
  skills: listSkillIds(join(pluginDir, "skills")),
111
230
  hooks: listModuleBasenames(join(pluginDir, "hooks")),
112
231
  tools: [...new Set(toolNames)].sort(),
232
+ schedules: listSchedules(join(pluginDir, "schedules")),
113
233
  };
114
234
  }
@@ -54,6 +54,8 @@ import {
54
54
  type PluginLocalInfo,
55
55
  } from "./inspect-plugin.js";
56
56
  import {
57
+ type ConfirmStagedInstall,
58
+ confirmStagedOrAbort,
57
59
  finalizeStagedInstall,
58
60
  type GitRunner,
59
61
  installPlugin,
@@ -138,6 +140,14 @@ export interface UpgradePluginDeps {
138
140
  * for dry runs or no-op upgrades (they never reach the swap).
139
141
  */
140
142
  readonly beforeSwap?: () => Promise<void>;
143
+ /**
144
+ * Consent gate between staging and finalize, identical to the install
145
+ * path's ({@link ConfirmStagedInstall}): the caller inspects the staged
146
+ * upgraded tree (e.g. its declared schedules) and may abort before
147
+ * anything replaces the live install. Applies to overwrite and merge
148
+ * upgrades alike; dry runs and no-ops never stage, so it is not invoked.
149
+ */
150
+ readonly confirmStaged?: ConfirmStagedInstall;
141
151
  }
142
152
 
143
153
  /** Result of an upgrade attempt. */
@@ -401,6 +411,7 @@ export async function upgradePlugin(
401
411
  runPostinstall: deps.runPostinstall,
402
412
  runInstallDeps: deps.runInstallDeps,
403
413
  beforeSwap: deps.beforeSwap,
414
+ confirmStaged: deps.confirmStaged,
404
415
  },
405
416
  );
406
417
 
@@ -591,6 +602,7 @@ async function directUpgrade(
591
602
  runPostinstall: deps.runPostinstall,
592
603
  runInstallDeps: deps.runInstallDeps,
593
604
  beforeSwap: deps.beforeSwap,
605
+ confirmStaged: deps.confirmStaged,
594
606
  },
595
607
  );
596
608
 
@@ -747,6 +759,12 @@ async function mergeUpgrade(
747
759
  conflictLabels,
748
760
  });
749
761
 
762
+ // Same staging-to-finalize consent boundary as `installPlugin`: the
763
+ // caller sees the fully merged tree before it replaces the live install.
764
+ // A decline removes the staging dir and throws; the outer catch's rmSync
765
+ // is then a no-op.
766
+ await confirmStagedOrAbort(name, stagingDir, deps.confirmStaged);
767
+
750
768
  const toCommit = theirs.commit ?? ctx.toCommit;
751
769
  const toTimestamp = theirs.committedAt ?? ctx.toTimestamp;
752
770
  await finalizeStagedInstall(stagingDir, {
package/src/cli.ts CHANGED
@@ -513,7 +513,7 @@ export async function startCli(): Promise<void> {
513
513
  if (content === "/conversations") {
514
514
  pendingSessionPick = true;
515
515
  try {
516
- const rows = listConversations(20);
516
+ const rows = listConversations({ limit: 20 });
517
517
  const conversations = rows.map((r) => ({
518
518
  id: r.id,
519
519
  title: r.title || "Untitled",
@@ -1,6 +1,7 @@
1
1
  import {
2
2
  existsSync,
3
3
  mkdirSync,
4
+ readdirSync,
4
5
  readFileSync,
5
6
  rmSync,
6
7
  writeFileSync,
@@ -267,4 +268,76 @@ describe("deployment-context embedding-provider default (via loadConfig)", () =>
267
268
  >;
268
269
  expect(v3Raw.live).toBe(true);
269
270
  });
271
+
272
+ describe("quarantine-reseed carries memory.v3.live forward", () => {
273
+ function removeQuarantineFiles(): void {
274
+ for (const name of readdirSync(WORKSPACE_DIR)) {
275
+ if (name.startsWith("config.json.corrupt-")) {
276
+ rmSync(join(WORKSPACE_DIR, name), { force: true });
277
+ }
278
+ }
279
+ }
280
+
281
+ function readV3Raw(): Record<string, unknown> {
282
+ const raw = readConfig();
283
+ return ((raw.memory as Record<string, unknown>).v3 ?? {}) as Record<
284
+ string,
285
+ unknown
286
+ >;
287
+ }
288
+
289
+ afterEach(() => {
290
+ removeQuarantineFiles();
291
+ });
292
+
293
+ test("a corrupt live-v3 config is quarantined and the reseed keeps live=true", () => {
294
+ // Truncated mid-write JSON from a memory-v3 assistant: unparseable,
295
+ // but the live flag survives in the raw text.
296
+ writeFileSync(
297
+ CONFIG_PATH,
298
+ `{\n "memory": {\n "v3": {\n "live": true\n }\n },\n "llm": {\n "activeProf`,
299
+ );
300
+
301
+ const config = loadConfig();
302
+
303
+ // The corrupt file was quarantined and the reseeded config carries the
304
+ // tier forward, both in-memory (this boot runs v3) and on disk
305
+ // (migration 105 is checkpointed on existing workspaces and never
306
+ // re-runs, so the persisted value is the only durable record).
307
+ expect(config.memory.v3.live).toBe(true);
308
+ expect(readV3Raw()).toEqual({ live: true });
309
+ });
310
+
311
+ test("a corrupt config without live=true reseeds with the leaf absent", () => {
312
+ writeFileSync(
313
+ CONFIG_PATH,
314
+ `{\n "memory": {\n "v3": {\n "live": false\n }\n },\n "llm": {\n "activeProf`,
315
+ );
316
+
317
+ const config = loadConfig();
318
+
319
+ // Never carry `false` forward: an absent leaf is the only way to keep
320
+ // "no decision recorded" representable for migration 105.
321
+ expect(config.memory.v3.live).toBe(false);
322
+ expect(readV3Raw()).toEqual({});
323
+ });
324
+
325
+ test("a deleted config with an earlier live-v3 quarantine still carries live=true", () => {
326
+ if (existsSync(CONFIG_PATH)) {
327
+ rmSync(CONFIG_PATH, { force: true });
328
+ }
329
+ writeFileSync(
330
+ join(
331
+ WORKSPACE_DIR,
332
+ "config.json.corrupt-2026-08-01T10-00-00.000Z.json",
333
+ ),
334
+ JSON.stringify({ memory: { v3: { live: true } } }, null, 2),
335
+ );
336
+
337
+ const config = loadConfig();
338
+
339
+ expect(config.memory.v3.live).toBe(true);
340
+ expect(readV3Raw()).toEqual({ live: true });
341
+ });
342
+ });
270
343
  });
@@ -0,0 +1,40 @@
1
+ /**
2
+ * Guards over the `memory.retrospective` switches that decide whether the
3
+ * retrospective pass runs at all, and how its fork is materialized.
4
+ *
5
+ * The defaults are the contract: a workspace that says nothing must behave
6
+ * exactly as it did before the fields existed: retrospectives on, forks
7
+ * cloned.
8
+ */
9
+
10
+ import { describe, expect, test } from "bun:test";
11
+
12
+ import { MemoryRetrospectiveConfigSchema } from "../schemas/memory-retrospective.js";
13
+
14
+ describe("memory.retrospective config schema", () => {
15
+ test("an empty block leaves retrospectives on and forks cloning", () => {
16
+ const parsed = MemoryRetrospectiveConfigSchema.parse({});
17
+ expect(parsed.enabled).toBe(true);
18
+ expect(parsed.forkStrategy).toBe("cloning");
19
+ });
20
+
21
+ test("enabled is a boolean-only kill switch", () => {
22
+ expect(
23
+ MemoryRetrospectiveConfigSchema.parse({ enabled: false }).enabled,
24
+ ).toBe(false);
25
+ expect(
26
+ MemoryRetrospectiveConfigSchema.safeParse({ enabled: "false" }).success,
27
+ ).toBe(false);
28
+ });
29
+
30
+ test("forkStrategy accepts only cloning and reference", () => {
31
+ expect(
32
+ MemoryRetrospectiveConfigSchema.parse({ forkStrategy: "reference" })
33
+ .forkStrategy,
34
+ ).toBe("reference");
35
+ expect(
36
+ MemoryRetrospectiveConfigSchema.safeParse({ forkStrategy: "referential" })
37
+ .success,
38
+ ).toBe(false);
39
+ });
40
+ });
@@ -0,0 +1,43 @@
1
+ /**
2
+ * Guards over the `pluginUpdates` config block — the opt-in that decides
3
+ * whether installed plugins move on their own.
4
+ *
5
+ * The defaults are the contract: a workspace that says nothing must behave
6
+ * exactly as it did before the block existed (manual, nothing upgrades), and
7
+ * an opted-in workspace must land on the `theirs` merge strategy.
8
+ */
9
+
10
+ import { describe, expect, test } from "bun:test";
11
+
12
+ import { PluginUpdatesConfigSchema } from "../schemas/plugin-updates.js";
13
+
14
+ describe("pluginUpdates config schema", () => {
15
+ test("defaults are manual / theirs / hourly", () => {
16
+ expect(PluginUpdatesConfigSchema.parse({})).toEqual({
17
+ mode: "manual",
18
+ strategy: "theirs",
19
+ checkIntervalMs: 3_600_000,
20
+ });
21
+ });
22
+
23
+ test("mode accepts only manual and auto", () => {
24
+ expect(PluginUpdatesConfigSchema.parse({ mode: "auto" }).mode).toBe("auto");
25
+ expect(
26
+ PluginUpdatesConfigSchema.safeParse({ mode: "sometimes" }).success,
27
+ ).toBe(false);
28
+ });
29
+
30
+ test("the unattended-hostile `assistant` strategy is not selectable", () => {
31
+ // It resolves conflicts by writing git conflict markers into the plugin's
32
+ // files, which unattended would leave broken code on disk.
33
+ expect(
34
+ PluginUpdatesConfigSchema.safeParse({ strategy: "assistant" }).success,
35
+ ).toBe(false);
36
+ });
37
+
38
+ test("sub-5-minute sweep intervals are rejected", () => {
39
+ expect(
40
+ PluginUpdatesConfigSchema.safeParse({ checkIntervalMs: 1_000 }).success,
41
+ ).toBe(false);
42
+ });
43
+ });
@@ -0,0 +1,131 @@
1
+ import { describe, expect, test } from "bun:test";
2
+
3
+ import { profileSupportsTools } from "../profile-tool-support.js";
4
+ import type { AssistantConfig } from "../schema.js";
5
+ import { LLMSchema } from "../schemas/llm.js";
6
+
7
+ /** An `AssistantConfig` carrying nothing the profile lookup does not read. */
8
+ function configWithProfiles(
9
+ profiles: Record<string, Record<string, unknown>>,
10
+ ): AssistantConfig {
11
+ return { llm: LLMSchema.parse({ profiles }) } as AssistantConfig;
12
+ }
13
+
14
+ describe("profileSupportsTools", () => {
15
+ test("reports false only for a model the catalog declares tool-less", () => {
16
+ const config = configWithProfiles({
17
+ "no-tools": { provider: "openrouter", model: "minimax/minimax-01" },
18
+ });
19
+ expect(profileSupportsTools("no-tools", config)).toBe(false);
20
+ });
21
+
22
+ test("reports true for a catalog model declared tool-capable", () => {
23
+ const config = configWithProfiles({
24
+ "with-tools": { provider: "anthropic", model: "claude-fable-5" },
25
+ });
26
+ expect(profileSupportsTools("with-tools", config)).toBe(true);
27
+ });
28
+
29
+ test("fails open for a BYOK model the catalog has never seen", () => {
30
+ const config = configWithProfiles({
31
+ byok: { provider: "openrouter", model: "acme/private-llm-9" },
32
+ });
33
+ expect(profileSupportsTools("byok", config)).toBeUndefined();
34
+ });
35
+
36
+ test("fails open for a profile key that resolves to nothing", () => {
37
+ expect(profileSupportsTools("not-a-profile", configWithProfiles({}))).toBe(
38
+ undefined,
39
+ );
40
+ });
41
+
42
+ test("verdicts a catalog default profile through the code catalog", () => {
43
+ // `balanced` carries no workspace body, so the answer has to come from the
44
+ // resolved default-profile catalog rather than `llm.profiles`.
45
+ expect(profileSupportsTools("balanced", configWithProfiles({}))).toBe(true);
46
+ });
47
+
48
+ test("reports false for a mix whose every arm is catalog-denied", () => {
49
+ const config = configWithProfiles({
50
+ "no-tools": { provider: "openrouter", model: "minimax/minimax-01" },
51
+ "no-tools-too": {
52
+ provider: "openrouter",
53
+ model: "minimax/minimax-m2-her",
54
+ },
55
+ blend: {
56
+ mix: [
57
+ { profile: "no-tools", weight: 1 },
58
+ { profile: "no-tools-too", weight: 1 },
59
+ ],
60
+ },
61
+ });
62
+ // Whichever arm the child's seed lands on, it cannot call tools.
63
+ expect(profileSupportsTools("blend", config)).toBe(false);
64
+ });
65
+
66
+ test("reports true for a mix whose every arm is catalog-capable", () => {
67
+ const config = configWithProfiles({
68
+ "tools-a": { provider: "anthropic", model: "claude-fable-5" },
69
+ "tools-b": {
70
+ provider: "anthropic",
71
+ model: "claude-haiku-4-5-20251001",
72
+ },
73
+ blend: {
74
+ mix: [
75
+ { profile: "tools-a", weight: 3 },
76
+ { profile: "tools-b", weight: 1 },
77
+ ],
78
+ },
79
+ });
80
+ expect(profileSupportsTools("blend", config)).toBe(true);
81
+ });
82
+
83
+ test("fails open for a mix of tool-capable and tool-less arms", () => {
84
+ const config = configWithProfiles({
85
+ "no-tools": { provider: "openrouter", model: "minimax/minimax-01" },
86
+ "with-tools": { provider: "anthropic", model: "claude-fable-5" },
87
+ blend: {
88
+ mix: [
89
+ { profile: "no-tools", weight: 1 },
90
+ { profile: "with-tools", weight: 1 },
91
+ ],
92
+ },
93
+ });
94
+ // The arm is picked from the child conversation's seed, and no such
95
+ // conversation exists yet, so neither arm's verdict speaks for the mix.
96
+ expect(profileSupportsTools("blend", config)).toBeUndefined();
97
+ });
98
+
99
+ test("fails open for a mix with an arm the catalog has never seen", () => {
100
+ const config = configWithProfiles({
101
+ "no-tools": { provider: "openrouter", model: "minimax/minimax-01" },
102
+ byok: { provider: "openrouter", model: "acme/private-llm-9" },
103
+ blend: {
104
+ mix: [
105
+ { profile: "no-tools", weight: 1 },
106
+ { profile: "byok", weight: 1 },
107
+ ],
108
+ },
109
+ });
110
+ expect(profileSupportsTools("blend", config)).toBeUndefined();
111
+ });
112
+
113
+ test("answers a mixed-capability mix identically on every call", () => {
114
+ const config = configWithProfiles({
115
+ "no-tools": { provider: "openrouter", model: "minimax/minimax-01" },
116
+ "with-tools": { provider: "anthropic", model: "claude-fable-5" },
117
+ blend: {
118
+ mix: [
119
+ { profile: "no-tools", weight: 1 },
120
+ { profile: "with-tools", weight: 1 },
121
+ ],
122
+ },
123
+ });
124
+ // No seed is available at probe time, so the verdict must not ride on a
125
+ // random arm pick.
126
+ const verdicts = new Set(
127
+ Array.from({ length: 50 }, () => profileSupportsTools("blend", config)),
128
+ );
129
+ expect([...verdicts]).toEqual([undefined]);
130
+ });
131
+ });
@@ -8,7 +8,7 @@ Each executor is a module exporting `run(input, context)` (the `SkillToolScript`
8
8
 
9
9
  ## Keeping public docs in sync
10
10
 
11
- A bundled skill's `SKILL.md` is its behavioral source of truth. Skills with a public reference page (`https://www.vellum.ai/docs/skills-reference/<slug>`, authored in the **vellum-assistant-platform** repo) are fingerprinted in `scripts/skill-docs-sync.manifest.json` and enforced by `skill-docs-sync-guard.test.ts`.
11
+ A bundled skill's `SKILL.md` is its behavioral source of truth. Skills with a public reference page (`https://www.vellum.ai/docs/skills-reference/<slug>`, authored in this repo at `clients/docs/src/app/docs/_components/skills-reference-<slug>-content.tsx`) are fingerprinted in `scripts/skill-docs-sync.manifest.json` and enforced by `skill-docs-sync-guard.test.ts`.
12
12
 
13
13
  When you change a documented skill's `SKILL.md`, the guard fails until you reconcile it:
14
14
 
@@ -75,7 +75,7 @@ Every app is multi-file TSX (`formatVersion: 2`). Never write a root-level `inde
75
75
 
76
76
  ## Responsive & design system
77
77
 
78
- Every app works phone (~360px) to desktop (~1400px+). The `<turn_context>` block carries a `client_os:` field (the OS the user is on): `ios`/`android` → mobile-first (design narrow first, body 17px); `macos`/`web` → desktop-first (multi-column, body 14px); absent → desktop-first unless the request implies phone use ("for my iPhone"). (The sibling `interface:` field is the transport surface always `web` for the apps so key layout off `client_os`, not `interface`.)
78
+ Every app works phone (~360px) to desktop (~1400px+). The `<turn_context>` block carries a `client_os:` field (the OS the user is on): `ios`/`android` → mobile-first (design narrow first, body 17px); `macos`/`windows`/`web` → desktop-first (multi-column, body 14px); absent → desktop-first unless the request implies phone use ("for my iPhone"). (The sibling `interface:` field is the transport surface, always `web` for the apps, so key layout off `client_os`, not `interface`.)
79
79
 
80
80
  **Universal baseline — every build, regardless of client_os:**
81
81
  - Viewport meta: `width=device-width, initial-scale=1, viewport-fit=cover`. Never `user-scalable=no` (blocks accessibility zoom).
@@ -4,12 +4,12 @@ Every app must work across phone (~360px) to desktop (~1400px+).
4
4
 
5
5
  ## Mode selection
6
6
 
7
- The conversation context's `<turn_context>` block carries a `client_os:` field the OS the user is on. (Key layout off this, not the sibling `interface:` field: `interface:` is the transport surface and is always `web` for the web/iOS/macOS apps.)
7
+ The conversation context's `<turn_context>` block carries a `client_os:` field, the OS the user is on. (Key layout off this, not the sibling `interface:` field: `interface:` is the transport surface and is always `web` for the web/iOS/macOS/Windows apps.)
8
8
 
9
9
  **If `client_os: ios` or `android`** (any mobile-web surface):
10
10
  → Mobile-first build. Design the narrow viewport first, enhance upward.
11
11
 
12
- **If `client_os: macos` or `web`**:
12
+ **If `client_os: macos`, `windows`, or `web`**:
13
13
  → Desktop-first build. Design larger composition first; narrow fallback still meets the universal baseline below.
14
14
 
15
15
  **If field is absent or ambiguous**:
@@ -52,6 +52,6 @@ The conversation context's `<turn_context>` block carries a `client_os:` field
52
52
  - Bottom-anchor the primary action: `position: sticky; bottom: env(safe-area-inset-bottom)`.
53
53
  - Replace side modals and popovers with bottom sheets.
54
54
 
55
- ## Desktop-first priorities (`client_os: macos` / `web`)
55
+ ## Desktop-first priorities (`client_os: macos` / `windows` / `web`)
56
56
 
57
57
  Multi-column composition, hover-rich affordances, denser information, side modals, inline primary actions. The universal baseline above is still the floor — narrow view must still work.
@@ -121,7 +121,11 @@ assistant conversations wake "$id" --hint "Summarize the new items" --external-c
121
121
 
122
122
  ## Inference Profile
123
123
 
124
- Execute-mode runs use the default `mainAgent` model selection unless the schedule pins an `inference_profile` (a key from `llm.profiles`). Pin a profile when a recurring task should run on a specific model e.g. a cost-optimized profile for a high-frequency digest. Pass `inference_profile: null` on update to revert to the default. The pinned profile is shown on the schedule's details page in settings.
124
+ Every schedule carries a pinned `inference_profile` (a key from `llm.profiles`), so a recurring task keeps running on the model (and the price) it was created under even when the user later changes their default profile. Creating a schedule without `inference_profile` pins it to the user's current default; pass one explicitly when a task should run on a specific model, e.g. a cost-optimized profile for a high-frequency digest. Passing `inference_profile: null` on update re-pins the schedule to the user's current default rather than unpinning it. The pinned profile is shown on the schedule's details page in settings.
125
+
126
+ The pin governs the agent turns a schedule runs: an execute-mode run, a wake-mode run, and the turns a script-mode schedule hands off to the agent loop with `assistant conversations wake`. The schedule id travels in the script's environment, so those handoff turns run on the pinned profile with no flag to pass. Notify-mode schedules run no agent turn at all, so their pin never comes into play.
127
+
128
+ Workflow-mode schedules are the exception: each step of a workflow resolves its own model, so a workflow schedule's pin does not govern its runs.
125
129
 
126
130
  ## Conversation Group
127
131
 
@@ -110,7 +110,7 @@
110
110
  },
111
111
  "inference_profile": {
112
112
  "type": "string",
113
- "description": "Inference profile (a key from llm.profiles) the schedule's LLM-executed runs should use, e.g. to run a heavy task on a cheaper model. When omitted, runs use the default mainAgent model selection. Must name a configured profile."
113
+ "description": "Inference profile (a key from llm.profiles) the schedule's LLM-executed runs should use, e.g. to run a heavy task on a cheaper model. When omitted, the schedule is pinned to the user's current default profile so its model does not change later. Must name a configured profile. Workflow-mode schedules resolve a model per workflow step, so the pin does not govern their runs."
114
114
  },
115
115
  "group": {
116
116
  "type": "string",
@@ -233,7 +233,7 @@
233
233
  },
234
234
  "inference_profile": {
235
235
  "type": ["string", "null"],
236
- "description": "Inference profile (a key from llm.profiles) the schedule's LLM-executed runs should use. Pass null to clear the override and revert to the default mainAgent model selection. Must name a configured profile."
236
+ "description": "Inference profile (a key from llm.profiles) the schedule's LLM-executed runs should use. Pass null to re-pin the schedule to the user's current default profile. Must name a configured profile. Workflow-mode schedules resolve a model per workflow step, so the pin does not govern their runs."
237
237
  },
238
238
  "group": {
239
239
  "type": ["string", "null"],
@@ -3,7 +3,7 @@
3
3
  "tools": [
4
4
  {
5
5
  "name": "voice_config_update",
6
- "description": "Update a voice configuration setting. Use tts_provider / stt_provider to switch the active TTS / STT provider. Provider \"vellum\" is Vellum-managed speech (billed to your organization; requires a Vellum platform connection via 'assistant platform connect'); any other provider uses the user's own API key. Valid TTS providers come from the provider catalog (vellum, elevenlabs, fish-audio, deepgram, xai); valid STT providers: vellum, deepgram, google-gemini, openai-whisper, xai. Use tts_voice_id to change the voice; it targets whichever TTS provider is currently active. For elevenlabs, pass an ElevenLabs voice ID. For vellum (managed), pass a managed voice model ID: this may be an ElevenLabs voice ID (managed speech serves the same ElevenLabs voices) or a Deepgram Aura model ID (e.g. aura-2-thalia-en); only rate-carded voices synthesize, so prefer models from the managed catalog (daemon route GET tts/managed-voices, also used by the web voice picker). For deepgram, pass a Deepgram Aura model ID. Use fish_audio_reference_id for Fish Audio voice reference. Use stt_language to set the spoken language for speech recognition: one of the 49 base language codes on the verified Deepgram nova-3 monolingual roster (e.g. en, es, hi, ta, zh, ko), or \"multi\" for code-switching mid-sentence across its 10-language roster (English, Spanish, French, German, Hindi, Russian, Portuguese, Japanese, Italian, Dutch; e.g. Hinglish); plain language names like \"tamil\" or \"multilingual\" are accepted and normalized. Accepted values follow the configured STT provider: vellum-managed and deepgram accept the full roster plus \"multi\"; xai accepts only the 10 multilingual-roster codes (en, es, fr, de, hi, ru, pt, ja, it, nl) and rejects \"multi\" and the extended codes (both are verified for Deepgram nova-3 only); google-gemini / openai-whisper auto-detect natively, so the value persists but is ignored while they are active. Deepgram uses the same API key for TTS and STT. Changes persist to services.stt / services.tts config and take effect immediately.",
6
+ "description": "Update a voice configuration setting. Use tts_provider / stt_provider to switch the active TTS / STT provider. Provider \"vellum\" is Vellum-managed speech (billed to your organization; requires a Vellum platform connection via 'assistant platform connect'); any other provider uses the user's own API key. Valid TTS providers come from the provider catalog (vellum, elevenlabs, fish-audio, deepgram, xai); valid STT providers: vellum, deepgram, google-gemini, openai-whisper, xai. Use tts_voice_id to change the voice; it targets whichever TTS provider is currently active. For elevenlabs, pass an ElevenLabs voice ID. For vellum (managed), pass a managed voice model ID: this may be an ElevenLabs voice ID (managed speech serves the same ElevenLabs voices) or a Deepgram Aura model ID (e.g. aura-2-thalia-en); only rate-carded voices synthesize, so prefer models from the managed catalog (daemon route GET tts/managed-voices, also used by the web voice picker). For deepgram, pass a Deepgram Aura model ID. Use fish_audio_reference_id for Fish Audio voice reference. Use stt_language to set the spoken language for speech recognition: one of the 50 base language codes on the verified Deepgram nova-3 monolingual roster (e.g. en, es, hi, ta, zh, ko), or \"multi\" for code-switching mid-sentence across its 10-language roster (English, Spanish, French, German, Hindi, Russian, Portuguese, Japanese, Italian, Dutch; e.g. Hinglish); plain language names like \"tamil\" or \"multilingual\" are accepted and normalized. Accepted values follow the configured STT provider: vellum-managed and deepgram accept the full roster plus \"multi\"; xai accepts only the 10 multilingual-roster codes (en, es, fr, de, hi, ru, pt, ja, it, nl) and rejects \"multi\" and the extended codes (both are verified for Deepgram nova-3 only); google-gemini / openai-whisper auto-detect natively, so the value persists but is ignored while they are active. Deepgram uses the same API key for TTS and STT. Changes persist to services.stt / services.tts config and take effect immediately.",
7
7
  "category": "system",
8
8
  "risk": "low",
9
9
  "input_schema": {
@@ -23,7 +23,7 @@
23
23
  "description": "The voice setting to change. tts_provider / stt_provider select the active provider for each service (\"vellum\" = Vellum-managed speech; anything else = the user's own API key). tts_voice_id sets the voice for the currently active TTS provider (ElevenLabs voice ID for elevenlabs; a managed voice model ID for vellum, meaning an ElevenLabs voice ID or a Deepgram Aura model ID like aura-2-thalia-en; a Deepgram Aura model ID for deepgram; voice ID for xai). fish_audio_reference_id sets the Fish Audio voice reference. stt_language sets the spoken language for speech recognition (vellum-managed/deepgram: the full roster plus \"multi\"; xai: only the 10 multilingual-roster codes, with \"multi\" and the extended codes rejected; google-gemini and openai-whisper auto-detect and ignore it). Deepgram shares one API key across TTS and STT."
24
24
  },
25
25
  "value": {
26
- "description": "The new value for the setting. For tts_provider: one of vellum, elevenlabs, fish-audio, deepgram, xai. For stt_provider: one of vellum, deepgram, google-gemini, openai-whisper, xai. For tts_voice_id: a voice ID for the active TTS provider: an alphanumeric ElevenLabs voice ID (elevenlabs), a managed voice model ID which may be an ElevenLabs voice ID or a Deepgram Aura model ID like aura-2-thalia-en (vellum), or a Deepgram Aura model ID (deepgram). For fish_audio_reference_id: a Fish Audio voice reference ID. For stt_language: one of the 49 base codes on the nova-3 monolingual roster (e.g. en, es, hi, ta, zh, ko), or multi (code-switching across its 10-language roster); language names like \"hindi\", \"tamil\", or \"multilingual\" are also accepted and normalized; when the configured STT provider is xai, only the 10 multilingual-roster codes (en, es, fr, de, hi, ru, pt, ja, it, nl) are accepted. For conversation_timeout: seconds (5, 10, 15, 30, or 60). For activation_key: key identifier string."
26
+ "description": "The new value for the setting. For tts_provider: one of vellum, elevenlabs, fish-audio, deepgram, xai. For stt_provider: one of vellum, deepgram, google-gemini, openai-whisper, xai. For tts_voice_id: a voice ID for the active TTS provider: an alphanumeric ElevenLabs voice ID (elevenlabs), a managed voice model ID which may be an ElevenLabs voice ID or a Deepgram Aura model ID like aura-2-thalia-en (vellum), or a Deepgram Aura model ID (deepgram). For fish_audio_reference_id: a Fish Audio voice reference ID. For stt_language: one of the 50 base codes on the nova-3 monolingual roster (e.g. en, es, hi, ta, zh, ko), or multi (code-switching across its 10-language roster); language names like \"hindi\", \"tamil\", or \"multilingual\" are also accepted and normalized; when the configured STT provider is xai, only the 10 multilingual-roster codes (en, es, fr, de, hi, ru, pt, ja, it, nl) are accepted. For conversation_timeout: seconds (5, 10, 15, 30, or 60). For activation_key: key identifier string."
27
27
  }
28
28
  }
29
29
  },
@@ -179,6 +179,7 @@ const STT_LANGUAGE_ALIASES: Record<
179
179
  thai: "th",
180
180
  turkish: "tr",
181
181
  ukrainian: "uk",
182
+ urdu: "ur",
182
183
  vietnamese: "vi",
183
184
  multilingual: "multi",
184
185
  auto: "multi",