@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
@@ -23,6 +23,7 @@ import {
23
23
  } from "../persistence/conversation-crud.js";
24
24
  import { getDb } from "../persistence/db-connection.js";
25
25
  import { initializeDb } from "../persistence/db-init.js";
26
+ import { resolveDefaultScheduleInferenceProfile } from "../schedule/inference-profile.js";
26
27
  import {
27
28
  createOwnerDeferredWake,
28
29
  createSchedule,
@@ -82,6 +83,9 @@ describe("scheduler wake mode", () => {
82
83
  source: "defer",
83
84
  persistTriggerAsEvent: true,
84
85
  trustContext: INTERNAL_GUARDIAN_TRUST_CONTEXT,
86
+ // Every schedule is pinned at creation, so the row always carries a
87
+ // profile for the woken turn to run under.
88
+ forceOverrideProfile: resolveDefaultScheduleInferenceProfile()!,
85
89
  });
86
90
 
87
91
  // AND processMessage is never called (wake mode doesn't use it)
@@ -113,6 +117,7 @@ describe("scheduler wake mode", () => {
113
117
  hint: "Follow up",
114
118
  source: "defer",
115
119
  persistTriggerAsEvent: true,
120
+ forceOverrideProfile: resolveDefaultScheduleInferenceProfile()!,
116
121
  });
117
122
  });
118
123
 
@@ -108,7 +108,10 @@ import {
108
108
  initializeProviders,
109
109
  listProviders,
110
110
  } from "../providers/registry.js";
111
- import { ROUTES } from "../runtime/routes/secret-routes.js";
111
+ import {
112
+ notifyCesOfAssistantApiKeyUpdate,
113
+ ROUTES,
114
+ } from "../runtime/routes/secret-routes.js";
112
115
 
