@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
@@ -405,6 +405,7 @@ describe("inspectPlugin", () => {
405
405
  skills: ["first-skill", "second-skill"],
406
406
  hooks: ["init", "post-model-call"],
407
407
  tools: [],
408
+ schedules: [],
408
409
  });
409
410
  });
410
411
 
@@ -28,6 +28,7 @@ import {
28
28
  installPlugin,
29
29
  InvalidPluginNameError,
30
30
  PluginAlreadyInstalledError,
31
+ PluginInstallDeclinedError,
31
32
  PluginNotFoundError,
32
33
  PluginPostinstallError,
33
34
  PluginSourceUnavailableError,
@@ -362,6 +363,84 @@ describe("installPlugin — install lifecycle", () => {
362
363
  });
363
364
  });
364
365
 
366
+ describe("installPlugin - staged-install consent", () => {
367
+ let ws: string;
368
+ let pluginsDir: string;
369
+
370
+ beforeEach(() => {
371
+ ws = mkdtempSync(join(tmpdir(), "vellum-plugins-consent-"));
372
+ pluginsDir = join(ws, "plugins");
373
+ mkdirSync(pluginsDir, { recursive: true });
374
+ });
375
+
376
+ afterEach(() => {
377
+ rmSync(ws, { recursive: true, force: true });
378
+ });
379
+
380
+ const CLONE_TREE = {
381
+ "package.json": '{"name":"caveman"}',
382
+ "schedules/daily/config.json": '{"expression": "0 9 * * *"}',
383
+ "schedules/daily/index.md": "Do it.\n",
384
+ };
385
+
386
+ test("confirmStaged sees the staged tree before anything is finalized", async () => {
387
+ const fetch = makeContentsFetch({ tree: {}, manifest: CAVEMAN_MANIFEST });
388
+ const runGit = fakeGitRunner({ tree: CLONE_TREE, commit: CAVEMAN_SHA });
389
+ const observed: Array<{ name: string; stagingDir: string }> = [];
390
+
391
+ const result = await installPlugin(
392
+ { name: "caveman", ref: "main" },
393
+ {
394
+ fetch,
395
+ runGit,
396
+ workspacePluginsDir: pluginsDir,
397
+ confirmStaged: async (staged) => {
398
+ observed.push(staged);
399
+ // The staged tree is fully materialized, but nothing has been
400
+ // swapped into the served plugins dir yet.
401
+ expect(
402
+ existsSync(join(staged.stagingDir, "schedules/daily/index.md")),
403
+ ).toBe(true);
404
+ expect(existsSync(join(pluginsDir, "caveman"))).toBe(false);
405
+ return true;
406
+ },
407
+ },
408
+ );
409
+
410
+ expect(observed.length).toBe(1);
411
+ expect(observed[0]!.name).toBe("caveman");
412
+ expect(result.target).toBe(join(pluginsDir, "caveman"));
413
+ expect(existsSync(join(result.target, "schedules/daily/index.md"))).toBe(
414
+ true,
415
+ );
416
+ });
417
+
418
+ test("declining aborts cleanly: no target, staging removed", async () => {
419
+ const fetch = makeContentsFetch({ tree: {}, manifest: CAVEMAN_MANIFEST });
420
+ const runGit = fakeGitRunner({ tree: CLONE_TREE, commit: CAVEMAN_SHA });
421
+ const stagingDirs: string[] = [];
422
+
423
+ await expect(
424
+ installPlugin(
425
+ { name: "caveman", ref: "main" },
426
+ {
427
+ fetch,
428
+ runGit,
429
+ workspacePluginsDir: pluginsDir,
430
+ confirmStaged: async (staged) => {
431
+ stagingDirs.push(staged.stagingDir);
432
+ return false;
433
+ },
434
+ },
435
+ ),
436
+ ).rejects.toBeInstanceOf(PluginInstallDeclinedError);
437
+
438
+ expect(existsSync(join(pluginsDir, "caveman"))).toBe(false);
439
+ expect(stagingDirs.length).toBe(1);
440
+ expect(existsSync(stagingDirs[0]!)).toBe(false);
441
+ });
442
+ });
443
+
365
444
  describe("installPlugin — marketplace resolution", () => {
366
445
  let ws: string;
367
446
  let pluginsDir: string;
@@ -15,7 +15,10 @@ import { gzipSync } from "node:zlib";
15
15
  import { afterEach, beforeEach, describe, expect, test } from "bun:test";
16
16
 
17
17
  import type { FetchLike } from "../fetch-like.js";
18
- import { readInstallMeta } from "../install-from-github.js";
18
+ import {
19
+ PluginInstallDeclinedError,
20
+ readInstallMeta,
21
+ } from "../install-from-github.js";
19
22
  import {
20
23
  extractPluginTarball,
21
24
  installPluginFromPlatform,
@@ -306,6 +309,49 @@ describe("installPluginFromPlatform — integrity", () => {
306
309
  });
307
310
  });
308
311
 
312
+ // ─── Staged-install consent ──────────────────────────────────────────────────
313
+
314
+ describe("installPluginFromPlatform - staged-install consent", () => {
315
+ test("confirmStaged runs over the staged tree; declining leaves nothing behind", async () => {
316
+ const fetchFn = makeInstallFetch({
317
+ entries: [
318
+ { name: "plugin.json", content: '{"name":"reading-pal"}' },
319
+ {
320
+ name: "schedules/daily/config.json",
321
+ content: '{"expression": "0 9 * * *"}',
322
+ },
323
+ { name: "schedules/daily/index.md", content: "Do it.\n" },
324
+ ],
325
+ });
326
+ const stagingDirs: string[] = [];
327
+
328
+ await expect(
329
+ installPluginFromPlatform(
330
+ { name: "reading-pal" },
331
+ {
332
+ fetch: fetchFn,
333
+ platformBaseUrl: PLATFORM,
334
+ workspacePluginsDir: pluginsDir,
335
+ confirmStaged: async (staged) => {
336
+ stagingDirs.push(staged.stagingDir);
337
+ expect(
338
+ existsSync(
339
+ join(staged.stagingDir, "schedules", "daily", "index.md"),
340
+ ),
341
+ ).toBe(true);
342
+ expect(existsSync(join(pluginsDir, "reading-pal"))).toBe(false);
343
+ return false;
344
+ },
345
+ },
346
+ ),
347
+ ).rejects.toBeInstanceOf(PluginInstallDeclinedError);
348
+
349
+ expect(existsSync(join(pluginsDir, "reading-pal"))).toBe(false);
350
+ expect(stagingDirs.length).toBe(1);
351
+ expect(existsSync(stagingDirs[0]!)).toBe(false);
352
+ });
353
+ });
354
+
309
355
  // ─── Path traversal ──────────────────────────────────────────────────────────
310
356
 
311
357
  describe("extractPluginTarball — path traversal", () => {
@@ -2,9 +2,14 @@
2
2
  * Tests for {@link detectPluginSurfaces}.
3
3
  *
4
4
  * Surface detection is a pure walk of an installed plugin's on-disk tree, so
5
- * the fixtures materialize the `hooks/`, `tools/`, and `skills/` directory
6
- * conventions in a real temp dir and assert the derived listing matches what
7
- * the runtime loader / skills catalog would discover.
5
+ * the fixtures materialize the `hooks/`, `tools/`, `skills/`, and `schedules/`
6
+ * directory conventions in a real temp dir and assert the derived listing
7
+ * matches what the runtime loader / skills catalog would discover.
8
+ *
9
+ * The schedules walk only mirrors the daemon parser's structural refusals;
10
+ * it stays permissive beyond structure. In particular, `expression` validity
11
+ * is not checked CLI-side, so a schedule whose expression the daemon rejects
12
+ * is still listed.
8
13
  */
9
14
 
10
15
  import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from "node:fs";
@@ -24,11 +29,16 @@ afterEach(() => {
24
29
  rmSync(pluginDir, { recursive: true, force: true });
25
30
  });
26
31
 
27
- /** Create `<pluginDir>/<rel>` with empty contents, making parents as needed. */
28
- function touch(rel: string): void {
32
+ /** Create `<pluginDir>/<rel>` with the given contents, making parents as needed. */
33
+ function touch(rel: string, content = ""): void {
29
34
  const path = join(pluginDir, rel);
30
35
  mkdirSync(join(path, ".."), { recursive: true });
31
- writeFileSync(path, "");
36
+ writeFileSync(path, content);
37
+ }
38
+
39
+ /** A prompt body carrying YAML frontmatter, which `index.md` must not have. */
40
+ function withFrontmatter(expression: string): string {
41
+ return `---\nexpression: "${expression}"\n---\nDo the thing.\n`;
32
42
  }
33
43
 
34
44
  describe("detectPluginSurfaces", () => {
@@ -106,6 +116,75 @@ describe("detectPluginSurfaces", () => {
106
116
  const surfaces = detectPluginSurfaces(pluginDir);
107
117
 
108
118
  // THEN every surface type is empty
109
- expect(surfaces).toEqual({ skills: [], hooks: [], tools: [] });
119
+ expect(surfaces).toEqual({
120
+ skills: [],
121
+ hooks: [],
122
+ tools: [],
123
+ schedules: [],
124
+ });
125
+ });
126
+
127
+ test("lists declared schedules with cadence and mode for both entrypoints", () => {
128
+ // GIVEN two declaration directories, one per entrypoint kind
129
+ touch(
130
+ "schedules/cleanup/config.json",
131
+ '{"expression": "0 0 * * 0", "timezone": "UTC"}',
132
+ );
133
+ touch("schedules/cleanup/index.sh", "#!/bin/sh\necho hi\n");
134
+ touch("schedules/digest/config.json", '{"expression": "0 8 * * 1"}');
135
+ touch("schedules/digest/index.md", "Summarize the week.\n");
136
+
137
+ // WHEN its surfaces are detected
138
+ const surfaces = detectPluginSurfaces(pluginDir);
139
+
140
+ // THEN each declaration reports its raw expression and entrypoint mode
141
+ expect(surfaces.schedules).toEqual([
142
+ { name: "cleanup", cadence: "0 0 * * 0", mode: "script" },
143
+ { name: "digest", cadence: "0 8 * * 1", mode: "execute" },
144
+ ]);
145
+ });
146
+
147
+ test("skips a markdown file sitting directly under schedules/", () => {
148
+ // GIVEN a flat declaration file, which is not a declaration form
149
+ touch("schedules/dupe.md", withFrontmatter("0 9 * * *"));
150
+ touch("schedules/keeper/config.json", '{"expression": "30 7 * * *"}');
151
+ touch("schedules/keeper/index.md", "body\n");
152
+
153
+ // WHEN its surfaces are detected
154
+ const surfaces = detectPluginSurfaces(pluginDir);
155
+
156
+ // THEN only the declaration directory is listed
157
+ expect(surfaces.schedules).toEqual([
158
+ { name: "keeper", cadence: "30 7 * * *", mode: "execute" },
159
+ ]);
160
+ });
161
+
162
+ test("skips structurally malformed schedule declarations (frontmatter, entrypoint, config, body)", () => {
163
+ // GIVEN a directory with both entrypoints
164
+ touch("schedules/two-entries/config.json", '{"expression": "0 9 * * *"}');
165
+ touch("schedules/two-entries/index.md", "body\n");
166
+ touch("schedules/two-entries/index.sh", "#!/bin/sh\n");
167
+ // AND a directory with an unsupported entrypoint
168
+ touch("schedules/bad-entry/config.json", '{"expression": "0 9 * * *"}');
169
+ touch("schedules/bad-entry/index.py", "print()\n");
170
+ // AND a directory whose index.md carries frontmatter (config belongs in
171
+ // config.json)
172
+ touch(
173
+ "schedules/md-frontmatter/config.json",
174
+ '{"expression": "0 9 * * *"}',
175
+ );
176
+ touch("schedules/md-frontmatter/index.md", withFrontmatter("0 9 * * *"));
177
+ // AND a directory whose index.md prompt body is empty
178
+ touch("schedules/md-empty/config.json", '{"expression": "0 9 * * *"}');
179
+ touch("schedules/md-empty/index.md", "\n \n");
180
+ // AND a directory missing its config.json
181
+ touch("schedules/no-config/index.md", "body\n");
182
+ // AND a directory whose config declares no expression
183
+ touch("schedules/no-expression/config.json", '{"timezone": "UTC"}');
184
+ touch("schedules/no-expression/index.md", "body\n");
185
+
186
+ // WHEN its surfaces are detected
187
+ // THEN none of them are listed
188
+ expect(detectPluginSurfaces(pluginDir).schedules).toEqual([]);
110
189
  });
111
190
  });
@@ -24,6 +24,7 @@ import { afterEach, beforeEach, describe, expect, test } from "bun:test";
24
24
  import type { FetchLike } from "../fetch-like.js";
25
25
  import {
26
26
  type GitRunner,
27
+ PluginInstallDeclinedError,
27
28
  PluginNotFoundError,
28
29
  PluginSourceUnavailableError,
29
30
  } from "../install-from-github.js";
@@ -1033,3 +1034,124 @@ describe("upgradePlugin --strategy", () => {
1033
1034
  expect(installedFile("level-up", "conflict.txt")).toBe("ours\n");
1034
1035
  });
1035
1036
  });
1037
+
1038
+ describe("upgradePlugin confirmStaged consent gate", () => {
1039
+ test("overwrite: declining aborts and preserves the existing install", async () => {
1040
+ // GIVEN an installed copy pinned to SHA_A with a newer marketplace pin
1041
+ installCopy(pluginsDir, "level-up", { commit: SHA_A });
1042
+ const fetch = makeFetch({ manifest: manifestWith("level-up", SHA_B) });
1043
+ const runGit = fakeGitRunner(SHA_B);
1044
+ const seen: Array<{ name: string; staged: boolean }> = [];
1045
+
1046
+ // WHEN the consent gate declines the staged upgrade
1047
+ await expect(
1048
+ upgradePlugin(
1049
+ { name: "level-up" },
1050
+ {
1051
+ fetch,
1052
+ runGit,
1053
+ workspacePluginsDir: pluginsDir,
1054
+ confirmStaged: async ({ name, stagingDir }) => {
1055
+ seen.push({
1056
+ name,
1057
+ staged: existsSync(join(stagingDir, "package.json")),
1058
+ });
1059
+ return false;
1060
+ },
1061
+ },
1062
+ ),
1063
+ ).rejects.toBeInstanceOf(PluginInstallDeclinedError);
1064
+
1065
+ // THEN the gate saw the fully staged tree exactly once
1066
+ expect(seen).toEqual([{ name: "level-up", staged: true }]);
1067
+ // AND the live install still carries the old pin
1068
+ expect(sidecarCommit(pluginsDir, "level-up")).toBe(SHA_A);
1069
+ });
1070
+
1071
+ test("overwrite: an accepting gate lets the upgrade proceed", async () => {
1072
+ installCopy(pluginsDir, "level-up", { commit: SHA_A });
1073
+ const fetch = makeFetch({ manifest: manifestWith("level-up", SHA_B) });
1074
+ const runGit = fakeGitRunner(SHA_B);
1075
+
1076
+ const result = await upgradePlugin(
1077
+ { name: "level-up" },
1078
+ {
1079
+ fetch,
1080
+ runGit,
1081
+ workspacePluginsDir: pluginsDir,
1082
+ confirmStaged: async () => true,
1083
+ },
1084
+ );
1085
+
1086
+ expect(result.outcome).toBe("upgraded");
1087
+ expect(sidecarCommit(pluginsDir, "level-up")).toBe(SHA_B);
1088
+ });
1089
+
1090
+ test("merge: the gate sees the merged tree; declining keeps the local install", async () => {
1091
+ const PKG = '{"name":"level-up"}\n';
1092
+ const base: Tree = { "package.json": PKG, "note.txt": "base\n" };
1093
+ const ours: Tree = {
1094
+ "package.json": PKG,
1095
+ "note.txt": "base\n",
1096
+ "local.txt": "mine\n",
1097
+ };
1098
+ const theirs: Tree = { "package.json": PKG, "note.txt": "upstream\n" };
1099
+ installMergeCopy("level-up", ours, SHA_A, base);
1100
+ const fetch = makeFetch({ manifest: manifestWith("level-up", SHA_B) });
1101
+ const runGit = treeGitRunner({ [SHA_A]: base, [SHA_B]: theirs });
1102
+ let sawMerged = false;
1103
+
1104
+ await expect(
1105
+ upgradePlugin(
1106
+ { name: "level-up", strategy: "ours" },
1107
+ {
1108
+ fetch,
1109
+ runGit,
1110
+ workspacePluginsDir: pluginsDir,
1111
+ confirmStaged: async ({ stagingDir }) => {
1112
+ // The staged tree is the merged result: the upstream edit plus
1113
+ // the surviving local addition.
1114
+ sawMerged =
1115
+ readFileSync(join(stagingDir, "note.txt"), "utf-8") ===
1116
+ "upstream\n" && existsSync(join(stagingDir, "local.txt"));
1117
+ return false;
1118
+ },
1119
+ },
1120
+ ),
1121
+ ).rejects.toBeInstanceOf(PluginInstallDeclinedError);
1122
+
1123
+ expect(sawMerged).toBe(true);
1124
+ // The live install is untouched by the declined merge.
1125
+ expect(installedFile("level-up", "note.txt")).toBe("base\n");
1126
+ expect(sidecarCommit(pluginsDir, "level-up")).toBe(SHA_A);
1127
+ });
1128
+
1129
+ test("dry runs and no-ops never invoke the gate", async () => {
1130
+ installCopy(pluginsDir, "level-up", { commit: SHA_A });
1131
+ const gate = async () => {
1132
+ throw new Error("confirmStaged must not run without staging");
1133
+ };
1134
+
1135
+ const dry = await upgradePlugin(
1136
+ { name: "level-up", dryRun: true },
1137
+ {
1138
+ fetch: makeFetch({ manifest: manifestWith("level-up", SHA_B) }),
1139
+ runGit: unusedGitRunner,
1140
+ workspacePluginsDir: pluginsDir,
1141
+ confirmStaged: gate,
1142
+ },
1143
+ );
1144
+ expect(dry.outcome).toBe("would-upgrade");
1145
+
1146
+ const noop = await upgradePlugin(
1147
+ { name: "level-up" },
1148
+ {
1149
+ fetch: makeFetch({ manifest: manifestWith("level-up", SHA_A) }),
1150
+ runGit: unusedGitRunner,
1151
+ workspacePluginsDir: pluginsDir,
1152
+ confirmStaged: gate,
1153
+ },
1154
+ );
1155
+ expect(noop.outcome).toBe("already-up-to-date");
1156
+ });
1157
+ });
@@ -154,6 +154,20 @@ export interface InstallPluginOptions {
154
154
  readonly trustedSource?: PluginFetchSource;
155
155
  }
156
156
 
157
+ /**
158
+ * Consent gate invoked after the plugin tree is fully staged and before it is
159
+ * finalized (dependencies installed, provenance recorded, swapped live). The
160
+ * caller inspects the staged tree to see what the plugin declares (e.g.
161
+ * schedules) and asks the user. Returning `false` aborts the install: the
162
+ * staging directory is removed, nothing is swapped, and
163
+ * {@link PluginInstallDeclinedError} is thrown.
164
+ */
165
+ export type ConfirmStagedInstall = (staged: {
166
+ readonly name: string;
167
+ /** Absolute path of the fully materialized staging directory. */
168
+ readonly stagingDir: string;
169
+ }) => Promise<boolean>;
170
+
157
171
  /** Dependencies injected by the caller. */
158
172
  export interface InstallPluginDeps {
159
173
  /** HTTP client. Production callers pass `globalThis.fetch.bind(globalThis)`. */
@@ -168,6 +182,8 @@ export interface InstallPluginDeps {
168
182
  readonly runInstallDeps?: DependencyInstaller;
169
183
  /** Forwarded to {@link finalizeStagedInstall}; see {@link FinalizeStagedInstallParams.beforeSwap}. */
170
184
  readonly beforeSwap?: () => Promise<void>;
185
+ /** Consent gate between staging and finalize; see {@link ConfirmStagedInstall}. */
186
+ readonly confirmStaged?: ConfirmStagedInstall;
171
187
  }
172
188
 
173
189
  /** Successful install result. */
@@ -214,6 +230,19 @@ export class PluginPostinstallError extends Error {
214
230
  }
215
231
  }
216
232
 
233
+ /**
234
+ * The caller's {@link ConfirmStagedInstall} gate declined the staged install.
235
+ * Nothing was installed and the staging directory was removed. The caller that
236
+ * supplied the gate owns the user-facing messaging, so this error signals the
237
+ * outcome rather than something to report as a failure.
238
+ */
239
+ export class PluginInstallDeclinedError extends Error {
240
+ constructor(pluginName: string) {
241
+ super(`Install of "${pluginName}" was declined.`);
242
+ this.name = "PluginInstallDeclinedError";
243
+ }
244
+ }
245
+
217
246
  /** A plugin with the same name is already installed and `--force` was not passed. */
218
247
  export class PluginAlreadyInstalledError extends Error {
219
248
  constructor(
@@ -503,6 +532,8 @@ export async function installPlugin(
503
532
  throw new PluginNotFoundError(name, ref, sourceLabel(effectiveSource));
504
533
  }
505
534
 
535
+ await confirmStagedOrAbort(name, stagingDir, deps.confirmStaged);
536
+
506
537
  await finalizeStagedInstall(stagingDir, {
507
538
  name,
508
539
  source: effectiveSource,
@@ -517,6 +548,36 @@ export async function installPlugin(
517
548
  return { name, target, fileCount, ref, commit, committedAt };
518
549
  }
519
550
 
551
+ /**
552
+ * Run the caller's {@link ConfirmStagedInstall} gate over the staged tree. A
553
+ * decline (or a gate that throws) removes the staging directory so no partial
554
+ * install is left behind; a decline then surfaces as
555
+ * {@link PluginInstallDeclinedError}. A no-op when no gate was supplied.
556
+ *
557
+ * Shared by {@link installPlugin} and the platform-endpoint install so both
558
+ * abort a declined install the same way.
559
+ */
560
+ export async function confirmStagedOrAbort(
561
+ name: string,
562
+ stagingDir: string,
563
+ confirmStaged: ConfirmStagedInstall | undefined,
564
+ ): Promise<void> {
565
+ if (!confirmStaged) {
566
+ return;
567
+ }
568
+ let confirmed = false;
569
+ try {
570
+ confirmed = await confirmStaged({ name, stagingDir });
571
+ } finally {
572
+ if (!confirmed) {
573
+ rmSync(stagingDir, { recursive: true, force: true });
574
+ }
575
+ }
576
+ if (!confirmed) {
577
+ throw new PluginInstallDeclinedError(name);
578
+ }
579
+ }
580
+
520
581
  /** Inputs for {@link finalizeStagedInstall}. */
521
582
  export interface FinalizeStagedInstallParams {
522
583
  readonly name: string;
@@ -33,6 +33,8 @@ import { getWorkspacePluginsDir } from "../../util/platform.js";
33
33
  import { APP_VERSION } from "../../version.js";
34
34
  import type { FetchLike } from "./fetch-like.js";
35
35
  import {
36
+ type ConfirmStagedInstall,
37
+ confirmStagedOrAbort,
36
38
  finalizeStagedInstall,
37
39
  type InstallPluginResult,
38
40
  PluginAlreadyInstalledError,
@@ -123,6 +125,8 @@ export interface InstallFromPlatformDeps {
123
125
  readonly maxAttempts?: number;
124
126
  /** Sleep between retries. Injected so tests don't wait real time. */
125
127
  readonly sleep?: (ms: number) => Promise<void>;
128
+ /** Consent gate between staging and finalize; see {@link ConfirmStagedInstall}. */
129
+ readonly confirmStaged?: ConfirmStagedInstall;
126
130
  }
127
131
 
128
132
  /** Total attempts (initial + retries) for a transient upstream status. */
@@ -194,6 +198,8 @@ export async function installPluginFromPlatform(
194
198
  throw new PluginNotFoundError(name, meta.ref ?? "", meta.repo ?? name);
195
199
  }
196
200
 
201
+ await confirmStagedOrAbort(name, stagingDir, deps.confirmStaged);
202
+
197
203
  // Record provenance for future update/version UX. The endpoint re-roots the
198
204
  // tarball to the plugin root, so the recorded ref is the pinned commit and
199
205
  // the ETag documents exactly which bytes were verified.
@@ -223,7 +229,7 @@ export async function installPluginFromPlatform(
223
229
  */
224
230
  export async function installPluginViaPlatform(
225
231
  opts: { name: string; force?: boolean; conversationId?: string },
226
- deps: { fetch: FetchLike },
232
+ deps: { fetch: FetchLike; confirmStaged?: ConfirmStagedInstall },
227
233
  ): Promise<InstallPluginResult> {
228
234
  const platformBaseUrl = getPlatformBaseUrl();
229
235
  const apiKey = await resolveAssistantApiKey();
@@ -237,7 +243,12 @@ export async function installPluginViaPlatform(
237
243
  ...(opts.conversationId ? { conversationId: opts.conversationId } : {}),
238
244
  assistantVersion: APP_VERSION,
239
245
  },
240
- { fetch: deps.fetch, platformBaseUrl, ...(apiKey ? { apiKey } : {}) },
246
+ {
247
+ fetch: deps.fetch,
248
+ platformBaseUrl,
249
+ ...(apiKey ? { apiKey } : {}),
250
+ ...(deps.confirmStaged ? { confirmStaged: deps.confirmStaged } : {}),
251
+ },
241
252
  );
242
253
  }
243
254