@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
@@ -38,6 +38,9 @@
38
38
  * `plugins:list` tag via the canonical resource-sync publisher (enable and
39
39
  * disable emit the SAME invalidation)
40
40
  * - Threads `x-vellum-client-id` into the published event's `originClientId`
41
+ * - Pokes the plugin-declared schedule reconcile on a successful toggle (and
42
+ * not on a failed one), so the plugin's rows disarm / re-arm immediately
43
+ * instead of at the reconciler's next backstop sweep
41
44
  * - A broadcast failure does not fail a successful toggle (the publisher
42
45
  * swallows hub errors)
43
46
  * - Maps `InvalidPluginNameError` → BadRequestError (400)
@@ -106,6 +109,7 @@ import {
106
109
  } from "../../../cli/lib/uninstall-plugin.js";
107
110
  import {
108
111
  PluginMergeBaselineError,
112
+ PluginNotCuratedError,
109
113
  PluginNotUpgradableError,
110
114
  type PluginUpgradeResult,
111
115
  type UpgradePluginDeps,
@@ -249,6 +253,7 @@ const upgradeSpy = mock(
249
253
 
250
254
  mock.module("../../../cli/lib/upgrade-plugin.js", () => ({
251
255
  PluginMergeBaselineError,
256
+ PluginNotCuratedError,
252
257
  PluginNotUpgradableError,
253
258
  upgradePlugin: upgradeSpy,
254
259
  }));
@@ -311,6 +316,15 @@ mock.module("../../../cli/lib/toggle-plugin.js", () => ({
311
316
  enablePlugin: enablePluginSpy,
312
317
  }));
313
318
 
319
+ // Spy on the plugin-schedule reconcile the toggle handlers poke. The handlers
320
+ // import it lazily at call time, so this mock intercepts the dynamic import
321
+ // and keeps the reconciler's persistence graph out of the test.
322
+ const reconcilePluginSchedulesSpy = mock(() => Promise.resolve());
323
+
324
+ mock.module("../../../schedule/plugin-schedule-reconciler.js", () => ({
325
+ reconcilePluginSchedules: reconcilePluginSchedulesSpy,
326
+ }));
327
+
314
328
  // Spy on broadcastMessage so we can assert the sync_changed invalidation the
315
329
  // enable/disable handlers emit. The handlers publish through the canonical
316
330
  // `publishPluginsChanged` → `publishSyncInvalidation` path (both left real), so
@@ -1883,7 +1897,14 @@ function inspection(
1883
1897
  remoteError: overrides.remoteError ?? null,
1884
1898
  surfaces:
1885
1899
  overrides.surfaces === undefined
1886
- ? { skills: [], hooks: ["post-model-call"], tools: [] }
1900
+ ? {
1901
+ skills: [],
1902
+ hooks: ["post-model-call"],
1903
+ tools: [],
1904
+ schedules: [
1905
+ { name: "digest", cadence: "0 9 * * *", mode: "execute" },
1906
+ ],
1907
+ }
1887
1908
  : overrides.surfaces,
1888
1909
  };
1889
1910
  }
@@ -1913,6 +1934,26 @@ describe("GET /v1/plugins/:name/inspect", () => {
1913
1934
  expect(inspectSpy.mock.calls[0]?.[0]).toEqual({ name: "level-up" });
1914
1935
  });
1915
1936
 
1937
+ test("the inspect wire schema carries the schedules surface", async () => {
1938
+ // GIVEN an inspection whose surfaces declare a schedule
1939
+ inspectSpy.mockImplementation(async () => inspection());
1940
+ const result = await invokeInspect({ pathParams: { name: "level-up" } });
1941
+
1942
+ // WHEN the handler result is parsed through the route's response schema
1943
+ // (zod strips undeclared keys, so an omission would drop the field)
1944
+ const route = PLUGINS_ROUTES.find(
1945
+ (r) => r.operationId === "plugins_inspect",
1946
+ )!;
1947
+ const parsed = (
1948
+ route.responseBody as { parse: (v: unknown) => PluginInspection }
1949
+ ).parse(result);
1950
+
1951
+ // THEN the declared schedules survive the wire contract
1952
+ expect(parsed.surfaces?.schedules).toEqual([
1953
+ { name: "digest", cadence: "0 9 * * *", mode: "execute" },
1954
+ ]);
1955
+ });
1956
+
1916
1957
  test("a captured marketplace error is returned as 200 with remote-unavailable, not thrown", async () => {
1917
1958
  // GIVEN the catalog was unreachable but a local copy exists, so the lib
1918
1959
  // returns a status rather than throwing
@@ -2088,6 +2129,7 @@ describe("POST /v1/plugins/:name/upgrade", () => {
2088
2129
  name: "level-up",
2089
2130
  dryRun: false,
2090
2131
  strategy: undefined,
2132
+ marketplaceOnly: false,
2091
2133
  });
2092
2134
  });
2093
2135
 
@@ -2109,6 +2151,7 @@ describe("POST /v1/plugins/:name/upgrade", () => {
2109
2151
  name: "level-up",
2110
2152
  dryRun: undefined,
2111
2153
  strategy: "ours",
2154
+ marketplaceOnly: false,
2112
2155
  });
2113
2156
  });
