@vellumai/assistant 0.11.2 → 0.11.3-dev.202608080427.92f668a

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 (826) hide show
  1. package/ARCHITECTURE.md +69 -9
  2. package/docs/flux-turn-detection-spike.md +243 -0
  3. package/docs/stt-provider-onboarding.md +3 -1
  4. package/docs/trusted-contact-access.md +9 -5
  5. package/docs/vellum-doctor.md +195 -0
  6. package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/channels.ts +50 -0
  7. package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/ingress.ts +10 -0
  8. package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/remote-web-pairing.ts +6 -0
  9. package/node_modules/@vellumai/ces-client/src/index.ts +1 -0
  10. package/node_modules/@vellumai/ces-client/src/rpc-client.ts +19 -2
  11. package/node_modules/@vellumai/environments/package.json +2 -1
  12. package/node_modules/@vellumai/environments/src/__tests__/cloud-assistant-hub-url.test.ts +38 -0
  13. package/node_modules/@vellumai/environments/src/__tests__/install-layout.test.ts +98 -0
  14. package/node_modules/@vellumai/environments/src/__tests__/package-boundary.test.ts +5 -5
  15. package/node_modules/@vellumai/environments/src/index.ts +17 -6
  16. package/node_modules/@vellumai/environments/src/install-layout.ts +49 -0
  17. package/node_modules/@vellumai/environments/src/seeds.ts +29 -0
  18. package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/channels.ts +50 -0
  19. package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/ingress.ts +10 -0
  20. package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/remote-web-pairing.ts +6 -0
  21. package/node_modules/@vellumai/gateway-client/src/index.ts +4 -0
  22. package/node_modules/@vellumai/gateway-client/src/verification-session-contract.ts +91 -6
  23. package/node_modules/@vellumai/ipc-server-utils/src/endpoint.test.ts +36 -0
  24. package/node_modules/@vellumai/ipc-server-utils/src/endpoint.ts +142 -0
  25. package/node_modules/@vellumai/ipc-server-utils/src/index.ts +2 -0
  26. package/node_modules/@vellumai/ipc-server-utils/src/listen-options.ts +3 -0
  27. package/node_modules/@vellumai/ipc-server-utils/src/socket-watchdog.test.ts +15 -1
  28. package/node_modules/@vellumai/ipc-server-utils/src/socket-watchdog.ts +17 -2
  29. package/node_modules/@vellumai/service-contracts/src/channels.ts +50 -0
  30. package/node_modules/@vellumai/service-contracts/src/ingress.ts +10 -0
  31. package/node_modules/@vellumai/service-contracts/src/remote-web-pairing.ts +6 -0
  32. package/node_modules/@vellumai/slack-text/src/index.ts +16 -2
  33. package/openapi.yaml +619 -47
  34. package/package.json +1 -1
  35. package/scripts/voice-ttft-spike.ts +3 -3
  36. package/src/__tests__/agent-image-optimize.test.ts +4 -4
  37. package/src/__tests__/agent-loop-mutable-latest-user-message.test.ts +113 -66
  38. package/src/__tests__/agent-loop-override-profile.test.ts +38 -48
  39. package/src/__tests__/agent-loop-subagent-billing-headers.test.ts +166 -0
  40. package/src/__tests__/agent-wake-disk-pressure-callsite.test.ts +1 -0
  41. package/src/__tests__/agent-wake-override-profile.test.ts +1 -0
  42. package/src/__tests__/answered-question-persistence.test.ts +240 -0
  43. package/src/__tests__/anthropic-provider.test.ts +165 -30
  44. package/src/__tests__/app-control-flow.test.ts +9 -8
  45. package/src/__tests__/assistant-event-hub.test.ts +48 -0
  46. package/src/__tests__/attachments-store-heic-normalize.test.ts +16 -12
  47. package/src/__tests__/attachments-store.test.ts +130 -68
  48. package/src/__tests__/attachments.test.ts +36 -36
  49. package/src/__tests__/background-workers-disk-pressure.test.ts +1 -1
  50. package/src/__tests__/call-controller.test.ts +120 -0
  51. package/src/__tests__/call-start-guardian-guard.test.ts +18 -4
  52. package/src/__tests__/canned-reply-release.test.ts +86 -0
  53. package/src/__tests__/channel-availability-routes.test.ts +14 -1
  54. package/src/__tests__/channel-guardian.test.ts +59 -29
  55. package/src/__tests__/channel-readiness-telegram-remote.test.ts +154 -0
  56. package/src/__tests__/channel-setup-panel-ack.test.ts +6 -5
  57. package/src/__tests__/client-os-metadata-persistence.test.ts +103 -4
  58. package/src/__tests__/compactor-image-manifest-trust.test.ts +1 -1
  59. package/src/__tests__/config-loader-backfill.test.ts +19 -0
  60. package/src/__tests__/config-schema.test.ts +150 -7
  61. package/src/__tests__/consult-deadline.test.ts +65 -0
  62. package/src/__tests__/conversation-agent-loop.test.ts +438 -2
  63. package/src/__tests__/conversation-disk-view-integration.test.ts +5 -1
  64. package/src/__tests__/conversation-disk-view.test.ts +4 -4
  65. package/src/__tests__/conversation-error.test.ts +22 -3
  66. package/src/__tests__/conversation-fork-crud.test.ts +5 -1
  67. package/src/__tests__/conversation-fork-referential.test.ts +352 -0
  68. package/src/__tests__/conversation-fork-retrospective.test.ts +5 -1
  69. package/src/__tests__/conversation-group-tools.test.ts +32 -2
  70. package/src/__tests__/conversation-inference-profile-route.test.ts +27 -1
  71. package/src/__tests__/conversation-initial-prompt.test.ts +51 -22
  72. package/src/__tests__/conversation-key-store-bootstrap-cleanup.test.ts +119 -0
  73. package/src/__tests__/conversation-key-store-origin.test.ts +105 -0
  74. package/src/__tests__/conversation-list-recency-ordering.test.ts +104 -0
  75. package/src/__tests__/conversation-origin-at-creation.test.ts +153 -0
  76. package/src/__tests__/conversation-origin-channel-filter.test.ts +95 -0
  77. package/src/__tests__/conversation-placement-promotion.test.ts +172 -0
  78. package/src/__tests__/conversation-process-app-control-preactivation.test.ts +123 -0
  79. package/src/__tests__/conversation-queue.test.ts +433 -1
  80. package/src/__tests__/conversation-retry-route.test.ts +3 -1
  81. package/src/__tests__/conversation-routes-hidden-queue.test.ts +64 -1
  82. package/src/__tests__/conversation-store-ephemeral.test.ts +4 -0
  83. package/src/__tests__/conversation-store.test.ts +26 -10
  84. package/src/__tests__/conversation-summarize-route.test.ts +1 -1
  85. package/src/__tests__/conversation-surfaces-action-delivery.test.ts +15 -6
  86. package/src/__tests__/conversation-surfaces-activation-emit.test.ts +6 -5
  87. package/src/__tests__/conversation-surfaces-app-control.test.ts +12 -11
  88. package/src/__tests__/conversation-surfaces-app-open.test.ts +11 -5
  89. package/src/__tests__/conversation-surfaces-data-persist.test.ts +5 -4
  90. package/src/__tests__/conversation-surfaces-history-restored-completion.test.ts +967 -0
  91. package/src/__tests__/conversation-surfaces-persisted-info.test.ts +312 -0
  92. package/src/__tests__/conversation-surfaces-queued-emit.test.ts +6 -5
  93. package/src/__tests__/conversation-surfaces-standalone-payloads.test.ts +12 -8
  94. package/src/__tests__/conversation-surfaces-standalone.test.ts +21 -11
  95. package/src/__tests__/conversation-surfaces-state-update.test.ts +7 -11
  96. package/src/__tests__/conversation-surfaces-table-action.test.ts +7 -6
  97. package/src/__tests__/conversation-surfaces-task-progress.test.ts +11 -6
  98. package/src/__tests__/conversation-tool-setup-app-refresh.test.ts +9 -6
  99. package/src/__tests__/conversation-tool-setup-attribution.test.ts +70 -7
  100. package/src/__tests__/conversation-wait-for-idle.test.ts +6 -11
  101. package/src/__tests__/credential-prompt-route.test.ts +54 -7
  102. package/src/__tests__/credential-security-invariants.test.ts +1 -0
  103. package/src/__tests__/cu-unified-flow.test.ts +89 -28
  104. package/src/__tests__/custom-group-listing-visibility.test.ts +1 -1
  105. package/src/__tests__/db-schedule-syntax-migration.test.ts +4 -1
  106. package/src/__tests__/discord-access-request-privacy.test.ts +366 -0
  107. package/src/__tests__/discord-requester-notice-privacy.test.ts +87 -0
  108. package/src/__tests__/disk-pressure-guard.test.ts +55 -55
  109. package/src/__tests__/disk-pressure-routes.test.ts +10 -10
  110. package/src/__tests__/disk-pressure-tools.test.ts +2 -2
  111. package/src/__tests__/disk-usage.test.ts +35 -20
  112. package/src/__tests__/document-sync-tags.test.ts +379 -0
  113. package/src/__tests__/document-update-default-surface.test.ts +28 -0
  114. package/src/__tests__/dynamic-skill-background-guard.test.ts +2 -2
  115. package/src/__tests__/emit-signal-routing-intent.test.ts +3 -4
  116. package/src/__tests__/ensure-conversation-exists.test.ts +8 -5
  117. package/src/__tests__/events-dev-bypass-actor.test.ts +294 -1
  118. package/src/__tests__/evict-conversations-for-reload.test.ts +90 -0
  119. package/src/__tests__/file-list-tool.test.ts +12 -10
  120. package/src/__tests__/file-ops-service.test.ts +151 -44
  121. package/src/__tests__/file-read-container-boundary.test.ts +137 -0
  122. package/src/__tests__/file-read-tool.test.ts +21 -9
  123. package/src/__tests__/file-url-path-guard.test.ts +83 -0
  124. package/src/__tests__/filesystem-tools.test.ts +82 -66
  125. package/src/__tests__/guardian-outbound-http.test.ts +8 -8
  126. package/src/__tests__/helpers/gateway-verification-sessions-stub.ts +14 -1
  127. package/src/__tests__/helpers/hub-clients.ts +42 -0
  128. package/src/__tests__/helpers/mock-conversation.ts +46 -0
  129. package/src/__tests__/helpers/verification-sessions-ipc-sim.ts +61 -57
  130. package/src/__tests__/history-repair.test.ts +112 -12
  131. package/src/__tests__/host-bash-proxy.test.ts +10 -5
  132. package/src/__tests__/host-bash-routes.test.ts +67 -0
  133. package/src/__tests__/host-browser-proxy.test.ts +2 -2
  134. package/src/__tests__/host-cu-proxy.test.ts +3 -7
  135. package/src/__tests__/host-file-proxy.test.ts +3 -7
  136. package/src/__tests__/host-file-read-tool.test.ts +5 -1
  137. package/src/__tests__/host-transfer-proxy-targeted.test.ts +6 -4
  138. package/src/__tests__/http-conversation-lineage.test.ts +2 -5
  139. package/src/__tests__/http-user-message-parity.test.ts +44 -0
  140. package/src/__tests__/identity-routes.test.ts +13 -13
  141. package/src/__tests__/image-conversion.test.ts +29 -26
  142. package/src/__tests__/image-source-path-reinject.test.ts +2 -2
  143. package/src/__tests__/inference-profile-session-handler.test.ts +95 -0
  144. package/src/__tests__/inference-profile-session-ipc.test.ts +25 -0
  145. package/src/__tests__/introduction-card-resolver.test.ts +6 -1
  146. package/src/__tests__/list-messages-attachments.test.ts +56 -32
  147. package/src/__tests__/list-messages-background-tool-completion.test.ts +4 -4
  148. package/src/__tests__/list-messages-client-message-id.test.ts +2 -2
  149. package/src/__tests__/list-messages-hidden-metadata.test.ts +16 -16
  150. package/src/__tests__/list-messages-page-latest.test.ts +61 -61
  151. package/src/__tests__/list-messages-provider-error.test.ts +6 -6
  152. package/src/__tests__/list-messages-queued.test.ts +49 -6
  153. package/src/__tests__/list-messages-system-card.test.ts +4 -4
  154. package/src/__tests__/list-messages-tool-merge.test.ts +9 -9
  155. package/src/__tests__/llm-resolver-override-or-default.test.ts +141 -0
  156. package/src/__tests__/llm-usage-store.test.ts +55 -0
  157. package/src/__tests__/manual-token-reconciliation.test.ts +45 -0
  158. package/src/__tests__/mcp-list-plugin-servers.test.ts +250 -0
  159. package/src/__tests__/mcp-tool-annotations-risk.test.ts +133 -0
  160. package/src/__tests__/media-stream-output.test.ts +175 -0
  161. package/src/__tests__/media-stream-server-integration.test.ts +36 -0
  162. package/src/__tests__/media-stream-stt-session.test.ts +67 -0
  163. package/src/__tests__/migration-export-http.test.ts +1 -1
  164. package/src/__tests__/migration-import-commit-http.test.ts +1 -1
  165. package/src/__tests__/migration-import-preflight-http.test.ts +1 -1
  166. package/src/__tests__/migration-validate-http.test.ts +1 -1
  167. package/src/__tests__/mtime-cache.test.ts +65 -3
  168. package/src/__tests__/notification-platform-adapter.test.ts +104 -0
  169. package/src/__tests__/notification-schedule-notify-dedup.test.ts +1 -1
  170. package/src/__tests__/oauth-provider-profiles.test.ts +4 -2
  171. package/src/__tests__/oauth-provider-seed-logos.test.ts +65 -0
  172. package/src/__tests__/oauth-provider-serializer.test.ts +17 -0
  173. package/src/__tests__/oauth-providers-routes.test.ts +1 -0
  174. package/src/__tests__/openai-responses-prompt-cache.test.ts +5 -2
  175. package/src/__tests__/path-policy.test.ts +164 -1
  176. package/src/__tests__/persist-media-references.test.ts +2 -2
  177. package/src/__tests__/persist-unsendable-image-downscale.test.ts +5 -5
  178. package/src/__tests__/persist-unsendable-image.test.ts +23 -23
  179. package/src/__tests__/persona-resolver.test.ts +154 -0
  180. package/src/__tests__/plugin-api-webhook-url.test.ts +170 -0
  181. package/src/__tests__/plugin-import-boundary-guard.test.ts +6 -0
  182. package/src/__tests__/processing-flag-persist-failure.test.ts +15 -10
  183. package/src/__tests__/profile-availability-incomplete.test.ts +113 -0
  184. package/src/__tests__/profiler-routes.test.ts +13 -13
  185. package/src/__tests__/prompt-cache-cross-turn-stability.test.ts +593 -0
  186. package/src/__tests__/proxy-approval-callback.test.ts +3 -3
  187. package/src/__tests__/queued-message-cancel-and-steer.test.ts +189 -0
  188. package/src/__tests__/queued-message-steer-actor-scoping.test.ts +164 -0
  189. package/src/__tests__/resolve-trust-class.test.ts +20 -0
  190. package/src/__tests__/run-conversation-turn-persistence.test.ts +1 -1
  191. package/src/__tests__/run-due-schedules.test.ts +174 -0
  192. package/src/__tests__/runtime-attachment-metadata.test.ts +15 -7
  193. package/src/__tests__/scaffold-managed-skill-tool.test.ts +166 -0
  194. package/src/__tests__/schedule-routes.test.ts +730 -8
  195. package/src/__tests__/schedule-store.test.ts +540 -0
  196. package/src/__tests__/schedule-tools.test.ts +103 -0
  197. package/src/__tests__/scheduler-wake.test.ts +5 -0
  198. package/src/__tests__/secret-routes-platform-proxy.test.ts +29 -1
  199. package/src/__tests__/secure-keys.test.ts +91 -0
  200. package/src/__tests__/server-history-render.test.ts +97 -7
  201. package/src/__tests__/size-guard.test.ts +8 -8
  202. package/src/__tests__/skill-docs-sync-guard.test.ts +3 -2
  203. package/src/__tests__/skill-tool-factory.test.ts +247 -0
  204. package/src/__tests__/skills-install-staging.test.ts +15 -6
  205. package/src/__tests__/skills.test.ts +32 -0
  206. package/src/__tests__/slack-edit-ordering-characterization.test.ts +103 -0
  207. package/src/__tests__/slack-inbound-verification.test.ts +4 -1
  208. package/src/__tests__/slack-mention-binding-invariance.test.ts +103 -0
  209. package/src/__tests__/slack-mention-provider-leak-audit.test.ts +135 -0
  210. package/src/__tests__/starter-task-flow.test.ts +5 -4
  211. package/src/__tests__/steer-on-enqueue-question.test.ts +3 -2
  212. package/src/__tests__/stt-language-catalog-parity.test.ts +38 -0
  213. package/src/__tests__/subagent-allowlist-validation.test.ts +26 -4
  214. package/src/__tests__/subagent-cron-run-attribution.test.ts +301 -0
  215. package/src/__tests__/subagent-disposal.test.ts +10 -0
  216. package/src/__tests__/subagent-fork-notifications.test.ts +10 -0
  217. package/src/__tests__/subagent-fork-prompt-role.test.ts +85 -11
  218. package/src/__tests__/subagent-fork-spawn.test.ts +15 -7
  219. package/src/__tests__/subagent-manager-notify.test.ts +148 -0
  220. package/src/__tests__/subagent-notify-parent.test.ts +1 -1
  221. package/src/__tests__/subagent-persistence.test.ts +141 -0
  222. package/src/__tests__/subagent-role-registry.test.ts +172 -63
  223. package/src/__tests__/subagent-role-resolution.test.ts +88 -0
  224. package/src/__tests__/subagent-spawn-and-await.test.ts +48 -0
  225. package/src/__tests__/subagent-spawn-tool-fork.test.ts +111 -10
  226. package/src/__tests__/subagent-terminal-message.test.ts +88 -1
  227. package/src/__tests__/subagent-tool-gate-mode.test.ts +225 -10
  228. package/src/__tests__/subagent-tools.test.ts +1901 -108
  229. package/src/__tests__/surface-completion-compaction-boundary.test.ts +124 -0
  230. package/src/__tests__/surface-completion-in-flight-snapshot.test.ts +298 -0
  231. package/src/__tests__/system-prompt.test.ts +82 -7
  232. package/src/__tests__/tool-grant-request-escalation.test.ts +40 -1
  233. package/src/__tests__/tool-result-spool.test.ts +74 -18
  234. package/src/__tests__/tool-side-effects-documents.test.ts +174 -0
  235. package/src/__tests__/tool-side-effects-hook-failures.test.ts +129 -0
  236. package/src/__tests__/tools-audio-read.test.ts +10 -10
  237. package/src/__tests__/tools-get-route.test.ts +85 -12
  238. package/src/__tests__/tools-list-cli.test.ts +171 -0
  239. package/src/__tests__/top-level-renderer.test.ts +54 -1
  240. package/src/__tests__/trusted-contact-inline-approval-integration.test.ts +68 -1
  241. package/src/__tests__/trusted-contact-multichannel.test.ts +4 -1
  242. package/src/__tests__/turn-tail-chain.test.ts +135 -0
  243. package/src/__tests__/ui-choice-copy-surfaces.test.ts +6 -4
  244. package/src/__tests__/ui-visual-surface.test.ts +5 -4
  245. package/src/__tests__/ui-work-result-surface.test.ts +5 -4
  246. package/src/__tests__/unified-turn-context-visible-app.test.ts +7 -8
  247. package/src/__tests__/usage-routes.test.ts +62 -0
  248. package/src/__tests__/verification-outbound-delivery.test.ts +176 -0
  249. package/src/__tests__/visible-app-context.test.ts +20 -4
  250. package/src/__tests__/web-search-history.test.ts +96 -0
  251. package/src/__tests__/workspace-migration-139-clear-renamed-cost-profile-label.test.ts +6 -6
  252. package/src/__tests__/workspace-migration-140-repair-seed-pinned-memory-v3-live.test.ts +330 -0
  253. package/src/__tests__/workspace-migration-142-consolidate-voice-front-door.test.ts +150 -0
  254. package/src/acp/failure-error.ts +3 -5
  255. package/src/agent/attachments.ts +26 -24
  256. package/src/agent/history-repair/history-repair.ts +27 -26
  257. package/src/agent/image-optimize.ts +6 -6
  258. package/src/agent/loop.ts +40 -22
  259. package/src/agent/message-types.ts +3 -3
  260. package/src/api/constants/document-tools.ts +55 -0
  261. package/src/api/events/question-answered.ts +53 -0
  262. package/src/api/events/tool-result.ts +7 -0
  263. package/src/api/index.ts +13 -0
  264. package/src/api/responses/conversation-message.ts +11 -0
  265. package/src/api/surfaces-oauth-connect.test.ts +96 -0
  266. package/src/api/surfaces.ts +36 -8
  267. package/src/approvals/guardian-channel-delivery.ts +83 -0
  268. package/src/approvals/guardian-expiry-notifier.ts +13 -11
  269. package/src/approvals/guardian-request-resolvers.ts +222 -135
  270. package/src/calls/__tests__/tts-text-sanitizer.test.ts +13 -0
  271. package/src/calls/__tests__/voice-session-bridge.test.ts +131 -12
  272. package/src/calls/__tests__/voice-triage-escalate.test.ts +106 -0
  273. package/src/calls/call-controller.ts +30 -2
  274. package/src/calls/call-speech-output.ts +12 -4
  275. package/src/calls/call-transport.ts +18 -1
  276. package/src/calls/media-stream-output.ts +53 -5
  277. package/src/calls/media-stream-server.ts +21 -1
  278. package/src/calls/media-stream-stt-session.ts +49 -0
  279. package/src/calls/telephony-synthesis-language.ts +84 -0
  280. package/src/calls/tts-text-sanitizer.ts +12 -5
  281. package/src/calls/voice-session-bridge.ts +86 -17
  282. package/src/calls/voice-triage-escalate.ts +54 -7
  283. package/src/channels/__tests__/gateway-verification-sessions.test.ts +3 -3
  284. package/src/channels/__tests__/plugin-channel-declarations.test.ts +161 -0
  285. package/src/channels/config.ts +20 -2
  286. package/src/channels/gateway-verification-sessions.ts +7 -2
  287. package/src/channels/plugin-channel-declarations.ts +108 -0
  288. package/src/channels/types.ts +37 -1
  289. package/src/cli/AGENTS.md +5 -2
  290. package/src/cli/commands/__tests__/cache.test.ts +7 -4
  291. package/src/cli/commands/__tests__/cli-test-harness.ts +82 -0
  292. package/src/cli/commands/__tests__/conversations-wake.test.ts +51 -6
  293. package/src/cli/commands/__tests__/inference-models.test.ts +14 -0
  294. package/src/cli/commands/__tests__/inference-profiles.test.ts +44 -1
  295. package/src/cli/commands/__tests__/inference-providers.test.ts +7 -0
  296. package/src/cli/commands/__tests__/plugins.test.ts +692 -0
  297. package/src/cli/commands/__tests__/schedules.test.ts +136 -37
  298. package/src/cli/commands/conversations.ts +7 -3
  299. package/src/cli/commands/credentials.help.ts +7 -0
  300. package/src/cli/commands/inference-profiles.ts +9 -3
  301. package/src/cli/commands/inference.help.ts +16 -2
  302. package/src/cli/commands/mcp.help.ts +17 -6
  303. package/src/cli/commands/mcp.ts +9 -0
  304. package/src/cli/commands/memory/memory-retrospective.ts +18 -0
  305. package/src/cli/commands/notifications.ts +3 -6
  306. package/src/cli/commands/oauth/connect-surface-guidance.test.ts +16 -0
  307. package/src/cli/commands/oauth/connect-surface-guidance.ts +25 -7
  308. package/src/cli/commands/oauth/connect.test.ts +26 -1
  309. package/src/cli/commands/oauth/connect.ts +4 -1
  310. package/src/cli/commands/oauth/index.help.ts +3 -1
  311. package/src/cli/commands/oauth/providers.ts +3 -0
  312. package/src/cli/commands/plugins.help.ts +25 -2
  313. package/src/cli/commands/plugins.ts +289 -10
  314. package/src/cli/commands/schedules.help.ts +14 -12
  315. package/src/cli/commands/schedules.ts +19 -5
  316. package/src/cli/commands/stt.help.ts +27 -2
  317. package/src/cli/commands/tools.help.ts +4 -2
  318. package/src/cli/commands/tools.ts +38 -0
  319. package/src/cli/lib/__tests__/inspect-plugin.test.ts +1 -0
  320. package/src/cli/lib/__tests__/install-from-github.test.ts +79 -0
  321. package/src/cli/lib/__tests__/install-from-platform.test.ts +47 -1
  322. package/src/cli/lib/__tests__/plugin-surfaces.test.ts +86 -7
  323. package/src/cli/lib/__tests__/upgrade-plugin.test.ts +161 -0
  324. package/src/cli/lib/install-from-github.ts +61 -0
  325. package/src/cli/lib/install-from-platform.ts +13 -2
  326. package/src/cli/lib/plugin-surfaces.ts +124 -4
  327. package/src/cli/lib/upgrade-plugin.ts +60 -0
  328. package/src/cli.ts +1 -1
  329. package/src/config/__tests__/default-profile-catalog.test.ts +11 -1
  330. package/src/config/__tests__/deployment-context-defaults.test.ts +73 -0
  331. package/src/config/__tests__/memory-retrospective-schema.test.ts +40 -0
  332. package/src/config/__tests__/plugin-updates-schema.test.ts +43 -0
  333. package/src/config/__tests__/profile-tool-support.test.ts +131 -0
  334. package/src/config/bundled-skills/AGENTS.md +1 -1
  335. package/src/config/bundled-skills/acp/SKILL.md +6 -7
  336. package/src/config/bundled-skills/app-builder/SKILL.md +1 -1
  337. package/src/config/bundled-skills/app-builder/references/RESPONSIVE.md +3 -3
  338. package/src/config/bundled-skills/media-processing/services/preprocess.ts +14 -4
  339. package/src/config/bundled-skills/phone-calls/references/CONFIG.md +15 -15
  340. package/src/config/bundled-skills/schedule/SKILL.md +5 -1
  341. package/src/config/bundled-skills/schedule/TOOLS.json +2 -2
  342. package/src/config/bundled-skills/settings/TOOLS.json +3 -3
  343. package/src/config/bundled-skills/settings/tools/voice-config-update.ts +1 -0
  344. package/src/config/bundled-skills/subagent/SKILL.md +53 -22
  345. package/src/config/bundled-skills/subagent/TOOLS.json +14 -13
  346. package/src/config/bundled-skills/transcribe/tools/transcribe-media.test.ts +22 -1
  347. package/src/config/bundled-skills/transcribe/tools/transcribe-media.ts +9 -2
  348. package/src/config/bundled-skills/visualize/SKILL.md +1 -1
  349. package/src/config/call-site-defaults.ts +4 -5
  350. package/src/config/default-profile-catalog.ts +9 -5
  351. package/src/config/feature-flag-registry.json +25 -17
  352. package/src/config/llm-resolver.ts +45 -15
  353. package/src/config/loader.ts +75 -1
  354. package/src/config/profile-tool-support.ts +110 -0
  355. package/src/config/sanitize-for-transfer.ts +11 -0
  356. package/src/config/schema.ts +4 -0
  357. package/src/config/schemas/__tests__/live-voice.test.ts +142 -4
  358. package/src/config/schemas/__tests__/stt.test.ts +20 -5
  359. package/src/config/schemas/call-site-catalog.ts +4 -4
  360. package/src/config/schemas/calls.ts +0 -4
  361. package/src/config/schemas/channels.ts +13 -0
  362. package/src/config/schemas/live-voice.ts +82 -23
  363. package/src/config/schemas/llm.ts +1 -1
  364. package/src/config/schemas/memory-retrospective.ts +25 -0
  365. package/src/config/schemas/plugin-updates.ts +59 -0
  366. package/src/config/schemas/services.ts +8 -4
  367. package/src/config/schemas/stt.ts +18 -5
  368. package/src/config/schemas/timeouts.ts +1 -1
  369. package/src/config/schemas/tts.ts +63 -0
  370. package/src/contacts/__tests__/guardian-delivery-reader.test.ts +16 -0
  371. package/src/contacts/guardian-delivery-reader.ts +29 -2
  372. package/src/context/compactor.ts +4 -4
  373. package/src/context/post-turn-tool-result-truncation.ts +32 -16
  374. package/src/context/strip-injections.ts +42 -0
  375. package/src/context/tool-result-spool.ts +32 -18
  376. package/src/credential-execution/ces-runtime.ts +3 -0
  377. package/src/credential-execution/client.ts +22 -1
  378. package/src/credential-execution/process-manager.test.ts +99 -0
  379. package/src/credential-execution/process-manager.ts +62 -16
  380. package/src/daemon/__tests__/abort-null-controller.test.ts +141 -5
  381. package/src/daemon/__tests__/conversation-surfaces-launch.test.ts +8 -12
  382. package/src/daemon/__tests__/install-assistant-command.test.ts +234 -0
  383. package/src/daemon/__tests__/turn-tail-assistant-reply-notify.test.ts +2 -4
  384. package/src/daemon/__tests__/turn-tail-deleted-conversation.test.ts +20 -29
  385. package/src/daemon/channel-ui-capability.ts +4 -4
  386. package/src/daemon/conversation-agent-loop-handlers.ts +117 -61
  387. package/src/daemon/conversation-agent-loop.ts +318 -148
  388. package/src/daemon/conversation-attachments.ts +1 -1
  389. package/src/daemon/conversation-error.ts +8 -1
  390. package/src/daemon/conversation-initial-prompt.ts +16 -38
  391. package/src/daemon/conversation-lifecycle.ts +154 -32
  392. package/src/daemon/conversation-messaging.ts +106 -43
  393. package/src/daemon/conversation-process.ts +131 -33
  394. package/src/daemon/conversation-queue-manager.ts +10 -0
  395. package/src/daemon/conversation-runtime-assembly.ts +9 -4
  396. package/src/daemon/conversation-store.ts +52 -3
  397. package/src/daemon/conversation-surface-state.ts +9 -0
  398. package/src/daemon/conversation-surfaces.ts +403 -210
  399. package/src/daemon/conversation-tool-setup.ts +56 -7
  400. package/src/daemon/conversation-turn-finalize.ts +134 -89
  401. package/src/daemon/conversation.ts +111 -27
  402. package/src/daemon/disk-pressure-guard-lifecycle.ts +2 -2
  403. package/src/daemon/disk-pressure-guard.ts +3 -3
  404. package/src/daemon/doordash-steps.ts +5 -5
  405. package/src/daemon/external-plugins-bootstrap.ts +7 -1
  406. package/src/daemon/handlers/config-channels.ts +48 -5
  407. package/src/daemon/handlers/conversation-history.ts +1 -1
  408. package/src/daemon/handlers/conversations.ts +62 -17
  409. package/src/daemon/handlers/shared.ts +12 -0
  410. package/src/daemon/handlers/skills.ts +11 -1
  411. package/src/daemon/identity-helpers.ts +38 -10
  412. package/src/daemon/install-assistant-command.ts +322 -0
  413. package/src/daemon/lifecycle.ts +14 -3
  414. package/src/daemon/message-types/conversations.ts +6 -7
  415. package/src/daemon/message-types/sync.ts +1 -0
  416. package/src/daemon/persist-media-references.ts +23 -14
  417. package/src/daemon/process-message.ts +6 -6
  418. package/src/daemon/tool-setup-types.ts +19 -122
  419. package/src/daemon/tool-side-effects.ts +17 -94
  420. package/src/daemon/trust-context-types.ts +39 -0
  421. package/src/daemon/trust-context.ts +14 -6
  422. package/src/daemon/turn-tail-chain.ts +73 -0
  423. package/src/daemon/web-search-history.ts +47 -33
  424. package/src/heartbeat/heartbeat-service.ts +6 -6
  425. package/src/inbound/__tests__/oauth-callback-url.test.ts +83 -0
  426. package/src/inbound/oauth-callback-url.ts +61 -0
  427. package/src/ipc/__tests__/socket-path.test.ts +21 -0
  428. package/src/ipc/assistant-server.ts +17 -10
  429. package/src/ipc/cli-client.ts +10 -1
  430. package/src/ipc/routes/__tests__/documents-sync-ipc-routes.test.ts +56 -0
  431. package/src/ipc/routes/documents-sync-ipc-routes.ts +42 -0
  432. package/src/ipc/socket-cleanup.ts +9 -7
  433. package/src/ipc/socket-path.ts +4 -96
  434. package/src/live-voice/__tests__/live-voice-agent-turn.test.ts +1 -104
  435. package/src/live-voice/__tests__/live-voice-archive.test.ts +16 -16
  436. package/src/live-voice/__tests__/live-voice-attach-image.test.ts +209 -0
  437. package/src/live-voice/__tests__/live-voice-connection.test.ts +20 -0
  438. package/src/live-voice/__tests__/live-voice-events.test.ts +7 -8
  439. package/src/live-voice/__tests__/live-voice-flux-turn-end.test.ts +932 -0
  440. package/src/live-voice/__tests__/live-voice-integration.test.ts +12 -5
  441. package/src/live-voice/__tests__/live-voice-metrics.test.ts +115 -8
  442. package/src/live-voice/__tests__/live-voice-photo.test.ts +100 -0
  443. package/src/live-voice/__tests__/live-voice-progress.test.ts +179 -146
  444. package/src/live-voice/__tests__/live-voice-session-telemetry.test.ts +265 -0
  445. package/src/live-voice/__tests__/live-voice-stt.test.ts +324 -2
  446. package/src/live-voice/__tests__/live-voice-triage-escalate.test.ts +131 -2
  447. package/src/live-voice/__tests__/live-voice-tts-session.test.ts +0 -483
  448. package/src/live-voice/__tests__/live-voice-tts.test.ts +148 -2
  449. package/src/live-voice/__tests__/live-voice-vad.test.ts +313 -32
  450. package/src/live-voice/__tests__/progress-narration.test.ts +214 -0
  451. package/src/live-voice/__tests__/progress-phrases.test.ts +167 -0
  452. package/src/live-voice/__tests__/protocol.test.ts +52 -0
  453. package/src/live-voice/__tests__/runtime-websocket-shell.test.ts +3 -0
  454. package/src/live-voice/live-voice-archive.ts +13 -13
  455. package/src/live-voice/live-voice-connection.ts +7 -1
  456. package/src/live-voice/live-voice-metrics.ts +57 -32
  457. package/src/live-voice/live-voice-photo.ts +168 -0
  458. package/src/live-voice/live-voice-session.ts +1137 -351
  459. package/src/live-voice/live-voice-tts.ts +18 -2
  460. package/src/live-voice/progress-narration.ts +277 -0
  461. package/src/live-voice/progress-phrases.ts +105 -2
  462. package/src/live-voice/protocol.ts +110 -2
  463. package/src/mcp/__tests__/mcp-auth-orchestrator.test.ts +0 -1
  464. package/src/mcp/__tests__/mcp-oauth-client-registration.test.ts +196 -0
  465. package/src/mcp/__tests__/mcp-oauth-provider.test.ts +9 -9
  466. package/src/mcp/client.ts +14 -7
  467. package/src/mcp/mcp-auth-orchestrator.ts +13 -22
  468. package/src/mcp/mcp-oauth-provider.ts +195 -240
  469. package/src/messaging/providers/__tests__/callback-routing.test.ts +5 -1
  470. package/src/messaging/providers/__tests__/transport-dispatch.test.ts +66 -4
  471. package/src/messaging/providers/callback-routing.ts +1 -0
  472. package/src/messaging/providers/discord/api.ts +312 -0
  473. package/src/messaging/providers/discord/dm-delivery.test.ts +204 -0
  474. package/src/messaging/providers/discord/render.test.ts +204 -0
  475. package/src/messaging/providers/discord/render.ts +195 -0
  476. package/src/messaging/providers/discord/send.test.ts +179 -0
  477. package/src/messaging/providers/discord/send.ts +212 -0
  478. package/src/messaging/providers/discord/transport.ts +81 -0
  479. package/src/messaging/providers/index.ts +3 -1
  480. package/src/messaging/providers/retry-policy.test.ts +247 -0
  481. package/src/messaging/providers/retry-policy.ts +206 -0
  482. package/src/messaging/providers/slack/mention-source.test.ts +285 -0
  483. package/src/messaging/providers/slack/mention-source.ts +287 -0
  484. package/src/messaging/providers/telegram-bot/api.ts +47 -130
  485. package/src/messaging/providers/whatsapp/api.ts +47 -136
  486. package/src/monitoring/__tests__/db-integrity-sample.test.ts +4 -1
  487. package/src/monitoring/__tests__/plugin-auto-update.test.ts +495 -0
  488. package/src/monitoring/db-integrity-sample.ts +6 -1
  489. package/src/monitoring/plugin-auto-update.ts +463 -0
  490. package/src/monitoring/resource-sampler.ts +21 -5
  491. package/src/monitoring/worker.ts +13 -0
  492. package/src/notifications/__tests__/assistant-reply-producer.test.ts +375 -2
  493. package/src/notifications/__tests__/broadcaster.test.ts +37 -1
  494. package/src/notifications/__tests__/copy-composer.test.ts +165 -0
  495. package/src/notifications/__tests__/emit-signal-pipeline-failure.test.ts +205 -0
  496. package/src/notifications/__tests__/notification-utils.test.ts +249 -0
  497. package/src/notifications/adapters/platform.ts +31 -2
  498. package/src/notifications/assistant-reply-producer.ts +79 -32
  499. package/src/notifications/broadcaster.ts +30 -10
  500. package/src/notifications/copy-composer.ts +69 -0
  501. package/src/notifications/emit-signal.ts +82 -15
  502. package/src/notifications/events-store.ts +10 -2
  503. package/src/notifications/home-feed-side-effect.ts +6 -3
  504. package/src/notifications/notification-utils.ts +173 -5
  505. package/src/notifications/signal.ts +13 -0
  506. package/src/oauth/AGENTS.md +4 -2
  507. package/src/oauth/byo-connection.ts +6 -1
  508. package/src/oauth/credential-token-resolver.ts +3 -10
  509. package/src/oauth/manual-token-connection.ts +29 -64
  510. package/src/oauth/manual-token-providers.ts +62 -0
  511. package/src/oauth/provider-serializer.ts +13 -0
  512. package/src/oauth/seed-providers.ts +62 -11
  513. package/src/onboarding/onboarding-events-store.ts +91 -1
  514. package/src/permissions/question-prompter.test.ts +4 -0
  515. package/src/permissions/question-prompter.ts +21 -4
  516. package/src/persistence/__tests__/conversation-lineage.test.ts +248 -0
  517. package/src/persistence/attachments-store.ts +15 -15
  518. package/src/persistence/conversation-attention-store.ts +30 -10
  519. package/src/persistence/conversation-bootstrap.ts +17 -0
  520. package/src/persistence/conversation-crud.ts +363 -97
  521. package/src/persistence/conversation-group-migration.ts +1 -1
  522. package/src/persistence/conversation-key-store.ts +38 -10
  523. package/src/persistence/conversation-lineage.ts +226 -0
  524. package/src/persistence/conversation-plugin-facade.ts +28 -3
  525. package/src/persistence/conversation-queries.ts +207 -33
  526. package/src/persistence/conversation-types.ts +107 -1
  527. package/src/persistence/jobs-store.ts +32 -8
  528. package/src/persistence/llm-usage-store.ts +22 -0
  529. package/src/persistence/migrations/361-normalize-managed-connection-rows.test.ts +168 -0
  530. package/src/persistence/migrations/361-normalize-managed-connection-rows.ts +76 -0
  531. package/src/persistence/migrations/362-add-conversation-subagent-kind.test.ts +187 -0
  532. package/src/persistence/migrations/362-add-conversation-subagent-kind.ts +93 -0
  533. package/src/persistence/migrations/363-backfill-schedule-inference-profile.test.ts +229 -0
  534. package/src/persistence/migrations/363-backfill-schedule-inference-profile.ts +127 -0
  535. package/src/persistence/migrations/364-add-schedule-source-key.test.ts +120 -0
  536. package/src/persistence/migrations/364-add-schedule-source-key.ts +43 -0
  537. package/src/persistence/migrations/365-add-conversation-fork-strategy.ts +27 -0
  538. package/src/persistence/schema/conversations.ts +32 -0
  539. package/src/persistence/schema/infrastructure.ts +3 -0
  540. package/src/persistence/steps.ts +30 -0
  541. package/src/persistence/subagent-store.ts +148 -6
  542. package/src/platform/managed-speech.test.ts +20 -0
  543. package/src/platform/managed-speech.ts +9 -0
  544. package/src/plugin-api/__tests__/oauth-callback-url-export.test.ts +29 -0
  545. package/src/plugin-api/index.ts +28 -5
  546. package/src/plugin-api/webhook-url.ts +151 -0
  547. package/src/plugins/__tests__/installed-plugin-dirs.test.ts +82 -0
  548. package/src/plugins/__tests__/mcp-servers.test.ts +370 -0
  549. package/src/plugins/__tests__/source-fingerprint.test.ts +5 -1
  550. package/src/plugins/collect-source-versions.ts +4 -27
  551. package/src/plugins/defaults/exploration-drift/hooks/post-tool-use.ts +5 -5
  552. package/src/plugins/defaults/exploration-drift/package.json +1 -1
  553. package/src/plugins/defaults/image-recovery/hooks/post-model-call.ts +4 -2
  554. package/src/plugins/defaults/image-recovery/recover.ts +67 -57
  555. package/src/plugins/defaults/index.ts +1 -1
  556. package/src/plugins/defaults/memory/__tests__/conversation-queries.test.ts +64 -18
  557. package/src/plugins/defaults/memory/__tests__/jobs-worker-outcome-contract.test.ts +242 -0
  558. package/src/plugins/defaults/memory/__tests__/jobs-worker-retrospective-sweep.test.ts +24 -1
  559. package/src/plugins/defaults/memory/__tests__/memory-retrospective-enqueue.test.ts +48 -3
  560. package/src/plugins/defaults/memory/__tests__/memory-retrospective-job.test.ts +511 -19
  561. package/src/plugins/defaults/memory/__tests__/memory-retrospective-prompt.test.ts +39 -3
  562. package/src/plugins/defaults/memory/__tests__/memory-retrospective-provider-path.test.ts +493 -0
  563. package/src/plugins/defaults/memory/__tests__/memory-retrospective-wake-chain.test.ts +5 -4
  564. package/src/plugins/defaults/memory/__tests__/memory-tier-boundary-guard.test.ts +5 -1
  565. package/src/plugins/defaults/memory/job-handlers.ts +87 -5
  566. package/src/plugins/defaults/memory/jobs-worker.ts +60 -6
  567. package/src/plugins/defaults/memory/memory-retrospective-constants.ts +11 -0
  568. package/src/plugins/defaults/memory/memory-retrospective-enqueue.ts +29 -0
  569. package/src/plugins/defaults/memory/memory-retrospective-job.ts +342 -60
  570. package/src/plugins/defaults/memory/memory-retrospective-prompt.ts +23 -3
  571. package/src/plugins/defaults/memory/substrate/consolidation-lock.ts +9 -4
  572. package/src/plugins/defaults/memory/substrate/skill-content.ts +8 -1
  573. package/src/plugins/defaults/memory/substrate/sweep-job.ts +7 -0
  574. package/src/plugins/defaults/memory/v1/graph/injection.ts +1 -1
  575. package/src/plugins/defaults/memory/v2/router.ts +8 -0
  576. package/src/plugins/defaults/turn-context/unified-turn-context.ts +1 -2
  577. package/src/plugins/external-plugin-loader.ts +64 -8
  578. package/src/plugins/installed-plugin-dirs.ts +101 -0
  579. package/src/plugins/mcp-servers.ts +343 -0
  580. package/src/plugins/mtime-cache.ts +34 -22
  581. package/src/plugins/types.ts +69 -8
  582. package/src/prompts/persona-resolver.ts +6 -2
  583. package/src/prompts/system-prompt.ts +72 -17
  584. package/src/providers/__tests__/inference.test.ts +40 -0
  585. package/src/providers/__tests__/preflight-resolved-config.test.ts +58 -1
  586. package/src/providers/__tests__/retry-callsite.test.ts +72 -2
  587. package/src/providers/__tests__/vellum-connection-routing.test.ts +42 -11
  588. package/src/providers/anthropic/client.ts +112 -95
  589. package/src/providers/connection-resolution.ts +35 -1
  590. package/src/providers/gemini/client.ts +5 -5
  591. package/src/providers/inference/adapter-factory.ts +16 -10
  592. package/src/providers/inference/auth.ts +16 -0
  593. package/src/providers/inference/connection-availability.ts +164 -12
  594. package/src/providers/inference/connections.ts +25 -1
  595. package/src/providers/media-resolve.ts +21 -11
  596. package/src/providers/model-catalog.ts +15 -0
  597. package/src/providers/openai/chat-completions-provider.ts +4 -4
  598. package/src/providers/openai/responses-provider.ts +12 -25
  599. package/src/providers/registry.ts +9 -4
  600. package/src/providers/retry.ts +25 -0
  601. package/src/providers/server-tool-pairing.ts +134 -0
  602. package/src/providers/speech-to-text/__tests__/deepgram-flux-frames.test.ts +433 -0
  603. package/src/providers/speech-to-text/__tests__/deepgram-flux-realtime.test.ts +620 -0
  604. package/src/providers/speech-to-text/__tests__/provider-catalog.test.ts +34 -0
  605. package/src/providers/speech-to-text/__tests__/resolve.test.ts +472 -12
  606. package/src/providers/speech-to-text/deepgram-flux-frames.ts +395 -0
  607. package/src/providers/speech-to-text/deepgram-flux-realtime.ts +719 -0
  608. package/src/providers/speech-to-text/deepgram-realtime.test.ts +283 -1
  609. package/src/providers/speech-to-text/deepgram-realtime.ts +117 -3
  610. package/src/providers/speech-to-text/deepgram.ts +1 -0
  611. package/src/providers/speech-to-text/provider-catalog.ts +137 -8
  612. package/src/providers/speech-to-text/resolve.ts +105 -12
  613. package/src/providers/speech-to-text/vellum-managed.ts +7 -1
  614. package/src/providers/speech-to-text/vellum-speech-relay-connection.ts +3 -1
  615. package/src/providers/types.ts +26 -6
  616. package/src/providers/vellum-model-routing.ts +6 -7
  617. package/src/runtime/AGENTS.md +2 -0
  618. package/src/runtime/__tests__/agent-wake.test.ts +16 -16
  619. package/src/runtime/__tests__/background-job-runner.test.ts +0 -144
  620. package/src/runtime/__tests__/desktop-presence.test.ts +162 -0
  621. package/src/runtime/__tests__/local-actor-identity-force-refresh.test.ts +104 -0
  622. package/src/runtime/agent-wake.ts +63 -24
  623. package/src/runtime/assistant-event-hub.ts +110 -12
  624. package/src/runtime/auth/__tests__/guard-tests.test.ts +2 -0
  625. package/src/runtime/auth/__tests__/scopes.test.ts +20 -0
  626. package/src/runtime/auth/same-actor.ts +56 -8
  627. package/src/runtime/auth/scopes.ts +12 -0
  628. package/src/runtime/auth/types.ts +2 -0
  629. package/src/runtime/background-job-runner.ts +5 -27
  630. package/src/runtime/channel-readiness-service.ts +59 -3
  631. package/src/runtime/channel-readiness-types.ts +18 -0
  632. package/src/runtime/desktop-presence.ts +69 -0
  633. package/src/runtime/http-server.ts +12 -0
  634. package/src/runtime/local-actor-identity.ts +29 -14
  635. package/src/runtime/migrations/__tests__/vbundle-symlink-streaming-importer.test.ts +0 -0
  636. package/src/runtime/routes/__tests__/channel-verification-routes.test.ts +1 -1
  637. package/src/runtime/routes/__tests__/client-routes.test.ts +186 -14
  638. package/src/runtime/routes/__tests__/conversation-list-routes.test.ts +547 -1
  639. package/src/runtime/routes/__tests__/conversation-management-routes.test.ts +162 -7
  640. package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +14 -10
  641. package/src/runtime/routes/__tests__/inference-profiles-routes.test.ts +340 -3
  642. package/src/runtime/routes/__tests__/llm-call-sites-routes.test.ts +28 -1
  643. package/src/runtime/routes/__tests__/plugins-routes.test.ts +217 -1
  644. package/src/runtime/routes/__tests__/question-routes.test.ts +64 -0
  645. package/src/runtime/routes/__tests__/retrospective-routes.test.ts +20 -0
  646. package/src/runtime/routes/__tests__/skill-history-route.test.ts +116 -0
  647. package/src/runtime/routes/__tests__/sse-actor-principal-heal.test.ts +165 -0
  648. package/src/runtime/routes/__tests__/stt-routes.test.ts +25 -0
  649. package/src/runtime/routes/__tests__/surface-action-routes.test.ts +2 -0
  650. package/src/runtime/routes/__tests__/wake-conversation-routes.test.ts +60 -2
  651. package/src/runtime/routes/archive-utils.ts +16 -12
  652. package/src/runtime/routes/attachment-routes.ts +13 -6
  653. package/src/runtime/routes/canned-reply-release.ts +54 -0
  654. package/src/runtime/routes/channel-availability-routes.ts +32 -14
  655. package/src/runtime/routes/channel-verification-routes.ts +2 -27
  656. package/src/runtime/routes/chatgpt-subscription-auth-routes.ts +5 -0
  657. package/src/runtime/routes/client-routes.ts +114 -2
  658. package/src/runtime/routes/conversation-cli-routes.ts +10 -12
  659. package/src/runtime/routes/conversation-list-routes.ts +59 -10
  660. package/src/runtime/routes/conversation-management-routes.ts +47 -18
  661. package/src/runtime/routes/conversation-query-routes.ts +32 -53
  662. package/src/runtime/routes/conversation-routes.ts +360 -293
  663. package/src/runtime/routes/credential-prompt-routes.ts +20 -2
  664. package/src/runtime/routes/debug-routes.ts +4 -1
  665. package/src/runtime/routes/default-provider-routes.ts +7 -11
  666. package/src/runtime/routes/documents-routes.ts +44 -1
  667. package/src/runtime/routes/events-routes.ts +29 -1
  668. package/src/runtime/routes/host-app-control-routes.ts +2 -0
  669. package/src/runtime/routes/host-bash-routes.ts +2 -0
  670. package/src/runtime/routes/host-browser-routes.ts +2 -0
  671. package/src/runtime/routes/host-cu-routes.ts +2 -0
  672. package/src/runtime/routes/host-file-routes.ts +2 -0
  673. package/src/runtime/routes/host-transfer-routes.ts +4 -0
  674. package/src/runtime/routes/identity-routes.ts +6 -6
  675. package/src/runtime/routes/inbound-message-handler.ts +5 -5
  676. package/src/runtime/routes/inbound-stages/acl-enforcement.ts +7 -3
  677. package/src/runtime/routes/inbound-stages/background-dispatch.ts +14 -0
  678. package/src/runtime/routes/inbound-stages/bootstrap-intercept.ts +2 -2
  679. package/src/runtime/routes/inbound-stages/guardian-activation-intercept.test.ts +14 -11
  680. package/src/runtime/routes/inbound-stages/guardian-activation-intercept.ts +14 -17
  681. package/src/runtime/routes/inference-profile-availability-guard.ts +171 -0
  682. package/src/runtime/routes/inference-profile-session-handler.ts +26 -2
  683. package/src/runtime/routes/inference-profile-session-routes.ts +2 -1
  684. package/src/runtime/routes/inference-profiles-routes.ts +152 -73
  685. package/src/runtime/routes/inference-provider-connection-routes.ts +0 -16
  686. package/src/runtime/routes/llm-call-sites-routes.ts +13 -0
  687. package/src/runtime/routes/log-export-routes.ts +1 -1
  688. package/src/runtime/routes/mcp-auth-routes.ts +141 -57
  689. package/src/runtime/routes/notification-routes.ts +0 -14
  690. package/src/runtime/routes/oauth-providers.ts +1 -0
  691. package/src/runtime/routes/plugins-routes.ts +127 -4
  692. package/src/runtime/routes/profiler-routes.ts +4 -2
  693. package/src/runtime/routes/question-routes.ts +28 -10
  694. package/src/runtime/routes/retrospective-routes.ts +9 -3
  695. package/src/runtime/routes/schedule-routes.ts +210 -12
  696. package/src/runtime/routes/secret-routes.ts +40 -23
  697. package/src/runtime/routes/settings-routes.ts +41 -13
  698. package/src/runtime/routes/skills-routes.ts +79 -0
  699. package/src/runtime/routes/sse-actor-principal-heal.ts +113 -0
  700. package/src/runtime/routes/stt-routes.ts +31 -25
  701. package/src/runtime/routes/ui-snapshot-routes.ts +1 -0
  702. package/src/runtime/routes/wake-conversation-routes.ts +23 -0
  703. package/src/runtime/services/__tests__/conversation-serializer.test.ts +1 -0
  704. package/src/runtime/services/conversation-serializer.ts +24 -9
  705. package/src/runtime/sync/documents-sidecar-publish.test.ts +101 -0
  706. package/src/runtime/sync/resource-sync-events.ts +79 -1
  707. package/src/runtime/sync/worker-daemon-notify.test.ts +34 -0
  708. package/src/runtime/sync/worker-daemon-notify.ts +44 -6
  709. package/src/runtime/verification-outbound-actions.ts +250 -222
  710. package/src/runtime/verification-templates.ts +41 -30
  711. package/src/schedule/__tests__/plugin-schedule-declarations.test.ts +614 -0
  712. package/src/schedule/__tests__/plugin-schedule-reconciler.test.ts +1066 -0
  713. package/src/schedule/inference-profile.ts +64 -3
  714. package/src/schedule/plugin-schedule-declarations.ts +518 -0
  715. package/src/schedule/plugin-schedule-reconciler.ts +605 -0
  716. package/src/schedule/plugin-schedules-gate.ts +23 -0
  717. package/src/schedule/schedule-store.ts +520 -26
  718. package/src/schedule/scheduler.ts +38 -0
  719. package/src/security/credential-backend.ts +51 -4
  720. package/src/security/encrypted-store.ts +64 -42
  721. package/src/security/oauth2.ts +6 -22
  722. package/src/security/secure-keys.ts +55 -7
  723. package/src/skills/catalog-install.ts +20 -7
  724. package/src/skills/inline-command-runner.ts +2 -7
  725. package/src/skills/skill-history.test.ts +269 -0
  726. package/src/skills/skill-history.ts +250 -0
  727. package/src/skills/skillssh-registry.ts +1 -1
  728. package/src/stt/__tests__/daemon-batch-transcriber.test.ts +22 -0
  729. package/src/stt/__tests__/language-metadata.test.ts +85 -0
  730. package/src/stt/__tests__/speech-energy.test.ts +79 -0
  731. package/src/stt/__tests__/types.test.ts +94 -0
  732. package/src/stt/daemon-batch-transcriber.ts +18 -6
  733. package/src/stt/language-metadata.ts +65 -0
  734. package/src/stt/speech-energy.ts +115 -12
  735. package/src/stt/stt-stream-session.ts +8 -4
  736. package/src/stt/types.ts +119 -0
  737. package/src/subagent/__tests__/consult-prompt.test.ts +25 -0
  738. package/src/subagent/consult-prompt.ts +3 -1
  739. package/src/subagent/consult-transcript.ts +1 -1
  740. package/src/subagent/manager.ts +304 -44
  741. package/src/subagent/progress-events.ts +35 -0
  742. package/src/subagent/role-resolution.ts +88 -0
  743. package/src/subagent/types.ts +303 -67
  744. package/src/subagent/validate-allowlists.ts +3 -4
  745. package/src/telegram/__tests__/webhook-health.test.ts +73 -1
  746. package/src/telegram/webhook-health.ts +101 -3
  747. package/src/telemetry/__tests__/telemetry-event-fixtures.ts +2 -0
  748. package/src/telemetry/activation-funnel.ts +6 -1
  749. package/src/telemetry/live-voice-funnel.ts +75 -0
  750. package/src/telemetry/telemetry-event-sources.ts +6 -0
  751. package/src/telemetry/telemetry-wire-source.json +1 -1
  752. package/src/telemetry/telemetry-wire.generated.ts +2 -0
  753. package/src/telemetry/types.ts +23 -4
  754. package/src/telemetry/usage-telemetry-reporter.test.ts +4 -0
  755. package/src/tools/__tests__/subagent-workflow-tool-input-schemas.test.ts +1 -1
  756. package/src/tools/ask-question/ask-question-tool.test.ts +118 -3
  757. package/src/tools/ask-question/ask-question-tool.ts +56 -2
  758. package/src/tools/browser/browser-execution.ts +1 -1
  759. package/src/tools/calls/call-start.ts +8 -5
  760. package/src/tools/conversation-groups/move_to_group.ts +10 -12
  761. package/src/tools/document/document-tool.ts +11 -4
  762. package/src/tools/execution-timeout.ts +9 -2
  763. package/src/tools/executor.ts +7 -6
  764. package/src/tools/filesystem/edit.ts +1 -1
  765. package/src/tools/filesystem/list.ts +1 -1
  766. package/src/tools/filesystem/read.ts +7 -13
  767. package/src/tools/filesystem/search.ts +23 -21
  768. package/src/tools/filesystem/write.ts +4 -1
  769. package/src/tools/host-filesystem/edit.ts +7 -2
  770. package/src/tools/host-filesystem/read.ts +21 -8
  771. package/src/tools/host-filesystem/write.ts +10 -2
  772. package/src/tools/host-terminal/host-shell.ts +2 -2
  773. package/src/tools/mcp/mcp-tool-factory.ts +25 -1
  774. package/src/tools/permission-checker.ts +8 -3
  775. package/src/tools/schedule/delete.ts +13 -1
  776. package/src/tools/schedule/list.ts +14 -3
  777. package/src/tools/schedule/update.ts +56 -33
  778. package/src/tools/shared/filesystem/audio-read.ts +6 -4
  779. package/src/tools/shared/filesystem/file-ops-service.ts +176 -116
  780. package/src/tools/shared/filesystem/image-read.ts +12 -12
  781. package/src/tools/shared/filesystem/path-policy.ts +70 -5
  782. package/src/tools/shared/filesystem/size-guard.ts +6 -6
  783. package/src/tools/shared/input-misuse.ts +72 -0
  784. package/src/tools/skills/scaffold-managed.ts +82 -0
  785. package/src/tools/skills/skill-tool-factory.ts +14 -1
  786. package/src/tools/subagent/consult-deadline.ts +12 -7
  787. package/src/tools/subagent/message.ts +3 -1
  788. package/src/tools/subagent/read.ts +66 -4
  789. package/src/tools/subagent/spawn.ts +500 -78
  790. package/src/tools/tool-approval-handler.ts +45 -4
  791. package/src/tools/types.ts +22 -3
  792. package/src/tools/ui-surface/surface-shape-docs.ts +1 -1
  793. package/src/tools/ui-surface/visual-validation.ts +2 -0
  794. package/src/tts/__tests__/provider-adapters.test.ts +147 -0
  795. package/src/tts/__tests__/speakable-segments.test.ts +470 -0
  796. package/src/tts/language-voices.ts +23 -0
  797. package/src/tts/providers/deepgram-provider.ts +3 -1
  798. package/src/tts/providers/elevenlabs-provider.ts +73 -1
  799. package/src/tts/providers/xai-provider.ts +28 -2
  800. package/src/tts/speakable-segments.ts +293 -23
  801. package/src/tts/synthesis-stream.ts +7 -0
  802. package/src/tts/types.ts +7 -0
  803. package/src/usage/__tests__/subagent-attribution.test.ts +109 -0
  804. package/src/usage/subagent-attribution.ts +108 -0
  805. package/src/util/__tests__/language-subtag.test.ts +54 -0
  806. package/src/util/__tests__/sqlite-retry.test.ts +112 -0
  807. package/src/util/abort-reasons.ts +16 -0
  808. package/src/util/ansi.ts +44 -0
  809. package/src/util/disk-usage.ts +17 -13
  810. package/src/util/image-conversion.ts +20 -13
  811. package/src/util/language-subtag.ts +43 -0
  812. package/src/util/schedule-source-key.ts +23 -0
  813. package/src/util/unicode.ts +1 -1
  814. package/src/util/worker-process.ts +7 -1
  815. package/src/workspace/git-service.ts +33 -9
  816. package/src/workspace/migrations/140-repair-seed-pinned-memory-v3-live.ts +260 -0
  817. package/src/workspace/migrations/141-stt-english-default-to-multilingual.ts +111 -0
  818. package/src/workspace/migrations/142-consolidate-voice-front-door.ts +70 -0
  819. package/src/workspace/migrations/__tests__/141-stt-english-default-to-multilingual.test.ts +116 -0
  820. package/src/workspace/migrations/registry.ts +6 -0
  821. package/src/workspace/top-level-renderer.ts +15 -2
  822. package/src/__tests__/tool-side-effects-slack-dm.test.ts +0 -357
  823. package/src/daemon/install-symlink.ts +0 -207
  824. package/src/live-voice/__tests__/front-decision.test.ts +0 -525
  825. package/src/live-voice/front-decision.ts +0 -429
  826. package/src/notifications/deferred-emit.ts +0 -147