113
116
  const addRoute = ROUTES.find(
114
117
  (r) => r.method === "POST" && r.endpoint === "secrets",
@@ -176,6 +179,31 @@ describe("secret routes managed proxy registry sync", () => {
176
179
  expect(lastGeminiConstructorOpts).toBeDefined();
177
180
  });
178
181
 
182
+ test("the CES key-update notification with no CES client logs a warning", async () => {
183
+ // When getCesClient() is undefined the push is skipped, but the skip must
184
+ // be logged, not dropped silently. Exercise the extracted helper directly
185
+ // with an injected logger.
186
+ const warns: string[] = [];
187
+ const fakeLogger = {
188
+ info: () => {},
189
+ warn: (a: unknown, b?: unknown) => {
190
+ warns.push(typeof a === "string" ? a : String(b));
191
+ },
192
+ };
193
+
194
+ await notifyCesOfAssistantApiKeyUpdate(
195
+ "ast-managed-key",
196
+ undefined,
197
+ fakeLogger as unknown as Parameters<
198
+ typeof notifyCesOfAssistantApiKeyUpdate
199
+ >[2],
200
+ );
201
+
202
+ expect(warns.some((m) => m.includes("no CES client is connected"))).toBe(
203
+ true,
204
+ );
205
+ });
206
+
179
207
  test("provider API key writes notify live-conversation refresh listeners", async () => {
180
208
  await addApiKey("fireworks", "fw-key");
181
209
 
@@ -19,6 +19,7 @@ import * as encryptedStore from "../security/encrypted-store.js";
19
19
  import {
20
20
  _resetBackend,
21
21
  deleteSecureKeyAsync,
22
+ getActiveBackendName,
22
23
  getSecureKeyAsync,
23
24
  getSecureKeyResultAsync,
24
25
  listSecureKeysAsync,
@@ -58,6 +59,9 @@ describe("secure-keys", () => {
58
59
  _resetBackend();
59
60
  delete process.env.VELLUM_DEV;
60
61
  delete process.env.VELLUM_DESKTOP_APP;
62
+ delete process.env.IS_CONTAINERIZED;
63
+ delete process.env.CES_CREDENTIAL_URL;
64
+ delete process.env.CES_LOCAL_SOCKET;
61
65
  });
62
66
 
63
67
  afterAll(() => {
@@ -507,4 +511,91 @@ describe("secure-keys", () => {
507
511
  ).toBeUndefined();
508
512
  });
509
513
  });
514
+
515
+ // -----------------------------------------------------------------------
516
+ // Encrypted-store → CES upgrade without a reconnect callback
517
+ // -----------------------------------------------------------------------
518
+ //
519
+ // Worker/CLI processes inject a CES client whose handshake is still in
520
+ // flight, so the first credential read falls back to the encrypted store.
521
+ // These processes never register `_cesReconnect`. Once the handshake
522
+ // completes and the client reports ready, the next read must upgrade to
523
+ // CES RPC, since the live client already exists and no reconnect is needed.
524
+ describe("encrypted-store → CES upgrade without a reconnect callback", () => {
525
+ test("upgrades to CES RPC once the injected client becomes ready", async () => {
526
+ const client = makeMockCesClient(false);
527
+ setCesClient(client);
528
+
529
+ // Not ready yet → falls back to the encrypted store (local mode).
530
+ encryptedStore.setKey("openai", "local-value");
531
+ expect(await getSecureKeyAsync("openai")).toBe("local-value");
532
+ expect(getActiveBackendName()).toBe("encrypted-store");
533
+
534
+ // Handshake completes; no `_cesReconnect` was ever registered.
535
+ client.setReady(true);
536
+
537
+ await getSecureKeyResultAsync("openai");
538
+ expect(getActiveBackendName()).toBe("ces-rpc");
539
+ });
540
+ });
541
+
542
+ // -----------------------------------------------------------------------
543
+ // Containerized pod with no ready CES backend
544
+ // -----------------------------------------------------------------------
545
+ //
546
+ // On a pod the local encrypted store does not exist. When no CES path is
547
+ // ready, reads must report the store as unreachable (presence
548
+ // indeterminate) rather than absent. A provisioned credential is never
549
+ // reported as "no key". The unreachable result must not be pinned: once a
550
+ // CES client arrives, the next read routes through it.
551
+ describe("containerized pod with no ready CES backend", () => {
552
+ test("reads report unreachable (indeterminate), never absent", async () => {
553
+ process.env.IS_CONTAINERIZED = "1";
554
+ delete process.env.CES_CREDENTIAL_URL;
555
+ delete process.env.CES_LOCAL_SOCKET;
556
+ _resetBackend();
557
+
558
+ const result = await getSecureKeyResultAsync("openai");
559
+ expect(result.value).toBeUndefined();
560
+ // unreachable → presence indeterminate, NEVER absent.
561
+ expect(result.unreachable).toBe(true);
562
+ });
563
+
564
+ test("does not pin the unreachable result: a ready CES client wins the next read", async () => {
565
+ process.env.IS_CONTAINERIZED = "1";
566
+ delete process.env.CES_CREDENTIAL_URL;
567
+ delete process.env.CES_LOCAL_SOCKET;
568
+ _resetBackend();
569
+
570
+ expect((await getSecureKeyResultAsync("openai")).unreachable).toBe(true);
571
+
572
+ setCesClient(makeMockCesClient(true));
573
+
574
+ const afterAttach = await getSecureKeyResultAsync("openai");
575
+ expect(afterAttach.unreachable).toBe(false);
576
+ expect(getActiveBackendName()).toBe("ces-rpc");
577
+ });
578
+ });
579
+
580
+ // -----------------------------------------------------------------------
581
+ // Encrypted-store read error vs genuinely-absent (local mode)
582
+ // -----------------------------------------------------------------------
583
+ describe("encrypted-store read error vs absent", () => {
584
+ test("genuinely-missing store → absent (unreachable false)", async () => {
585
+ const result = await getSecureKeyResultAsync("nope");
586
+ expect(result.value).toBeUndefined();
587
+ expect(result.unreachable).toBe(false);
588
+ });
589
+
590
+ test("store exists but key material is missing → unreachable", async () => {
591
+ encryptedStore.setKey("openai", "sk-1");
592
+ // Remove the store key so the existing entry can no longer be decrypted.
593
+ rmSync(join(TEST_DIR, "store.key"));
594
+ _resetBackend();
595
+
596
+ const result = await getSecureKeyResultAsync("openai");
597
+ expect(result.value).toBeUndefined();
598
+ expect(result.unreachable).toBe(true);
599
+ });
600
+ });
510
601
  });
@@ -1,5 +1,6 @@
1
1
  import { beforeEach, describe, expect, test } from "bun:test";
2
2
 
3
+ import type { AnsweredQuestion } from "../api/events/question-answered.js";
3
4
  import { renderHistoryContent } from "../daemon/handlers/shared.js";
4
5
  import type { ToolActivityMetadata } from "../daemon/message-types/web-activity.js";
5
6
  import {
@@ -298,9 +299,9 @@ describe("renderHistoryContent", () => {
298
299
  expect(output.toolCalls[0].errorCode).toBe("acp_claude_oauth_missing");
299
300
  });
300
301
 
301
- test("emits the attachment id for a workspace_ref tool-result image", () => {
302
+ test("emits the attachment id for a workspace_ref tool-result image", async () => {
302
303
  // "aGVsbG8=" = "hello" — a stand-in for screenshot bytes.
303
- const stored = uploadAttachment("shot.png", "image/png", "aGVsbG8=");
304
+ const stored = await uploadAttachment("shot.png", "image/png", "aGVsbG8=");
304
305
  const output = renderHistoryContent([
305
306
  { type: "tool_use", id: "tu_1", name: "browser_screenshot", input: {} },
306
307
  {
@@ -549,6 +550,95 @@ describe("renderHistoryContent", () => {
549
550
  expect(output.toolCalls[0].activityMetadata).toEqual(activityMetadata);
550
551
  });
551
552
 
553
+ // ── Persisted answered ask_question ────────────────────────────────────────
554
+
555
+ test("hydrates a persisted _answeredQuestion onto its tool_use block", () => {
556
+ // Mirrors what the daemon stamps when an `ask_question` prompt settles, so
557
+ // the answered card rehydrates on a reopened conversation instead of the
558
+ // user's decision disappearing with the interactive prompt.
559
+ const answeredQuestion: AnsweredQuestion = {
560
+ requestId: "req-1",
561
+ questions: [
562
+ {
563
+ id: "q1",
564
+ question: "Which Alice?",
565
+ options: [
566
+ { id: "alice_work", label: "Alice (work)" },
567
+ { id: "alice_personal", label: "Alice (personal)" },
568
+ ],
569
+ },
570
+ ],
571
+ responses: [
572
+ { questionId: "q1", decision: "option", optionId: "alice_work" },
573
+ ],
574
+ overall: "completed",
575
+ };
576
+
577
+ const output = renderHistoryContent([
578
+ {
579
+ type: "tool_use",
580
+ id: "tu_q",
581
+ name: "ask_question",
582
+ input: { questions: [{ question: "Which Alice?" }] },
583
+ _answeredQuestion: answeredQuestion,
584
+ },
585
+ ]);
586
+
587
+ expect(output.toolCalls[0].answeredQuestion).toEqual(answeredQuestion);
588
+ });
589
+
590
+ test("hydrates a free-text answer verbatim", () => {
591
+ const output = renderHistoryContent([
592
+ {
593
+ type: "tool_use",
594
+ id: "tu_q",
595
+ name: "ask_question",
596
+ input: {},
597
+ _answeredQuestion: {
598
+ requestId: "req-2",
599
+ questions: [{ id: "q1", question: "Which fruit?", options: [] }],
600
+ responses: [
601
+ { questionId: "q1", decision: "free_text", text: "Cherry" },
602
+ ],
603
+ overall: "completed",
604
+ },
605
+ },
606
+ ]);
607
+
608
+ expect(output.toolCalls[0].answeredQuestion?.responses).toEqual([
609
+ { questionId: "q1", decision: "free_text", text: "Cherry" },
610
+ ]);
611
+ });
612
+
613
+ test("leaves answeredQuestion absent on rows that predate the annotation", () => {
614
+ const output = renderHistoryContent([
615
+ {
616
+ type: "tool_use",
617
+ id: "tu_q",
618
+ name: "ask_question",
619
+ input: { questions: [{ question: "Which Alice?" }] },
620
+ },
621
+ ]);
622
+
623
+ expect(output.toolCalls[0].answeredQuestion).toBeUndefined();
624
+ });
625
+
626
+ test("ignores a malformed _answeredQuestion annotation", () => {
627
+ // The card renders these contents directly, so a rider that does not match
628
+ // the schema must degrade to no card rather than a crashing render.
629
+ const output = renderHistoryContent([
630
+ {
631
+ type: "tool_use",
632
+ id: "tu_q",
633
+ name: "ask_question",
634
+ input: {},
635
+ _answeredQuestion: { requestId: "req-3", overall: "sideways" },
636
+ },
637
+ ]);
638
+
639
+ expect(output.toolCalls[0].answeredQuestion).toBeUndefined();
640
+ });
641
+
552
642
  test("ignores non-object _activityMetadata annotations", () => {
553
643
  const output = renderHistoryContent([
554
644
  {
@@ -1084,7 +1174,7 @@ describe("getAttachmentsForMessage", () => {
1084
1174
 
1085
1175
  test("returns attachments linked to a message", async () => {
1086
1176
  const msgId = await createMessage("assistant", "Here is a chart");
1087
- const stored = uploadAttachment("chart.png", "image/png", "iVBORw==");
1177
+ const stored = await uploadAttachment("chart.png", "image/png", "iVBORw==");
1088
1178
  linkAttachmentToMessage(msgId, stored.id, 0);
1089
1179
 
1090
1180
  const result = getAttachmentsForMessage(msgId);
@@ -1101,8 +1191,8 @@ describe("getAttachmentsForMessage", () => {
1101
1191
 
1102
1192
  test("returns multiple attachments in position order", async () => {
1103
1193
  const msgId = await createMessage("assistant", "Two files");
1104
- const a1 = uploadAttachment("first.txt", "text/plain", "AAAA");
1105
- const a2 = uploadAttachment("second.txt", "text/plain", "BBBB");
1194
+ const a1 = await uploadAttachment("first.txt", "text/plain", "AAAA");
1195
+ const a2 = await uploadAttachment("second.txt", "text/plain", "BBBB");
1106
1196
 
1107
1197
  linkAttachmentToMessage(msgId, a2.id, 1);
1108
1198
  linkAttachmentToMessage(msgId, a1.id, 0);
@@ -1115,8 +1205,8 @@ describe("getAttachmentsForMessage", () => {
1115
1205
 
1116
1206
  test("returns all attachments linked to a message", async () => {
1117
1207
  const msgId = await createMessage("assistant", "Mixed");
1118
- const a1 = uploadAttachment("a.png", "image/png", "AAAA");
1119
- const a2 = uploadAttachment("b.png", "image/png", "BBBB");
1208
+ const a1 = await uploadAttachment("a.png", "image/png", "AAAA");
1209
+ const a2 = await uploadAttachment("b.png", "image/png", "BBBB");
1120
1210
 
1121
1211
  linkAttachmentToMessage(msgId, a1.id, 0);
1122
1212
  linkAttachmentToMessage(msgId, a2.id, 1);
@@ -38,41 +38,41 @@ describe("MAX_FILE_SIZE_BYTES", () => {
38
38
  // ---------------------------------------------------------------------------
39
39
 
40
40
  describe("checkFileSizeOnDisk", () => {
41
- test("returns undefined for a file within the default limit", () => {
41
+ test("returns undefined for a file within the default limit", async () => {
42
42
  const dir = makeTempDir();
43
43
  const filePath = join(dir, "small.txt");
44
44
  writeFileSync(filePath, "hello");
45
45
 
46
- expect(checkFileSizeOnDisk(filePath)).toBeUndefined();
46
+ expect(await checkFileSizeOnDisk(filePath)).toBeUndefined();
47
47
  });
48
48
 
49
- test("returns error string for a file exceeding a custom limit", () => {
49
+ test("returns error string for a file exceeding a custom limit", async () => {
50
50
  const dir = makeTempDir();
51
51
  const filePath = join(dir, "big.txt");
52
52
  writeFileSync(filePath, "x".repeat(200));
53
53
 
54
- const result = checkFileSizeOnDisk(filePath, 100);
54
+ const result = await checkFileSizeOnDisk(filePath, 100);
55
55
  expect(result).toBeDefined();
56
56
  expect(result).toContain("exceeds");
57
57
  expect(result).toContain("limit");
58
58
  expect(result).toContain(filePath);
59
59
  });
60
60
 
61
- test("returns undefined for a file exactly at the custom limit", () => {
61
+ test("returns undefined for a file exactly at the custom limit", async () => {
62
62
  const dir = makeTempDir();
63
63
  const filePath = join(dir, "exact.txt");
64
64
  writeFileSync(filePath, "x".repeat(100));
65
65
 
66
- expect(checkFileSizeOnDisk(filePath, 100)).toBeUndefined();
66
+ expect(await checkFileSizeOnDisk(filePath, 100)).toBeUndefined();
67
67
  });
68
68
 
69
- test("uses default limit when none is provided", () => {
69
+ test("uses default limit when none is provided", async () => {
70
70
  const dir = makeTempDir();
71
71
  const filePath = join(dir, "tiny.txt");
72
72
  writeFileSync(filePath, "a");
73
73
 
74
74
  // Should not error since 1 byte is well under 100 MB
75
- expect(checkFileSizeOnDisk(filePath)).toBeUndefined();
75
+ expect(await checkFileSizeOnDisk(filePath)).toBeUndefined();
76
76
  });
77
77
  });
78
78
 
@@ -5,8 +5,9 @@ import { describe, expect, test } from "bun:test";
5
5
 
6
6
  /**
7
7
  * Guard for skill ↔ public-docs drift. A bundled skill's SKILL.md is the
8
- * behavioral source of truth; its public reference page lives in a separate
9
- * repo (vellum-assistant-platform, https://www.vellum.ai/docs/skills-reference).
8
+ * behavioral source of truth; its public reference page lives in this repo at
9
+ * clients/docs/src/app/docs/_components/skills-reference-<slug>-content.tsx
10
+ * (served at https://www.vellum.ai/docs/skills-reference/<slug>).
10
11
  * `scripts/check-skill-docs-sync.ts` fingerprints each documented SKILL.md so a
11
12
  * behavior change can't land without the author reconciling the docs page and
12
13
  * re-recording the fingerprint.
@@ -1,3 +1,4 @@
1
+ import { readFileSync } from "node:fs";
1
2
  import { mkdtemp, rm, writeFile } from "node:fs/promises";
2
3
  import { tmpdir } from "node:os";
3
4
  import { join } from "node:path";
@@ -6,6 +7,9 @@ import { afterAll, beforeAll, describe, expect, test } from "bun:test";
6
7
  import type { SkillToolEntry } from "../config/skills.js";
7
8
  import { RiskLevel } from "../permissions/types.js";
8
9
  import { computeSkillVersionHash } from "../skills/version-hash.js";
10
+ import { resolveSubagentRole } from "../subagent/role-resolution.js";
11
+ import type { SubagentRole } from "../subagent/types.js";
12
+ import { bundledToolInputMisuseKeys } from "../tools/shared/input-misuse.js";
9
13
  import {
10
14
  createSkillTool,
11
15
  createSkillToolsFromManifest,
@@ -490,6 +494,249 @@ describe("createSkillTool — required/type/enum validation", () => {
490
494
  });
491
495
  });
492
496
 
497
+ // ---------------------------------------------------------------------------
498
+ // createSkillTool: parameter misuse redirects
499
+ // ---------------------------------------------------------------------------
500
+
501
+ /**
502
+ * The subagent skill's real `subagent_read` manifest entry. Its redirect keys
503
+ * are absent from the advertised `properties`, so manifest validation is the
504
+ * layer that has to surface the redirect: nothing downstream of it runs.
505
+ */
506
+ function subagentReadEntry(): SkillToolEntry {
507
+ const manifest = JSON.parse(
508
+ readFileSync(
509
+ join(import.meta.dir, "../config/bundled-skills/subagent/TOOLS.json"),
510
+ "utf-8",
511
+ ),
512
+ ) as { tools: SkillToolEntry[] };
513
+ const entry = manifest.tools.find((t) => t.name === "subagent_read");
514
+ if (!entry) {
515
+ throw new Error("subagent_read is missing from the subagent TOOLS.json");
516
+ }
517
+ return entry;
518
+ }
519
+
520
+ function subagentReadTool() {
521
+ return createSkillTool(
522
+ subagentReadEntry(),
523
+ "/skills/subagent",
524
+ "v1:test",
525
+ BUNDLED,
526
+ );
527
+ }
528
+
529
+ describe("createSkillTool: parameter misuse redirects", () => {
530
+ for (const key of ["path", "file", "filename"]) {
531
+ test(`subagent_read sends "${key}" to file_read`, async () => {
532
+ const result = await subagentReadTool().execute(
533
+ { [key]: "/tmp/notes.md" },
534
+ makeContext(),
535
+ );
536
+
537
+ expect(result.isError).toBe(true);
538
+ expect(result.content).toBe(
539
+ "subagent_read returns a subagent's output, it does not read files. Use file_read for files. Pass subagent_id or label here.",
540
+ );
541
+ });
542
+ }
543
+
544
+ for (const key of ["subagentId", "agent_id"]) {
545
+ test(`subagent_read names "${key}" as a misspelling of subagent_id`, async () => {
546
+ const result = await subagentReadTool().execute(
547
+ { [key]: "sa-1" },
548
+ makeContext(),
549
+ );
550
+
551
+ expect(result.isError).toBe(true);
552
+ expect(result.content).toBe(
553
+ "Unknown parameter. Use subagent_id (snake_case) or label.",
554
+ );
555
+ });
556
+ }
557
+
558
+ test("a key with no redirect keeps the generic validation error", async () => {
559
+ const result = await subagentReadTool().execute(
560
+ { subagent_id: "sa-1", nonsense: 1 },
561
+ makeContext(),
562
+ );
563
+
564
+ expect(result.isError).toBe(true);
565
+ expect(result.content).toContain('Invalid input for tool "subagent_read"');
566
+ expect(result.content).toContain('Unknown parameter "nonsense"');
567
+ });
568
+
569
+ test("redirect keys stay out of the advertised schema", () => {
570
+ const properties = (
571
+ subagentReadEntry().input_schema as {
572
+ properties: Record<string, unknown>;
573
+ }
574
+ ).properties;
575
+
576
+ for (const key of bundledToolInputMisuseKeys("subagent_read")) {
577
+ expect(properties[key]).toBeUndefined();
578
+ }
579
+ });
580
+
581
+ test("a non-bundled skill reusing the name keeps the generic validation error", async () => {
582
+ // Tool names are not reserved, so a workspace skill can define its own
583
+ // `subagent_read` where `path` is a declared parameter. Its validation
584
+ // errors must describe its own manifest, not Vellum's file-reader redirect.
585
+ const tool = createSkillTool(
586
+ makeEntry({
587
+ name: "subagent_read",
588
+ input_schema: {
589
+ type: "object",
590
+ properties: { path: { type: "string" } },
591
+ required: ["path"],
592
+ },
593
+ }),
594
+ "/workspace/skills/notes",
595
+ "v1:test",
596
+ false,
597
+ );
598
+
599
+ const result = await tool.execute(
600
+ { path: "/tmp/notes.md", nonsense: 1 },
601
+ makeContext(),
602
+ );
603
+
604
+ expect(result.isError).toBe(true);
605
+ expect(result.content).toContain('Invalid input for tool "subagent_read"');
606
+ expect(result.content).toContain('Unknown parameter "nonsense"');
607
+ });
608
+
609
+ test("an unspecified owner keeps the generic validation error", async () => {
610
+ const tool = createSkillTool(
611
+ subagentReadEntry(),
612
+ "/skills/subagent",
613
+ "v1:test",
614
+ );
615
+
616
+ const result = await tool.execute({ path: "/tmp/notes.md" }, makeContext());
617
+
618
+ expect(result.isError).toBe(true);
619
+ expect(result.content).toContain('Invalid input for tool "subagent_read"');
620
+ expect(result.content).toContain('Unknown parameter "path"');
621
+ });
622
+
623
+ test("a tool with no rules keeps the generic validation error", async () => {
624
+ const hash = computeSkillVersionHash(tempDir);
625
+ const tool = createSkillTool(
626
+ makeEntry({ executor: "echo.ts" }),
627
+ tempDir,
628
+ hash,
629
+ BUNDLED,
630
+ );
631
+
632
+ const result = await tool.execute(
633
+ { query: "hello", path: "/tmp/notes.md" },
634
+ makeContext(),
635
+ );
636
+
637
+ expect(result.isError).toBe(true);
638
+ expect(result.content).toContain('Invalid input for tool "test_tool"');
639
+ expect(result.content).toContain('Unknown parameter "path"');
640
+ });
641
+ });
642
+
643
+ // ---------------------------------------------------------------------------
644
+ // createSkillTool: subagent_spawn roles survive manifest validation
645
+ // ---------------------------------------------------------------------------
646
+
647
+ /** The subagent skill's real `subagent_spawn` manifest entry. */
648
+ function subagentSpawnEntry(): SkillToolEntry {
649
+ const manifest = JSON.parse(
650
+ readFileSync(
651
+ join(import.meta.dir, "../config/bundled-skills/subagent/TOOLS.json"),
652
+ "utf-8",
653
+ ),
654
+ ) as { tools: SkillToolEntry[] };
655
+ const entry = manifest.tools.find((t) => t.name === "subagent_spawn");
656
+ if (!entry) {
657
+ throw new Error("subagent_spawn is missing from the subagent TOOLS.json");
658
+ }
659
+ return entry;
660
+ }
661
+
662
+ /**
663
+ * The real `subagent_spawn` schema wired to a stub executor. Only the schema
664
+ * is under test here, so the manifest's own executor (which would really spawn)
665
+ * is swapped for the echo script.
666
+ */
667
+ function subagentSpawnTool() {
668
+ return createSkillTool(
669
+ { ...subagentSpawnEntry(), executor: "echo.ts" },
670
+ tempDir,
671
+ computeSkillVersionHash(tempDir),
672
+ BUNDLED,
673
+ );
674
+ }
675
+
676
+ /**
677
+ * Every `role` string {@link resolveSubagentRole} defines an answer for has to
678
+ * clear manifest validation first: `createSkillTool` validates against the
679
+ * manifest BEFORE the executor runs, so a constraint there that is narrower
680
+ * than the resolver silently deletes the resolver's behavior, and every test
681
+ * that calls `executeSubagentSpawn` directly still passes.
682
+ */
683
+ describe("createSkillTool: subagent_spawn role validation", () => {
684
+ const ROLE_CASES: ReadonlyArray<readonly [string, SubagentRole]> = [
685
+ ["researcher", "researcher"],
686
+ ["builder", "builder"],
687
+ ["advisor", "advisor"],
688
+ ["planner", "researcher"],
689
+ ["investigator", "researcher"],
690
+ ["coder", "builder"],
691
+ ["general", "builder"],
692
+ ["a skeptical security reviewer", "researcher"],
693
+ ["Researcher", "researcher"],
694
+ ];
695
+
696
+ test.each(ROLE_CASES)(
697
+ 'role "%s" passes manifest validation and resolves to %s',
698
+ async (role, expected) => {
699
+ const result = await subagentSpawnTool().execute(
700
+ { label: "Check", objective: "Do the thing", role },
701
+ makeContext(),
702
+ );
703
+
704
+ // Reached the executor at all, so validation let the role through.
705
+ expect(result.isError).toBe(false);
706
+ expect(JSON.parse(result.content).input.role).toBe(role);
707
+ // And the value the executor receives lands where the tool description
708
+ // and the docs say it does.
709
+ expect(resolveSubagentRole(role).role).toBe(expected);
710
+ },
711
+ );
712
+
713
+ test("role declares no enum, so the resolver stays reachable", () => {
714
+ const role = (
715
+ subagentSpawnEntry().input_schema as {
716
+ properties: Record<string, { type?: string; enum?: unknown }>;
717
+ }
718
+ ).properties.role;
719
+
720
+ expect(role.type).toBe("string");
721
+ // An enum here is enforced by `validateInputAgainstSchema` ahead of the
722
+ // executor, so any list short of "every string" rejects the legacy names
723
+ // and free-text personas the resolver exists to handle.
724
+ expect(role.enum).toBeUndefined();
725
+ });
726
+
727
+ test("output_contract keeps its enum, which the resolver has no fallback for", () => {
728
+ // The counterpart constraint: unlike `role`, an unrecognized contract has
729
+ // no defined behavior, so rejecting it at the boundary is correct.
730
+ const contract = (
731
+ subagentSpawnEntry().input_schema as {
732
+ properties: Record<string, { enum?: string[] }>;
733
+ }
734
+ ).properties.output_contract;
735
+
736
+ expect(contract.enum).toEqual(["report", "verdict", "artifact"]);
737
+ });
738
+ });
739
+
493
740
  // ---------------------------------------------------------------------------
494
741
  // createSkillTool — expectedSkillVersionHash plumbing
495
742
  // ---------------------------------------------------------------------------