2114
2157
 
@@ -2136,9 +2179,94 @@ describe("POST /v1/plugins/:name/upgrade", () => {
2136
2179
  name: "level-up",
2137
2180
  dryRun: undefined,
2138
2181
  strategy: "assistant",
2182
+ marketplaceOnly: false,
2139
2183
  });
2140
2184
  });
2141
2185
 
2186
+ test("refuses a concurrent upgrade of the same plugin with ConflictError (409)", async () => {
2187
+ // Two upgrades of one plugin derive the same staging path in this
2188
+ // process, so the second would delete the first's tree mid-swap. It is
2189
+ // refused instead \u2014 the monitor's auto-update sweep and a user clicking
2190
+ // Upgrade can now land at the same moment.
2191
+ let releaseFirst: () => void = () => {};
2192
+ const firstStarted = Promise.withResolvers<void>();
2193
+ upgradeSpy.mockImplementation(async () => {
2194
+ firstStarted.resolve();
2195
+ await new Promise<void>((resolve) => {
2196
+ releaseFirst = resolve;
2197
+ });
2198
+ return upgradeResult();
2199
+ });
2200
+
2201
+ const first = invokeUpgrade({ pathParams: { name: "level-up" } });
2202
+ await firstStarted.promise;
2203
+
2204
+ await expect(
2205
+ invokeUpgrade({ pathParams: { name: "level-up" } }),
2206
+ ).rejects.toBeInstanceOf(ConflictError);
2207
+
2208
+ releaseFirst();
2209
+ await expect(first).resolves.toMatchObject({ outcome: "upgraded" });
2210
+
2211
+ // The guard is released once the first settles, so the next one runs.
2212
+ upgradeSpy.mockImplementation(async () => upgradeResult());
2213
+ await expect(
2214
+ invokeUpgrade({ pathParams: { name: "level-up" } }),
2215
+ ).resolves.toMatchObject({ outcome: "upgraded" });
2216
+ });
2217
+
2218
+ test("a different plugin is not blocked by an in-flight upgrade", async () => {
2219
+ let releaseFirst: () => void = () => {};
2220
+ const firstStarted = Promise.withResolvers<void>();
2221
+ upgradeSpy.mockImplementation(async (opts) => {
2222
+ if (opts.name === "level-up") {
2223
+ firstStarted.resolve();
2224
+ await new Promise<void>((resolve) => {
2225
+ releaseFirst = resolve;
2226
+ });
2227
+ }
2228
+ return upgradeResult({ name: opts.name });
2229
+ });
2230
+
2231
+ const first = invokeUpgrade({ pathParams: { name: "level-up" } });
2232
+ await firstStarted.promise;
2233
+
2234
+ await expect(
2235
+ invokeUpgrade({ pathParams: { name: "other-plugin" } }),
2236
+ ).resolves.toMatchObject({ name: "other-plugin" });
2237
+
2238
+ releaseFirst();
2239
+ await first;
2240
+ });
2241
+
2242
+ test("a dry run neither takes nor respects the in-flight guard", async () => {
2243
+ // Dry runs return before the swap boundary, so they never stage.
2244
+ let releaseFirst: () => void = () => {};
2245
+ const firstStarted = Promise.withResolvers<void>();
2246
+ upgradeSpy.mockImplementation(async (opts) => {
2247
+ if (!opts.dryRun) {
2248
+ firstStarted.resolve();
2249
+ await new Promise<void>((resolve) => {
2250
+ releaseFirst = resolve;
2251
+ });
2252
+ }
2253
+ return upgradeResult();
2254
+ });
2255
+
2256
+ const first = invokeUpgrade({ pathParams: { name: "level-up" } });
2257
+ await firstStarted.promise;
2258
+
2259
+ await expect(
2260
+ invokeUpgrade({
2261
+ pathParams: { name: "level-up" },
2262
+ body: { dryRun: true },
2263
+ }),
2264
+ ).resolves.toMatchObject({ outcome: "upgraded" });
2265
+
2266
+ releaseFirst();
2267
+ await first;
2268
+ });
2269
+
2142
2270
  test("PluginMergeBaselineError \u2192 ConflictError (409)", async () => {
2143
2271
  // A merge strategy whose install-time baseline can't be reconstructed: a
2144
2272
  // well-formed request that isn't actionable in the current state.
@@ -2177,6 +2305,10 @@ describe("POST /v1/plugins/:name/upgrade", () => {
2177
2305
  expect(upgradeSpy.mock.calls[0]?.[0]).toEqual({
2178
2306
  name: "level-up",
2179
2307
  dryRun: undefined,
2308
+ strategy: undefined,
2309
+ // Unlike dryRun, an absent flag here is a definite "no": the untrusted
2310
+ // direct path stays available unless a caller opts out of it.
2311
+ marketplaceOnly: false,
2180
2312
  });
2181
2313
  });
2182
2314
 
@@ -2220,6 +2352,41 @@ describe("POST /v1/plugins/:name/upgrade", () => {
2220
2352
  ).rejects.toBeInstanceOf(ConflictError);
2221
2353
  });
2222
2354
 
2355
+ test("marketplaceOnly is forwarded so the lib enforces it, not the caller", async () => {
2356
+ // The lib re-inspects the catalog inside the call, so the flag has to
2357
+ // travel with the request: a caller's own pre-check cannot bind a decision
2358
+ // the lib makes afterwards.
2359
+ upgradeSpy.mockImplementation(async () => upgradeResult());
2360
+
2361
+ await invokeUpgrade({
2362
+ pathParams: { name: "level-up" },
2363
+ body: { marketplaceOnly: true },
2364
+ });
2365
+
2366
+ expect(upgradeSpy.mock.calls[0]?.[0]).toEqual({
2367
+ name: "level-up",
2368
+ dryRun: undefined,
2369
+ strategy: undefined,
2370
+ marketplaceOnly: true,
2371
+ });
2372
+ });
2373
+
2374
+ test("PluginNotCuratedError → ConflictError (409)", async () => {
2375
+ // A marketplaceOnly caller asked about an install the catalog does not
2376
+ // claim. Retrying changes nothing: only a human upgrading it without the
2377
+ // flag can move it.
2378
+ upgradeSpy.mockImplementation(async () => {
2379
+ throw new PluginNotCuratedError("level-up");
2380
+ });
2381
+
2382
+ await expect(
2383
+ invokeUpgrade({
2384
+ pathParams: { name: "level-up" },
2385
+ body: { marketplaceOnly: true },
2386
+ }),
2387
+ ).rejects.toBeInstanceOf(ConflictError);
2388
+ });
2389
+
2223
2390
  test("PluginNotFoundError → NotFoundError (404)", async () => {
2224
2391
  upgradeSpy.mockImplementation(async () => {
2225
2392
  throw new PluginNotFoundError("level-up", "main", "vellum-ai/level-up");
@@ -2414,6 +2581,21 @@ function invokeDisable(args: RouteHandlerArgs = {}): { ok: boolean } {
2414
2581
  return disableHandler(args) as { ok: boolean };
2415
2582
  }
2416
2583
 
2584
+ /**
2585
+ * Wait for the toggle handlers' fire-and-forget reconcile poke to land. The
2586
+ * lazy `import()` resolves off the microtask queue, so the assertion cannot
2587
+ * run on the handler's synchronous return.
2588
+ */
2589
+ async function waitForScheduleReconcile(): Promise<void> {
2590
+ for (let i = 0; i < 50; i++) {
2591
+ if (reconcilePluginSchedulesSpy.mock.calls.length > 0) {
2592
+ return;
2593
+ }
2594
+ await new Promise((resolve) => setTimeout(resolve, 2));
2595
+ }
2596
+ throw new Error("plugin schedule reconcile was never triggered");
2597
+ }
2598
+
2417
2599
  /** Assert the spy received exactly one sync_changed carrying `plugins:list`. */
2418
2600
  function expectPluginsListBroadcast(): void {
2419
2601
  expect(broadcastMessageSpy.mock.calls).toHaveLength(1);
@@ -2426,6 +2608,15 @@ describe("POST /v1/plugins/:name/enable", () => {
2426
2608
  beforeEach(() => {
2427
2609
  enablePluginSpy.mockReset();
2428
2610
  broadcastMessageSpy.mockReset();
2611
+ reconcilePluginSchedulesSpy.mockClear();
2612
+ });
2613
+
2614
+ test("reconciles plugin-declared schedules so the plugin's rows re-arm now", async () => {
2615
+ enablePluginSpy.mockImplementation((name) => toggleResult(name, "enable"));
2616
+
2617
+ invokeEnable({ pathParams: { name: "simple-memory" } });
2618
+
2619
+ await waitForScheduleReconcile();
2429
2620
  });
2430
2621
 
2431
2622
  test("enables the plugin and broadcasts sync_changed(plugins:list)", () => {
@@ -2504,6 +2695,31 @@ describe("POST /v1/plugins/:name/disable", () => {
2504
2695
  beforeEach(() => {
2505
2696
  disablePluginSpy.mockReset();
2506
2697
  broadcastMessageSpy.mockReset();
2698
+ reconcilePluginSchedulesSpy.mockClear();
2699
+ });
2700
+
2701
+ test("reconciles plugin-declared schedules so the plugin's rows disarm now", async () => {
2702
+ disablePluginSpy.mockImplementation((name) =>
2703
+ toggleResult(name, "disable"),
2704
+ );
2705
+
2706
+ invokeDisable({ pathParams: { name: "simple-memory" } });
2707
+
2708
+ // Without this poke the rows stay armed until the reconciler's next
2709
+ // backstop sweep.
2710
+ await waitForScheduleReconcile();
2711
+ });
2712
+
2713
+ test("a failed toggle does not poke the schedule reconcile", async () => {
2714
+ disablePluginSpy.mockImplementation((name) => {
2715
+ throw new PluginDirectoryNotFoundError(name);
2716
+ });
2717
+
2718
+ expect(() => invokeDisable({ pathParams: { name: "ghost" } })).toThrow(
2719
+ NotFoundError,
2720
+ );
2721
+ await new Promise((resolve) => setTimeout(resolve, 10));
2722
+ expect(reconcilePluginSchedulesSpy).not.toHaveBeenCalled();
2507
2723
  });
2508
2724
 
2509
2725
  test("disables the plugin and broadcasts sync_changed(plugins:list)", () => {
@@ -394,4 +394,68 @@ describe("POST /v1/question-response", () => {
394
394
  // Pending interaction left in place.
395
395
  expect(pendingInteractions.get("req-legacy-multi")).toBeDefined();
396
396
  });
397
+
398
+ // A gone interaction must read as 404 for every body shape. Clients treat
399
+ // 404 as the terminal signal that retires a stale card; a 400 leaves the
400
+ // card on screen and surfaces a body-validation string the user cannot act
401
+ // on. The legacy shapes are the ones that matter here: the web client sends
402
+ // them for every single-question answer, which is the common prompt.
403
+ test("legacy option against a gone interaction is 404, not a validation 400", async () => {
404
+ let thrown: unknown;
405
+ try {
406
+ await call({
407
+ body: { requestId: "req-gone", kind: "option", optionId: "yes" },
408
+ });
409
+ } catch (err) {
410
+ thrown = err;
411
+ }
412
+ expect(thrown).toBeInstanceOf(NotFoundError);
413
+ });
414
+
415
+ test("legacy free-text against a gone interaction is 404, not a validation 400", async () => {
416
+ let thrown: unknown;
417
+ try {
418
+ await call({
419
+ body: { requestId: "req-gone", kind: "free_text", text: "maybe" },
420
+ });
421
+ } catch (err) {
422
+ thrown = err;
423
+ }
424
+ expect(thrown).toBeInstanceOf(NotFoundError);
425
+ });
426
+
427
+ test("close against a gone interaction is 404", async () => {
428
+ let thrown: unknown;
429
+ try {
430
+ await call({ body: { requestId: "req-gone", kind: "close" } });
431
+ } catch (err) {
432
+ thrown = err;
433
+ }
434
+ expect(thrown).toBeInstanceOf(NotFoundError);
435
+ });
436
+
437
+ test("legacy option against a confirmation requestId is 404, not a validation 400", async () => {
438
+ // Cross-talk safety has to hold for the legacy shapes too: the wrong-kind
439
+ // interaction carries no question metadata for the shim to read.
440
+ pendingInteractions.register("req-confirm-legacy", {
441
+ conversationId: "conv-1",
442
+ kind: "confirmation",
443
+ });
444
+
445
+ let thrown: unknown;
446
+ try {
447
+ await call({
448
+ body: {
449
+ requestId: "req-confirm-legacy",
450
+ kind: "option",
451
+ optionId: "y",
452
+ },
453
+ });
454
+ } catch (err) {
455
+ thrown = err;
456
+ }
457
+ expect(thrown).toBeInstanceOf(NotFoundError);
458
+ // The confirmation is left intact rather than consumed.
459
+ expect(pendingInteractions.get("req-confirm-legacy")).toBeDefined();
460
+ });
397
461
  });
@@ -413,6 +413,26 @@ describe("getRetrospectiveConfig handler", () => {
413
413
  expect(result.success).toBe(true);
414
414
  });
415
415
 
416
+ test("available but not enabled when only the retrospective switch is off", async () => {
417
+ writeFileSync(
418
+ configPath,
419
+ JSON.stringify(
420
+ { memory: { retrospective: { enabled: false } } },
421
+ null,
422
+ 2,
423
+ ) + "\n",
424
+ );
425
+
426
+ const handler = findHandler("getRetrospectiveConfig");
427
+ const result = (await handler({})) as ConfigResponse;
428
+
429
+ // `available` stays true so the Settings row still renders. It reads
430
+ // "Paused" rather than disappearing as it would with memory off.
431
+ expect(result.available).toBe(true);
432
+ expect(result.enabled).toBe(false);
433
+ expect(result.success).toBe(true);
434
+ });
435
+
416
436
  test("stays available when memory v2 is disabled (retrospectives do not gate on v2)", async () => {
417
437
  writeFileSync(
418
438
  configPath,
@@ -0,0 +1,116 @@
1
+ /**
2
+ * Tests for the `getSkillHistory` route's current-resource boundary.
3
+ *
4
+ * The service layer reads retained git history, which outlives the skill it
5
+ * describes: a deleted skill's commits stay in the workspace repository
6
+ * forever. The route is therefore the only place that can decide whether an id
7
+ * still names something, and it must reach the same answer as the sibling file
8
+ * routes rather than serving history for a resource the rest of the API
9
+ * reports as gone.
10
+ *
11
+ * Both collaborators are mocked, because what is under test is the ordering
12
+ * between them: the existence check has to gate the history read, not run
13
+ * alongside it.
14
+ */
15
+
16
+ import { beforeEach, describe, expect, mock, test } from "bun:test";
17
+
18
+ const skillExistsLocallyMock = mock((_id: string): boolean => true);
19
+ const getSkillHistoryMock = mock(async (_id: string, _opts?: unknown) => ({
20
+ skillId: _id,
21
+ revisions: [{ id: "abc1234", changedAt: "", files: ["SKILL.md"], diff: "" }],
22
+ truncatedByCompaction: false,
23
+ }));
24
+
25
+ mock.module("../../../daemon/handlers/skills.js", () => ({
26
+ skillExistsLocally: skillExistsLocallyMock,
27
+ // The route module imports the whole handler surface; the rest is unused
28
+ // here and only needs to exist.
29
+ checkSkillUpdates: mock(),
30
+ configureSkill: mock(),
31
+ createSkill: mock(),
32
+ disableSkill: mock(),
33
+ draftSkill: mock(),
34
+ enableSkill: mock(),
35
+ getSkill: mock(),
36
+ getSkillFileContent: mock(),
37
+ getSkillFiles: mock(),
38
+ getSkillLocalDetail: mock(),
39
+ inspectSkill: mock(),
40
+ installSkill: mock(),
41
+ listSkills: mock(),
42
+ listSkillsFiltered: mock(),
43
+ searchSkills: mock(),
44
+ uninstallSkill: mock(),
45
+ updateSkill: mock(),
46
+ }));
47
+
48
+ mock.module("../../../skills/skill-history.js", () => ({
49
+ getSkillHistory: getSkillHistoryMock,
50
+ }));
51
+
52
+ const { ROUTES } = await import("../skills-routes.js");
53
+
54
+ const handler = ROUTES.find(
55
+ (r) => r.operationId === "getSkillHistory",
56
+ )!.handler;
57
+
58
+ beforeEach(() => {
59
+ skillExistsLocallyMock.mockClear();
60
+ getSkillHistoryMock.mockClear();
61
+ skillExistsLocallyMock.mockImplementation(() => true);
62
+ });
63
+
64
+ describe("getSkillHistory route", () => {
65
+ test("returns history for a skill that currently exists", async () => {
66
+ const result = (await handler({
67
+ pathParams: { id: "release-triage" },
68
+ })) as {
69
+ revisions: unknown[];
70
+ };
71
+
72
+ expect(result.revisions).toHaveLength(1);
73
+ expect(getSkillHistoryMock).toHaveBeenCalledTimes(1);
74
+ });
75
+
76
+ test("404s for a deleted skill whose commits are still in the repository", async () => {
77
+ // The skill is gone from the resolver, but git would still answer.
78
+ skillExistsLocallyMock.mockImplementation(() => false);
79
+
80
+ await expect(
81
+ handler({ pathParams: { id: "release-triage" } }),
82
+ ).rejects.toThrow(/not found/i);
83
+ });
84
+
85
+ test("does not read history at all when the skill is gone", async () => {
86
+ skillExistsLocallyMock.mockImplementation(() => false);
87
+
88
+ await Promise.resolve(
89
+ handler({ pathParams: { id: "release-triage" } }),
90
+ ).catch(() => {});
91
+
92
+ // Ordering is the point: a check that ran after the read would still
93
+ // reject, but would have spent the git traversal to do it.
94
+ expect(getSkillHistoryMock).not.toHaveBeenCalled();
95
+ });
96
+
97
+ test("rejects a malformed id as the caller's error, not a server fault", async () => {
98
+ getSkillHistoryMock.mockImplementation(() => {
99
+ throw new Error("Invalid skill id: contains a path separator");
100
+ });
101
+
102
+ await expect(handler({ pathParams: { id: "bad" } })).rejects.toMatchObject({
103
+ statusCode: 400,
104
+ });
105
+ });
106
+
107
+ test("surfaces an unexpected service failure as a server error", async () => {
108
+ getSkillHistoryMock.mockImplementation(() => {
109
+ throw new Error("git exploded");
110
+ });
111
+
112
+ await expect(handler({ pathParams: { id: "ok" } })).rejects.toMatchObject({
113
+ statusCode: 500,
114
+ });
115
+ });
116
+ });
@@ -0,0 +1,165 @@
1
+ /**
2
+ * Tests for the retrying SSE actor-principal self-heal.
3
+ *
4
+ * The invariant they pin: a single empty or failed guardian lookup must not
5
+ * strand the subscription without a principal. The read goes over the gateway
6
+ * IPC and comes back empty both when the transport fails and when no binding
7
+ * exists yet, and a subscription that stays principal-less rejects every
8
+ * host-proxy result its client submits with "Submitting actor does not match
9
+ * the target client's actor for this request" until it reconnects.
10
+ */
11
+ import { describe, expect, mock, test } from "bun:test";
12
+
13
+ import { startActorPrincipalHeal } from "../sse-actor-principal-heal.js";
14
+
15
+ /** All-zero schedule so the retries run without real wall-clock delay. */
16
+ const NO_DELAYS = [0, 0, 0];
17
+
18
+ /** Minimal hub double: `needs` flips to false once a principal is filled. */
19
+ function fakeHub(options?: { needs?: boolean }) {
20
+ let needs = options?.needs ?? true;
21
+ const filled: string[] = [];
22
+ return {
23
+ filled,
24
+ setNeeds(value: boolean) {
25
+ needs = value;
26
+ },
27
+ needsActorPrincipalHeal: mock(() => needs),
28
+ fillClientActorPrincipalId: mock((_connectionId: string, id: string) => {
29
+ filled.push(id);
30
+ needs = false;
31
+ }),
32
+ };
33
+ }
34
+
35
+ /** Let the heal's async loop run to completion. */
36
+ async function settle(): Promise<void> {
37
+ for (let i = 0; i < 10; i++) {
38
+ await new Promise((resolve) => setTimeout(resolve, 0));
39
+ }
40
+ }
41
+
42
+ describe("startActorPrincipalHeal", () => {
43
+ test("fills the principal on the first successful attempt", async () => {
44
+ const hub = fakeHub();
45
+ startActorPrincipalHeal({
46
+ hub,
47
+ connectionId: "conn-1",
48
+ resolve: () => Promise.resolve("guardian-real-id"),
49
+ delaysMs: NO_DELAYS,
50
+ });
51
+ await settle();
52
+
53
+ expect(hub.filled).toEqual(["guardian-real-id"]);
54
+ expect(hub.needsActorPrincipalHeal).toHaveBeenCalledTimes(1);
55
+ });
56
+
57
+ test("retries after a lookup that resolves undefined (gateway unreachable)", async () => {
58
+ const hub = fakeHub();
59
+ let calls = 0;
60
+ startActorPrincipalHeal({
61
+ hub,
62
+ connectionId: "conn-1",
63
+ resolve: () => {
64
+ calls++;
65
+ // Two failed reads, then the gateway comes up.
66
+ return Promise.resolve(calls < 3 ? undefined : "guardian-real-id");
67
+ },
68
+ delaysMs: NO_DELAYS,
69
+ });
70
+ await settle();
71
+
72
+ expect(calls).toBe(3);
73
+ expect(hub.filled).toEqual(["guardian-real-id"]);
74
+ });
75
+
76
+ test("retries after a lookup that rejects", async () => {
77
+ const hub = fakeHub();
78
+ let calls = 0;
79
+ startActorPrincipalHeal({
80
+ hub,
81
+ connectionId: "conn-1",
82
+ resolve: () => {
83
+ calls++;
84
+ return calls === 1
85
+ ? Promise.reject(new Error("ipc timeout"))
86
+ : Promise.resolve("guardian-real-id");
87
+ },
88
+ delaysMs: NO_DELAYS,
89
+ });
90
+ await settle();
91
+
92
+ expect(calls).toBe(2);
93
+ expect(hub.filled).toEqual(["guardian-real-id"]);
94
+ });
95
+
96
+ test("stops retrying once the connection no longer needs healing", async () => {
97
+ // Models a disconnect (or a reconnect that replaced this subscription)
98
+ // between attempts: the loop must not keep polling the gateway for a
99
+ // connection that is gone.
100
+ const hub = fakeHub();
101
+ let calls = 0;
102
+ startActorPrincipalHeal({
103
+ hub,
104
+ connectionId: "conn-1",
105
+ resolve: () => {
106
+ calls++;
107
+ hub.setNeeds(false);
108
+ return Promise.resolve(undefined);
109
+ },
110
+ delaysMs: NO_DELAYS,
111
+ });
112
+ await settle();
113
+
114
+ expect(calls).toBe(1);
115
+ expect(hub.filled).toEqual([]);
116
+ });
117
+
118
+ test("never starts a lookup when the connection is already healed or gone", async () => {
119
+ const hub = fakeHub({ needs: false });
120
+ const resolve = mock(() => Promise.resolve("guardian-real-id"));
121
+ startActorPrincipalHeal({
122
+ hub,
123
+ connectionId: "conn-1",
124
+ resolve,
125
+ delaysMs: NO_DELAYS,
126
+ });
127
+ await settle();
128
+
129
+ expect(resolve).toHaveBeenCalledTimes(0);
130
+ expect(hub.filled).toEqual([]);
131
+ });
132
+
133
+ test("gives up after the schedule is exhausted without filling", async () => {
134
+ const hub = fakeHub();
135
+ let calls = 0;
136
+ startActorPrincipalHeal({
137
+ hub,
138
+ connectionId: "conn-1",
139
+ resolve: () => {
140
+ calls++;
141
+ return Promise.resolve(undefined);
142
+ },
143
+ delaysMs: NO_DELAYS,
144
+ });
145
+ await settle();
146
+
147
+ expect(calls).toBe(NO_DELAYS.length);
148
+ expect(hub.filled).toEqual([]);
149
+ });
150
+
151
+ test("dispatches the first attempt synchronously", () => {
152
+ const hub = fakeHub();
153
+ const resolve = mock(() => Promise.resolve("guardian-real-id"));
154
+ startActorPrincipalHeal({
155
+ hub,
156
+ connectionId: "conn-1",
157
+ resolve,
158
+ delaysMs: NO_DELAYS,
159
+ });
160
+
161
+ // No await: the leading zero delay must not cost a macrotask, so a
162
+ // warm-cache heal lands before the stream delivers its first event.
163
+ expect(resolve).toHaveBeenCalledTimes(1);
164
+ });
165
+ });