@@ -41,6 +41,7 @@ import {
41
41
  import { z } from "zod";
42
42
 
43
43
  import type { AssistantConfig } from "../../../../config/types.js";
44
+ import { warmGuardianBindings } from "../../../../contacts/guardian-delivery-reader.js";
44
45
  import {
45
46
  cachedTextBlock,
46
47
  extractToolUse,
@@ -393,6 +394,13 @@ async function runRouterBatch(
393
394
  provider,
394
395
  } = params;
395
396
 
397
+ // Warm both guardian-delivery cache keys (vellum + unfiltered) so
398
+ // resolveUserName's sync guardian-file resolution, including its
399
+ // any-channel fallback, hits fresh keys instead of falling back to
400
+ // users/default.md on a cold/TTL-expired cache. Two local-socket IPC
401
+ // reads per batch, dwarfed by the batch's provider call.
402
+ await warmGuardianBindings();
403
+
396
404
  const systemPrompt = resolveRouterPrompt(
397
405
  config.memory?.v2?.router?.router_prompt_path ?? null,
398
406
  workspaceDir,
@@ -29,7 +29,6 @@ export interface UnifiedTurnContextOptions {
29
29
  name: string;
30
30
  /** Directory stem: the handle a human would recognize the app by. */
31
31
  slug: string;
32
- sourceDir: string;
33
32
  /** Set when the app is bundled by an installed plugin rather than built here. */
34
33
  pluginName?: string;
35
34
  } | null;
@@ -127,7 +126,7 @@ export function buildUnifiedTurnContextBlock(
127
126
  ? ` It is bundled by the "${sanitizeInlineContextValue(app.pluginName)}" plugin, not built in the sandbox.`
128
127
  : "";
129
128
  lines.push(
130
- `visible_app: "${sanitizeInlineContextValue(app.name)}" (app_id: "${sanitizeInlineContextValue(app.appId)}", slug: "${sanitizeInlineContextValue(app.slug)}", source: "${sanitizeInlineContextValue(app.sourceDir)}"). The user has this app open on screen; unqualified references to "the app" mean this one. The app tools take the app_id.${provenance}`,
129
+ `visible_app: "${sanitizeInlineContextValue(app.name)}" (app_id: "${sanitizeInlineContextValue(app.appId)}", slug: "${sanitizeInlineContextValue(app.slug)}"). The user has this app open on screen; unqualified references to "the app" mean this one.${provenance}`,
131
130
  );
132
131
  }
133
132
 
@@ -92,6 +92,12 @@ const PluginPackageJsonSchema = z
92
92
  version: z.string().optional(),
93
93
  peerDependencies: z.record(z.string(), z.string()).optional(),
94
94
  credentialKeyPatterns: z.unknown().optional(),
95
+ /** Human title, when the package name is not one ("@vellumai/imessage"). */
96
+ displayName: z.string().min(1).optional(),
97
+ /** Standard npm field, reused as the one-line description clients show. */
98
+ description: z.string().min(1).optional(),
99
+ /** Lucide icon name without the `lucide-` prefix, matching `ChannelInfo`. */
100
+ icon: z.string().min(1).optional(),
95
101
  })
96
102
  .passthrough();
97
103
 
@@ -461,9 +467,55 @@ export async function loadExternalPlugin(
461
467
  * Exported so the mtime cache can discover plugin identity without going
462
468
  * through the full `buildExternalPlugin` path. The identity mirrors
463
469
  * {@link buildPluginFromDir}: the directory name, not `package.json` `name`.
470
+ *
471
+ * `quiet` suppresses the failure logs, for callers that poll on a timer and
472
+ * surface the failure through their own channel (e.g. the schedule
473
+ * reconciler's per-day deduped notification).
474
+ */
475
+ /**
476
+ * How a plugin presents itself: its title, its one line, its glyph.
477
+ *
478
+ * Every field is optional and none of them gate anything. A surface that
479
+ * shows a plugin falls back rather than hiding it, so a missing `icon`
480
+ * costs an icon and not the row.
481
+ */
482
+ export interface PluginPresentation {
483
+ displayName?: string;
484
+ description?: string;
485
+ icon?: string;
486
+ }
487
+
488
+ /**
489
+ * Read a plugin's presentation fields from its manifest.
490
+ *
491
+ * Separate from {@link parsePluginManifest} because the two answer different
492
+ * questions: that one decides whether a plugin is loadable, this one only
493
+ * dresses it for display. An unreadable or invalid manifest yields `undefined`
494
+ * here rather than an error, since a caller showing a plugin has already
495
+ * established it exists.
464
496
  */
497
+ export async function parsePluginPresentation(
498
+ pluginDir: string,
499
+ ): Promise<PluginPresentation | undefined> {
500
+ let rawPkg: unknown;
501
+ try {
502
+ rawPkg = JSON.parse(
503
+ await readFile(join(pluginDir, "package.json"), "utf8"),
504
+ );
505
+ } catch {
506
+ return undefined;
507
+ }
508
+ const parsed = PluginPackageJsonSchema.safeParse(rawPkg);
509
+ if (!parsed.success) {
510
+ return undefined;
511
+ }
512
+ const { displayName, description, icon } = parsed.data;
513
+ return { displayName, description, icon };
514
+ }
515
+
465
516
  export async function parsePluginManifest(
466
517
  pluginDir: string,
518
+ opts: { quiet?: boolean } = {},
467
519
  ): Promise<
468
520
  Pick<PluginManifest, "name" | "version" | "credentialKeyPatterns"> | undefined
469
521
  > {
@@ -473,18 +525,22 @@ export async function parsePluginManifest(
473
525
  rawPkg = JSON.parse(await readFile(pkgPath, "utf8"));
474
526
  } catch (err) {
475
527
  const reason = err instanceof Error ? err.message : String(err);
476
- log.error(
477
- { err, pluginDir },
478
- `package.json at ${pluginDir} could not be read or parsed: ${reason}`,
479
- );
528
+ if (!opts.quiet) {
529
+ log.error(
530
+ { err, pluginDir },
531
+ `package.json at ${pluginDir} could not be read or parsed: ${reason}`,
532
+ );
533
+ }
480
534
  return undefined;
481
535
  }
482
536
  const parsed = PluginPackageJsonSchema.safeParse(rawPkg);
483
537
  if (!parsed.success) {
484
- log.error(
485
- { err: parsed.error, pluginDir },
486
- `package.json at ${pluginDir} failed schema validation: ${parsed.error.message}`,
487
- );
538
+ if (!opts.quiet) {
539
+ log.error(
540
+ { err: parsed.error, pluginDir },
541
+ `package.json at ${pluginDir} failed schema validation: ${parsed.error.message}`,
542
+ );
543
+ }
488
544
  return undefined;
489
545
  }
490
546
  const pkg: PluginPackageJson = parsed.data;
@@ -0,0 +1,101 @@
1
+ /**
2
+ * Enumerate installed plugin directories under the workspace plugins dir.
3
+ *
4
+ * An installed plugin is a non-hidden directory carrying a `package.json`,
5
+ * whose realpath stays under the realpath of the plugins directory. This walk
6
+ * is shared by the plugin source-version collector
7
+ * (`./collect-source-versions.ts`) and the schedule reconciler
8
+ * (`../schedule/plugin-schedule-reconciler.ts`) so both agree on what counts
9
+ * as an installed plugin. Disabled-state and manifest validation are caller
10
+ * concerns: the source collector fingerprints disabled plugins too, while the
11
+ * reconciler skips them and additionally gates on `parsePluginManifest`.
12
+ *
13
+ * The containment half of that definition is exported on its own
14
+ * ({@link isInsidePluginRoot}) for the plugin loader, which applies the same
15
+ * boundary at boot discovery and before it imports a plugin directory, and for
16
+ * the schedule fire-time probe, which applies it before answering that a
17
+ * declaration is available to run.
18
+ */
19
+
20
+ import { existsSync, readdirSync, realpathSync, statSync } from "node:fs";
21
+ import { join, sep } from "node:path";
22
+
23
+ import { getWorkspacePluginsDir } from "../util/platform.js";
24
+
25
+ export interface InstalledPluginDir {
26
+ /** Directory basename: the plugin's install identity. */
27
+ readonly name: string;
28
+ /** Absolute path to the plugin directory. */
29
+ readonly dir: string;
30
+ }
31
+
32
+ /**
33
+ * True when `dir` resolves to a location strictly inside `root`, where `root`
34
+ * is the tree the caller requires `dir` to stay in: an allowed plugin root
35
+ * (the workspace plugins directory, or the standalone workspace hooks
36
+ * directory) for a plugin directory, or a plugin directory for something
37
+ * installed under it.
38
+ *
39
+ * Both sides are resolved before the comparison. Resolving the candidate is
40
+ * what judges a symlinked entry by where it points rather than by where it
41
+ * sits; resolving the root is what keeps a root that itself sits behind a
42
+ * symlinked path component (macOS `/tmp` to `/private/tmp`, a workspace under
43
+ * a linked home) matching its own children. A resolved candidate compared
44
+ * against an unresolved root reports every entry as outside.
45
+ *
46
+ * This is the one containment boundary the plugin loader applies: enumeration
47
+ * here, boot discovery, and the pre-import check before a plugin directory is
48
+ * dynamic-imported (`scanPlugins` and `isAllowedPluginDir` in
49
+ * `./mtime-cache.ts`) all go through it, so a root the loader refuses to
50
+ * activate is never reported as installed either. A link pointing at the root
51
+ * itself is outside the boundary: it aliases the root, not a plugin. The
52
+ * schedule fire-time probe
53
+ * (`../schedule/plugin-schedule-declarations.ts`) applies the same boundary,
54
+ * so such a root cannot be run by a declared schedule either.
55
+ */
56
+ export function isInsidePluginRoot(dir: string, root: string): boolean {
57
+ try {
58
+ return realpathSync(dir).startsWith(realpathSync(root) + sep);
59
+ } catch {
60
+ // Unresolvable (dangling link, races with an uninstall, unreadable): not
61
+ // provably contained, so it is not inside.
62
+ return false;
63
+ }
64
+ }
65
+
66
+ /**
67
+ * List every installed plugin directory, sorted by the underlying readdir
68
+ * order. A missing plugins directory yields `[]`.
69
+ */
70
+ export function listInstalledPluginDirs(): InstalledPluginDir[] {
71
+ const pluginsDir = getWorkspacePluginsDir();
72
+ let entries: string[] = [];
73
+ try {
74
+ entries = readdirSync(pluginsDir);
75
+ } catch {
76
+ // No plugins directory yet, so nothing installed.
77
+ return [];
78
+ }
79
+ const out: InstalledPluginDir[] = [];
80
+ for (const entry of entries) {
81
+ if (entry.startsWith(".")) {
82
+ continue;
83
+ }
84
+ const dir = join(pluginsDir, entry);
85
+ try {
86
+ if (!statSync(dir).isDirectory()) {
87
+ continue;
88
+ }
89
+ } catch {
90
+ continue;
91
+ }
92
+ if (!isInsidePluginRoot(dir, pluginsDir)) {
93
+ continue;
94
+ }
95
+ if (!existsSync(join(dir, "package.json"))) {
96
+ continue;
97
+ }
98
+ out.push({ name: entry, dir });
99
+ }
100
+ return out;
101
+ }
@@ -0,0 +1,343 @@
1
+ /**
2
+ * Plugin-contributed MCP servers.
3
+ *
4
+ * A plugin declares MCP servers in a root `mcp.json`, per the Agent
5
+ * Plugins 1.0.0 specification:
6
+ *
7
+ * {
8
+ * "$schema": "https://agent-plugins.org/schemas/1.0.0/mcp.schema.json",
9
+ * "mcpServers": {
10
+ * "unabyss": { "type": "streamable-http", "url": "https://mcp.unabyss.com" }
11
+ * }
12
+ * }
13
+ *
14
+ * This module reads those files and projects each entry onto the
15
+ * assistant's own `McpServerConfig` shape, so a plugin-declared server can
16
+ * flow through the same surfaces as one configured in the workspace
17
+ * `config.json`.
18
+ *
19
+ * Failure isolation follows the spec: an invalid top-level `mcp.json`
20
+ * disables MCP for that plugin only, and an invalid individual server
21
+ * disables only that entry. Neither ever throws, because a malformed file
22
+ * in one plugin must not remove another plugin's servers from a listing.
23
+ *
24
+ * Authentication is deliberately absent. Agent Plugins 1.0.0 defines no
25
+ * portable OAuth or credential-reference fields; authentication is
26
+ * client-managed, and any `headers` in the file are literal package data.
27
+ * A plugin therefore cannot ship a credential, and the assistant's own
28
+ * credential store stays the only place secrets live.
29
+ *
30
+ * Consumers must not resolve a plugin server's id against the
31
+ * `mcp:<serverId>:*` credential namespace. Those keys belong to
32
+ * workspace-configured servers, and a plugin controls both its server key
33
+ * and its URL, so honoring them for a plugin-declared server would send a
34
+ * workspace credential to an endpoint the plugin chose.
35
+ */
36
+
37
+ import { existsSync, readFileSync } from "node:fs";
38
+ import { join } from "node:path";
39
+
40
+ import { z } from "zod";
41
+
42
+ import {
43
+ type AllPluginInfo,
44
+ listAllPlugins,
45
+ type ListInstalledPluginsOptions,
46
+ } from "../cli/lib/list-installed-plugins.js";
47
+ import type { McpServerConfig, McpTransport } from "../config/schemas/mcp.js";
48
+ import { getLogger } from "../util/logger.js";
49
+
50
+ const log = getLogger("plugin-mcp-servers");
51
+
52
+ /** Filename a plugin declares its MCP servers in, at the plugin root. */
53
+ export const PLUGIN_MCP_MANIFEST = "mcp.json";
54
+
55
+ /**
56
+ * Risk level assigned to a plugin-declared server. `mcp.json` has no risk
57
+ * field, since the spec defines none, so the assistant's own default
58
+ * applies. `high` matches `McpServerConfigSchema` and is the safe
59
+ * direction: a plugin cannot lower the approval bar for the tools it
60
+ * introduces.
61
+ */
62
+ const PLUGIN_SERVER_DEFAULT_RISK = "high" as const;
63
+
64
+ /** Matches the `maxTools` default in `McpServerConfigSchema`. */
65
+ const PLUGIN_SERVER_DEFAULT_MAX_TOOLS = 20;
66
+
67
+ // ---------------------------------------------------------------------------
68
+ // Wire schema (Agent Plugins 1.0.0)
69
+ // ---------------------------------------------------------------------------
70
+
71
+ const PluginStdioServerSchema = z.object({
72
+ type: z.literal("stdio"),
73
+ command: z.string().min(1),
74
+ args: z.array(z.string()).optional(),
75
+ env: z.record(z.string(), z.string()).optional(),
76
+ cwd: z.string().optional(),
77
+ });
78
+
79
+ const PluginHttpServerSchema = z.object({
80
+ type: z.enum(["streamable-http", "sse"]),
81
+ url: z.string().min(1),
82
+ headers: z.record(z.string(), z.string()).optional(),
83
+ });
84
+
85
+ const PluginMcpServerSchema = z.discriminatedUnion("type", [
86
+ PluginStdioServerSchema,
87
+ PluginHttpServerSchema,
88
+ ]);
89
+
90
+ const PluginMcpManifestSchema = z.object({
91
+ mcpServers: z.record(z.string(), z.unknown()),
92
+ });
93
+
94
+ // ---------------------------------------------------------------------------
95
+ // Public shape
96
+ // ---------------------------------------------------------------------------
97
+
98
+ /** One MCP server declared by one plugin. */
99
+ export interface PluginMcpServer {
100
+ /** Server id in the assistant's namespace. See {@link buildServerId}. */
101
+ readonly id: string;
102
+ /** Directory name of the plugin that declared it. */
103
+ readonly pluginName: string;
104
+ /** Key this server appears under in the plugin's `mcp.json`. */
105
+ readonly serverKey: string;
106
+ /** Projected onto the assistant's own server-config shape. */
107
+ readonly config: McpServerConfig;
108
+ }
109
+
110
+ /** A plugin's `mcp.json` that could not be used, in whole or in part. */
111
+ export interface PluginMcpIssue {
112
+ readonly pluginName: string;
113
+ /** Absent when the whole manifest is unusable rather than one entry. */
114
+ readonly serverKey?: string;
115
+ readonly message: string;
116
+ }
117
+
118
+ export interface PluginMcpServersResult {
119
+ readonly servers: readonly PluginMcpServer[];
120
+ readonly issues: readonly PluginMcpIssue[];
121
+ }
122
+
123
+ /**
124
+ * Server id for a plugin-declared server.
125
+ *
126
+ * Plugin servers share one namespace with workspace servers and with each
127
+ * other, so the plugin name is the qualifier. The redundant case is
128
+ * collapsed: a plugin named `unabyss` whose server key is also `unabyss`
129
+ * yields `unabyss`, not `unabyss__unabyss`, which matters because the id
130
+ * is embedded in every tool name the server contributes
131
+ * (`mcp__<id>__<tool>`).
132
+ */
133
+ export function buildServerId(pluginName: string, serverKey: string): string {
134
+ return pluginName === serverKey ? pluginName : `${pluginName}__${serverKey}`;
135
+ }
136
+
137
+ /**
138
+ * Expand the two path variables the Agent Plugins spec defines for stdio
139
+ * servers. They interpolate textually in `args`, `env` values, and `cwd`
140
+ * only, never in `command`, a URL, or a header, so a manifest cannot use
141
+ * them to build the executable path itself.
142
+ */
143
+ export function interpolatePluginPaths(
144
+ value: string,
145
+ pluginRoot: string,
146
+ pluginData: string,
147
+ ): string {
148
+ return value
149
+ .replaceAll("${PLUGIN_ROOT}", pluginRoot)
150
+ .replaceAll("${PLUGIN_DATA}", pluginData);
151
+ }
152
+
153
+ /**
154
+ * Whether a directory is a plugin the runtime would actually load.
155
+ *
156
+ * `listAllPlugins` is an inventory of directories and reports a malformed
157
+ * entry rather than dropping it, so it happily returns a directory with no
158
+ * usable `package.json`. The runtime loader rejects those in
159
+ * `parsePluginManifest`: the manifest must parse and carry a non-empty
160
+ * `name`. Applying the same gate here keeps `mcp.json` from being honored
161
+ * for a directory that will never load as a plugin.
162
+ */
163
+ export function hasLoadableManifest(plugin: AllPluginInfo): boolean {
164
+ const name = plugin.packageJson?.name;
165
+ return typeof name === "string" && name.length > 0;
166
+ }
167
+
168
+ // ---------------------------------------------------------------------------
169
+ // Reading
170
+ // ---------------------------------------------------------------------------
171
+
172
+ /**
173
+ * Read every installed plugin's `mcp.json` and return the servers they
174
+ * declare, plus anything that could not be read.
175
+ *
176
+ * A disabled plugin is skipped entirely: `.disabled` means the plugin
177
+ * contributes no hooks and no tools, and an MCP server it declares is no
178
+ * different.
179
+ */
180
+ export function readPluginMcpServers(
181
+ opts: ListInstalledPluginsOptions = {},
182
+ ): PluginMcpServersResult {
183
+ const servers: PluginMcpServer[] = [];
184
+ const issues: PluginMcpIssue[] = [];
185
+ const seen = new Map<string, string>();
186
+
187
+ let plugins: readonly AllPluginInfo[];
188
+ try {
189
+ plugins = listAllPlugins(opts);
190
+ } catch (err) {
191
+ // The plugins directory being unreadable is not a reason to fail a
192
+ // listing that also has workspace-configured servers in it.
193
+ log.warn({ err }, "Could not enumerate plugins for MCP declarations");
194
+ return { servers: [], issues: [] };
195
+ }
196
+
197
+ for (const plugin of plugins) {
198
+ if (plugin.disabled) {
199
+ continue;
200
+ }
201
+ const manifestPath = join(plugin.target, PLUGIN_MCP_MANIFEST);
202
+ if (!existsSync(manifestPath)) {
203
+ continue;
204
+ }
205
+
206
+ if (!hasLoadableManifest(plugin)) {
207
+ issues.push({
208
+ pluginName: plugin.name,
209
+ message: `${PLUGIN_MCP_MANIFEST} ignored: package.json is missing, unparseable, or has no name, so the runtime will not load this directory as a plugin`,
210
+ });
211
+ continue;
212
+ }
213
+
214
+ const parsed = parseManifest(manifestPath);
215
+ if ("error" in parsed) {
216
+ issues.push({ pluginName: plugin.name, message: parsed.error });
217
+ continue;
218
+ }
219
+
220
+ for (const [serverKey, raw] of Object.entries(parsed.mcpServers)) {
221
+ if (serverKey.trim().length === 0) {
222
+ issues.push({
223
+ pluginName: plugin.name,
224
+ message: "server key must be a non-empty string; skipping",
225
+ });
226
+ continue;
227
+ }
228
+
229
+ const entry = PluginMcpServerSchema.safeParse(raw);
230
+ if (!entry.success) {
231
+ issues.push({
232
+ pluginName: plugin.name,
233
+ serverKey,
234
+ message: `invalid server entry: ${entry.error.issues
235
+ .map((i) => `${i.path.join(".") || "<root>"}: ${i.message}`)
236
+ .join("; ")}`,
237
+ });
238
+ continue;
239
+ }
240
+
241
+ const id = buildServerId(plugin.name, serverKey);
242
+ const previous = seen.get(id);
243
+ if (previous) {
244
+ // Two plugins claiming one id would silently shadow each other, and
245
+ // the loser's tools would just be missing. Report both instead.
246
+ issues.push({
247
+ pluginName: plugin.name,
248
+ serverKey,
249
+ message: `server id "${id}" is already declared by plugin "${previous}"; skipping`,
250
+ });
251
+ continue;
252
+ }
253
+ seen.set(id, plugin.name);
254
+
255
+ // The spec allows `cwd` on a stdio server; the assistant's transport
256
+ // has no such field. Dropping it silently would leave a server that
257
+ // resolves its relative paths against the wrong directory and fails
258
+ // for no visible reason, so say so.
259
+ if (entry.data.type === "stdio" && entry.data.cwd) {
260
+ issues.push({
261
+ pluginName: plugin.name,
262
+ serverKey,
263
+ message: `"cwd" is not supported by this host and was ignored; the server runs in the daemon's working directory`,
264
+ });
265
+ }
266
+
267
+ servers.push({
268
+ id,
269
+ pluginName: plugin.name,
270
+ serverKey,
271
+ config: {
272
+ transport: projectTransport(entry.data, plugin.target),
273
+ enabled: true,
274
+ defaultRiskLevel: PLUGIN_SERVER_DEFAULT_RISK,
275
+ maxTools: PLUGIN_SERVER_DEFAULT_MAX_TOOLS,
276
+ },
277
+ });
278
+ }
279
+ }
280
+
281
+ return { servers, issues };
282
+ }
283
+
284
+ function parseManifest(
285
+ path: string,
286
+ ): { mcpServers: Record<string, unknown> } | { error: string } {
287
+ let raw: string;
288
+ try {
289
+ raw = readFileSync(path, "utf8");
290
+ } catch (err) {
291
+ return {
292
+ error: `${PLUGIN_MCP_MANIFEST} unreadable: ${err instanceof Error ? err.message : String(err)}`,
293
+ };
294
+ }
295
+
296
+ let json: unknown;
297
+ try {
298
+ json = JSON.parse(raw);
299
+ } catch (err) {
300
+ return {
301
+ error: `${PLUGIN_MCP_MANIFEST} invalid JSON: ${err instanceof Error ? err.message : String(err)}`,
302
+ };
303
+ }
304
+
305
+ const manifest = PluginMcpManifestSchema.safeParse(json);
306
+ if (!manifest.success) {
307
+ return {
308
+ error: `${PLUGIN_MCP_MANIFEST} is missing a valid "mcpServers" object`,
309
+ };
310
+ }
311
+ return { mcpServers: manifest.data.mcpServers };
312
+ }
313
+
314
+ /**
315
+ * Project one spec-shaped entry onto the assistant's transport union. The
316
+ * two vocabularies already agree on type names and required fields, so this
317
+ * is a field copy plus path interpolation for the stdio case.
318
+ */
319
+ function projectTransport(
320
+ entry: z.infer<typeof PluginMcpServerSchema>,
321
+ pluginRoot: string,
322
+ ): McpTransport {
323
+ if (entry.type === "stdio") {
324
+ const pluginData = join(pluginRoot, "data");
325
+ const expand = (v: string): string =>
326
+ interpolatePluginPaths(v, pluginRoot, pluginData);
327
+ return {
328
+ type: "stdio",
329
+ command: entry.command,
330
+ args: (entry.args ?? []).map(expand),
331
+ ...(entry.env && {
332
+ env: Object.fromEntries(
333
+ Object.entries(entry.env).map(([k, v]) => [k, expand(v)]),
334
+ ),
335
+ }),
336
+ };
337
+ }
338
+ return {
339
+ type: entry.type,
340
+ url: entry.url,
341
+ ...(entry.headers && { headers: entry.headers }),
342
+ };
343
+ }
@@ -31,13 +31,7 @@
31
31
  * each hook through the hook loader on demand.
32
32
  */
33
33
 
34
- import {
35
- existsSync,
36
- readdirSync,
37
- readFileSync,
38
- realpathSync,
39
- statSync,
40
- } from "node:fs";
34
+ import { existsSync, readdirSync, readFileSync, statSync } from "node:fs";
41
35
  import { join } from "node:path";
42
36
 
43
37
  import type { Logger } from "pino";
@@ -72,6 +66,7 @@ import {
72
66
  listSurfaceDir,
73
67
  parsePluginManifest,
74
68
  } from "./external-plugin-loader.js";
69
+ import { isInsidePluginRoot } from "./installed-plugin-dirs.js";
75
70
  import { snapshotPluginSource } from "./source-fingerprint.js";
76
71
  import type { PluginSourceVersion } from "./source-versions.js";
77
72
  import {
@@ -293,6 +288,18 @@ export async function reconcilePluginSourcesNow(): Promise<void> {
293
288
  } catch (err) {
294
289
  log.error({ err }, "imperative plugin reconcile failed");
295
290
  }
291
+ // Converge plugin-declared schedules against the plugin set this apply
292
+ // just settled, so install/uninstall/upgrade arm and disarm rows in the
293
+ // same poke. Imported lazily to keep the notification pipeline out of
294
+ // this module's static graph (sidecar workers import it for hook reads).
295
+ // Self-contained: never throws and checks DB readiness itself.
296
+ try {
297
+ const { reconcilePluginSchedules } =
298
+ await import("../schedule/plugin-schedule-reconciler.js");
299
+ await reconcilePluginSchedules();
300
+ } catch (err) {
301
+ log.error({ err }, "plugin schedule reconcile failed");
302
+ }
296
303
  })().finally(() => {
297
304
  reconcileInFlight = null;
298
305
  });
@@ -306,32 +313,26 @@ export async function reconcilePluginSourcesNow(): Promise<void> {
306
313
  * never dynamically imports code from outside the designated plugin roots,
307
314
  * regardless of what the collector walked.
308
315
  *
309
- * Uses `realpathSync` to resolve symlinks before the prefix check, so a
310
- * symlinked path that looks like it's under the plugins dir but points
311
- * elsewhere is rejected.
316
+ * Containment runs through {@link isInsidePluginRoot}, which resolves the
317
+ * candidate and the root, so a symlinked path that looks like it's under the
318
+ * plugins dir but points elsewhere is rejected while a plugins dir reached
319
+ * through a symlinked path component still accepts its own children.
312
320
  */
313
321
  function isAllowedPluginDir(
314
322
  dir: string,
315
323
  pluginsDir: string,
316
324
  hooksDir: string,
317
325
  ): boolean {
318
- let resolved: string;
319
- try {
320
- resolved = realpathSync(dir);
321
- } catch {
322
- // Directory doesn't exist or is inaccessible — allow it through so
323
- // bringUpPlugin/parsePluginManifest can log the normal failure. The
326
+ if (!existsSync(dir)) {
327
+ // Directory doesn't exist or is inaccessible: allow it through so
328
+ // bringUpPlugin/parsePluginManifest can log the normal failure, and so a
329
+ // directory that just went away still reaches its teardown branch. The
324
330
  // danger is importing code from an unexpected location, not a missing
325
331
  // directory.
326
332
  return true;
327
333
  }
328
- const normalizedPluginsDir = pluginsDir + "/";
329
- const normalizedHooksDir = hooksDir + "/";
330
334
  return (
331
- resolved === pluginsDir ||
332
- resolved.startsWith(normalizedPluginsDir) ||
333
- resolved === hooksDir ||
334
- resolved.startsWith(normalizedHooksDir)
335
+ isInsidePluginRoot(dir, pluginsDir) || isInsidePluginRoot(dir, hooksDir)
335
336
  );
336
337
  }
337
338
 
@@ -754,6 +755,17 @@ async function scanPlugins(): Promise<void> {
754
755
  } catch {
755
756
  continue;
756
757
  }
758
+ // Judge the entry by where it resolves. A symlinked plugin root pointing
759
+ // out of the plugins directory is not an install, and activating one at
760
+ // boot would run code that enumeration and the reload path both refuse to
761
+ // touch.
762
+ if (!isInsidePluginRoot(pluginDir, pluginsDir)) {
763
+ log.warn(
764
+ { pluginDir },
765
+ "plugin root resolves outside the plugins directory, skipping",
766
+ );
767
+ continue;
768
+ }
757
769
  if (!existsSync(join(pluginDir, "package.json"))) {
758
770
  continue;
759
771
  }