@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
@@ -0,0 +1,113 @@
1
+ /**
2
+ * Retrying self-heal for an SSE client subscription that registered without an
3
+ * actor principal.
4
+ *
5
+ * In `DISABLE_HTTP_AUTH` deployments the SSE subscribe path cannot await, so it
6
+ * resolves the dev-bypass actor principal from an IO-free peek at the
7
+ * guardian-delivery cache. A cold cache registers the subscription with
8
+ * `actorPrincipalId: undefined`, and every host-proxy result that client
9
+ * submits then fails the same-actor gate with `missing_target`: a 403 that only
10
+ * a fresh registration clears.
11
+ *
12
+ * The heal resolves the guardian asynchronously and patches the live hub
13
+ * record. Each attempt can come back empty, since the lookup goes over the
14
+ * gateway IPC and yields nothing both when the transport fails (unreachable
15
+ * gateway, its 2s timeout, a pod still starting) and when no guardian binding
16
+ * exists yet, so attempts repeat on a bounded backoff. The loop stops on the
17
+ * first success, when the connection is gone or already carries a principal, or
18
+ * when the schedule is exhausted.
19
+ *
20
+ * Callers must pass a cache-bypassing lookup: the guardian-delivery reader
21
+ * caches a successful empty result for minutes, which outlives the whole
22
+ * schedule, so a cached read would spend every attempt on the same stale
23
+ * answer.
24
+ */
25
+ import { getLogger } from "../../util/logger.js";
26
+
27
+ const log = getLogger("sse-actor-heal");
28
+
29
+ /**
30
+ * The hub surface this module needs: a liveness check and the fill. Narrow by
31
+ * design so tests can drive the loop without a real hub.
32
+ */
33
+ export interface ActorPrincipalHealHub {
34
+ needsActorPrincipalHeal(connectionId: string): boolean;
35
+ fillClientActorPrincipalId(
36
+ connectionId: string,
37
+ actorPrincipalId: string,
38
+ ): void;
39
+ }
40
+
41
+ /**
42
+ * Delay before each attempt, in milliseconds. The leading zero dispatches the
43
+ * first attempt inline with the subscribe; the rest back off to cover a gateway
44
+ * that is slow to come up, without holding a timer for the life of a long-lived
45
+ * stream. Total span is about 44s.
46
+ */
47
+ export const ACTOR_PRINCIPAL_HEAL_DELAYS_MS: readonly number[] = [
48
+ 0, 1_000, 3_000, 10_000, 30_000,
49
+ ];
50
+
51
+ /**
52
+ * Start the retrying heal for `connectionId`. Fire-and-forget: never awaited by
53
+ * the route, never delays the stream.
54
+ *
55
+ * `resolve` performs the daemon's own server-side guardian lookup, bypassing
56
+ * the guardian-delivery cache. The value must never come from client input. It
57
+ * may resolve `undefined` (no binding yet, or the gateway is unreachable) or
58
+ * reject; both count as a failed attempt.
59
+ */
60
+ export function startActorPrincipalHeal(args: {
61
+ hub: ActorPrincipalHealHub;
62
+ connectionId: string;
63
+ resolve: () => Promise<string | undefined>;
64
+ /** Override the backoff schedule. Tests pass all-zero delays. */
65
+ delaysMs?: readonly number[];
66
+ }): void {
67
+ const { hub, connectionId, resolve } = args;
68
+ const delays = args.delaysMs ?? ACTOR_PRINCIPAL_HEAL_DELAYS_MS;
69
+
70
+ void (async () => {
71
+ for (let attempt = 0; attempt < delays.length; attempt++) {
72
+ const delayMs = delays[attempt] ?? 0;
73
+ if (delayMs > 0) {
74
+ await sleep(delayMs);
75
+ }
76
+
77
+ // Re-check every pass: the connection may have closed, been replaced by a
78
+ // reconnect, or been healed by another path since the last one.
79
+ if (!hub.needsActorPrincipalHeal(connectionId)) {
80
+ return;
81
+ }
82
+
83
+ let resolved: string | undefined;
84
+ try {
85
+ resolved = await resolve();
86
+ } catch {
87
+ resolved = undefined;
88
+ }
89
+ if (resolved) {
90
+ hub.fillClientActorPrincipalId(connectionId, resolved);
91
+ return;
92
+ }
93
+ }
94
+
95
+ // Log the give-up so a principal-less subscription is greppable next to the
96
+ // same-actor rejections it causes, rather than surfacing only as opaque
97
+ // 403s on the client.
98
+ if (hub.needsActorPrincipalHeal(connectionId)) {
99
+ log.warn(
100
+ { connectionId, attempts: delays.length },
101
+ "gave up healing missing actorPrincipalId for client subscription; host-proxy results from this connection are rejected until it reconnects",
102
+ );
103
+ }
104
+ })();
105
+ }
106
+
107
+ function sleep(ms: number): Promise<void> {
108
+ return new Promise((resolve) => {
109
+ const timer = setTimeout(resolve, ms);
110
+ // Never hold the process (or a test runner) open on a pending backoff.
111
+ (timer as { unref?: () => void }).unref?.();
112
+ });
113
+ }
@@ -223,6 +223,35 @@ const STT_ERROR_MAP: Record<SttErrorCategory, () => RouteError> = {
223
223
  "provider-error": () => new BadGatewayError("STT provider error"),
224
224
  };
225
225
 
226
+ /**
227
+ * Resolve the configured batch transcriber, or fail the request with a 503
228
+ * naming which kind of unavailability this is.
229
+ *
230
+ * A typed {@link SttError} already names the situation and its fix (a
231
+ * configured provider that cannot batch transcribe), so its message is
232
+ * surfaced verbatim. `null` means nothing is configured at all, and any other
233
+ * throw is an unexpected resolver failure.
234
+ */
235
+ async function resolveBatchTranscriberOrUnavailable(): Promise<BatchTranscriber> {
236
+ let transcriber: BatchTranscriber | null;
237
+ try {
238
+ transcriber = await resolveBatchTranscriber();
239
+ } catch (err) {
240
+ if (err instanceof SttError) {
241
+ throw new ServiceUnavailableError(err.message);
242
+ }
243
+ log.error({ err }, "Failed to resolve STT transcriber");
244
+ throw new ServiceUnavailableError("STT provider is not available");
245
+ }
246
+
247
+ if (!transcriber) {
248
+ throw new ServiceUnavailableError(
249
+ "No speech-to-text provider is configured",
250
+ );
251
+ }
252
+ return transcriber;
253
+ }
254
+
226
255
  // ---------------------------------------------------------------------------
227
256
  // Handlers
228
257
  // ---------------------------------------------------------------------------
@@ -288,19 +317,7 @@ async function handleTranscribe({ body }: RouteHandlerArgs) {
288
317
  }
289
318
 
290
319
  // -- Resolve transcriber --------------------------------------------------
291
- let transcriber;
292
- try {
293
- transcriber = await resolveBatchTranscriber();
294
- } catch (err) {
295
- log.error({ err }, "Failed to resolve STT transcriber");
296
- throw new ServiceUnavailableError("STT provider is not available");
297
- }
298
-
299
- if (!transcriber) {
300
- throw new ServiceUnavailableError(
301
- "No speech-to-text provider is configured",
302
- );
303
- }
320
+ const transcriber = await resolveBatchTranscriberOrUnavailable();
304
321
 
305
322
  // -- Transcribe with timeout ----------------------------------------------
306
323
  const abortController = new AbortController();
@@ -358,18 +375,7 @@ async function handleTranscribeFile({ body }: RouteHandlerArgs) {
358
375
  );
359
376
  }
360
377
 
361
- let transcriber;
362
- try {
363
- transcriber = await resolveBatchTranscriber();
364
- } catch (err) {
365
- log.error({ err }, "Failed to resolve STT transcriber");
366
- throw new ServiceUnavailableError("STT provider is not available");
367
- }
368
- if (!transcriber) {
369
- throw new ServiceUnavailableError(
370
- "No speech-to-text provider is configured",
371
- );
372
- }
378
+ const transcriber = await resolveBatchTranscriberOrUnavailable();
373
379
 
374
380
  const startTime = Date.now();
375
381
  let wavPath: string | null = null;
@@ -206,6 +206,7 @@ async function handleHostUiSnapshotResult({ body, headers }: RouteHandlerArgs) {
206
206
  targetActorPrincipalId: peeked.targetActorPrincipalId,
207
207
  targetClientId: peeked.targetClientId,
208
208
  op: "host_ui_snapshot",
209
+ hubForMissingTarget: assistantEventHub,
209
210
  });
210
211
  }
211
212
 
@@ -8,6 +8,7 @@ import { z } from "zod";
8
8
 
9
9
  import { INTERNAL_GUARDIAN_TRUST_CONTEXT } from "../../daemon/trust-context.js";
10
10
  import { getConversation } from "../../persistence/conversation-crud.js";
11
+ import { getSchedule } from "../../schedule/schedule-store.js";
11
12
  import { wakeAgentForOpportunity } from "../agent-wake.js";
12
13
  import { ACTOR_PRINCIPALS } from "../auth/route-policy.js";
13
14
  import { NotFoundError } from "./errors.js";
@@ -20,6 +21,10 @@ const WakeConversationBody = z.object({
20
21
  // Honored only for a `local` caller (see handler) — a remote `actor` could
21
22
  // otherwise attribute its wake's cost to an arbitrary firing.
22
23
  cronRunId: z.string().min(1).optional(),
24
+ // Runs the turn on this schedule's pinned inference profile. Honored only for
25
+ // a `local` caller (see handler): a remote `actor` could otherwise run turns
26
+ // under an arbitrary schedule's profile and spend the owner's money.
27
+ scheduleId: z.string().min(1).optional(),
23
28
  // Persist the trigger as a background event rather than an ephemeral hint, so
24
29
  // repeated wakes stay prompt-cache-friendly.
25
30
  persist: z.boolean().optional(),
@@ -53,6 +58,7 @@ export const ROUTES: RouteDefinition[] = [
53
58
  hint,
54
59
  source,
55
60
  cronRunId,
61
+ scheduleId,
56
62
  persist,
57
63
  externalContent,
58
64
  } = WakeConversationBody.parse(body);
@@ -72,6 +78,22 @@ export const ROUTES: RouteDefinition[] = [
72
78
  // local caller.
73
79
  const isLocal = headers?.["x-vellum-principal-type"] === "local";
74
80
 
81
+ // A script-mode schedule hands off to the agent loop through this route,
82
+ // so the schedule's pinned profile has to be applied here for the woken
83
+ // turn to run on the model the schedule was created under.
84
+ //
85
+ // The pin applies per turn rather than as a durable conversation pin,
86
+ // and it wins over the target conversation's own pin for turns the
87
+ // schedule triggers. That matters because a script may wake any
88
+ // conversation it names, not only the `scheduled` one it created:
89
+ // waking an interactive chat runs that one turn on the schedule's
90
+ // profile, while the user's own replies keep using their selection.
91
+ // A schedule with no pin resolves as any other turn does.
92
+ const scheduleProfile =
93
+ isLocal && scheduleId
94
+ ? (getSchedule(scheduleId)?.inferenceProfile ?? null)
95
+ : null;
96
+
75
97
  return wakeAgentForOpportunity({
76
98
  conversationId,
77
99
  hint,
@@ -80,6 +102,7 @@ export const ROUTES: RouteDefinition[] = [
80
102
  ? { trustContext: INTERNAL_GUARDIAN_TRUST_CONTEXT, clientless: true }
81
103
  : {}),
82
104
  ...(isLocal && cronRunId ? { cronRunId } : {}),
105
+ ...(scheduleProfile ? { forceOverrideProfile: scheduleProfile } : {}),
83
106
  ...(persist ? { persistTriggerAsEvent: true } : {}),
84
107
  ...(externalContent !== undefined
85
108
  ? { untrustedOutput: { content: externalContent, source: "webhook" } }
@@ -38,6 +38,7 @@ function makeConversationRow(
38
38
  originInterface: null,
39
39
  forkParentConversationId: null,
40
40
  forkParentMessageId: null,
41
+ forkStrategy: null,
41
42
  isAutoTitle: 0,
42
43
  scheduleJobId: null,
43
44
  lastMessageAt: 3000,
@@ -22,6 +22,7 @@ import {
22
22
  getDisplayMetaForConversations,
23
23
  isConversationProcessing,
24
24
  } from "../../persistence/conversation-crud.js";
25
+ import { isReferentialFork } from "../../persistence/conversation-lineage.js";
25
26
  import type { ExternalConversationBinding } from "../../persistence/external-conversation-store.js";
26
27
  import { getBindingsForConversations } from "../../persistence/external-conversation-store.js";
27
28
 
@@ -64,14 +65,24 @@ function buildAssistantAttention(attentionState: AttentionState | undefined):
64
65
  };
65
66
  }
66
67
 
67
- function buildForkParent(
68
+ interface ForkLineage {
69
+ forkParent?: { conversationId: string; messageId: string; title: string };
70
+ /**
71
+ * The fork reads its history from a parent that is gone. Only meaningful for
72
+ * referential forks: a cloning fork holds its own copy, so a deleted parent
73
+ * costs it nothing and is not worth telling the user about.
74
+ */
75
+ historyOrphaned?: true;
76
+ }
77
+
78
+ function buildForkLineage(
68
79
  conversation: ConversationRow,
69
80
  parentCache: Map<string, ConversationRow | null>,
70
- ): { conversationId: string; messageId: string; title: string } | undefined {
81
+ ): ForkLineage {
71
82
  const parentConversationId = conversation.forkParentConversationId;
72
83
  const parentMessageId = conversation.forkParentMessageId;
73
84
  if (!parentConversationId || !parentMessageId) {
74
- return undefined;
85
+ return {};
75
86
  }
76
87
 
77
88
  let parentConversation: ConversationRow | null | undefined =
@@ -81,13 +92,17 @@ function buildForkParent(
81
92
  parentCache.set(parentConversationId, parentConversation);
82
93
  }
83
94
  if (!parentConversation) {
84
- return undefined;
95
+ // The parent lookup already happened, so reporting the orphan costs
96
+ // nothing beyond the branch.
97
+ return isReferentialFork(conversation) ? { historyOrphaned: true } : {};
85
98
  }
86
99
 
87
100
  return {
88
- conversationId: parentConversationId,
89
- messageId: parentMessageId,
90
- title: parentConversation.title ?? "Untitled",
101
+ forkParent: {
102
+ conversationId: parentConversationId,
103
+ messageId: parentMessageId,
104
+ title: parentConversation.title ?? "Untitled",
105
+ },
91
106
  };
92
107
  }
93
108
 
@@ -174,7 +189,7 @@ export function serializeConversationSummary(params: {
174
189
  } = params;
175
190
  const originChannel = parseChannelId(conversation.originChannel);
176
191
  const assistantAttention = buildAssistantAttention(attentionState);
177
- const forkParent = buildForkParent(conversation, parentCache);
192
+ const forkLineage = buildForkLineage(conversation, parentCache);
178
193
 
179
194
  return {
180
195
  id: conversation.id,
@@ -208,7 +223,7 @@ export function serializeConversationSummary(params: {
208
223
  conversation,
209
224
  displayMeta?.groupId ?? null,
210
225
  ),
211
- ...(forkParent ? { forkParent } : {}),
226
+ ...forkLineage,
212
227
  ...(conversation.archivedAt != null
213
228
  ? { archivedAt: conversation.archivedAt }
214
229
  : {}),
@@ -0,0 +1,101 @@
1
+ /**
2
+ * A scheduled, heartbeat, or background turn runs its document tools in a
3
+ * sidecar worker, whose local hub has no SSE subscriber. The documents-changed
4
+ * broadcast has to be handed to the daemon there, or the document edited while
5
+ * the user was not looking stays invisible on every client.
6
+ */
7
+
8
+ import { beforeEach, describe, expect, mock, test } from "bun:test";
9
+
10
+ let notifyCount = 0;
11
+
12
+ mock.module("./worker-daemon-notify.js", () => ({
13
+ NOTIFY_CONVERSATION_PERSISTED_IPC_METHOD:
14
+ "notify_conversation_persisted_externally",
15
+ NOTIFY_DOCUMENTS_CHANGED_IPC_METHOD: "notify_documents_changed_externally",
16
+ notifyDaemonConversationPersisted: async () => {},
17
+ notifyDaemonDocumentsChanged: async () => {
18
+ notifyCount++;
19
+ },
20
+ }));
21
+
22
+ import type { AssistantEventEnvelope } from "../../api/index.js";
23
+ import { assistantEventHub } from "../assistant-event-hub.js";
24
+ import {
25
+ _resetStreamStateForTesting,
26
+ disableStreamSeqStamping,
27
+ } from "../assistant-stream-state.js";
28
+ import {
29
+ DOCUMENTS_CHANGED_COALESCE_MS,
30
+ publishDocumentsChanged,
31
+ } from "./resource-sync-events.js";
32
+
33
+ async function settle(): Promise<void> {
34
+ await new Promise((resolve) =>
35
+ setTimeout(resolve, DOCUMENTS_CHANGED_COALESCE_MS + 50),
36
+ );
37
+ }
38
+
39
+ /** Run `publish`, then return the `sync_changed` events it put on the hub. */
40
+ async function captureLocalPublishes(
41
+ publish: () => void,
42
+ ): Promise<AssistantEventEnvelope[]> {
43
+ await settle();
44
+ const received: AssistantEventEnvelope[] = [];
45
+ const subscription = assistantEventHub.subscribe({
46
+ type: "process",
47
+ callback: (event) => {
48
+ if (event.message.type === "sync_changed") {
49
+ received.push(event);
50
+ }
51
+ },
52
+ });
53
+ try {
54
+ publish();
55
+ await settle();
56
+ return received;
57
+ } finally {
58
+ subscription.dispose();
59
+ }
60
+ }
61
+
62
+ describe("publishDocumentsChanged in a sidecar worker", () => {
63
+ beforeEach(async () => {
64
+ await settle();
65
+ notifyCount = 0;
66
+ _resetStreamStateForTesting();
67
+ });
68
+
69
+ test("hands the broadcast to the daemon instead of publishing into a void", async () => {
70
+ disableStreamSeqStamping();
71
+
72
+ const events = await captureLocalPublishes(() => {
73
+ publishDocumentsChanged();
74
+ });
75
+
76
+ expect(notifyCount).toBe(1);
77
+ expect(events).toEqual([]);
78
+ });
79
+
80
+ test("coalesces a burst of worker writes into one hand-off", async () => {
81
+ disableStreamSeqStamping();
82
+
83
+ await captureLocalPublishes(() => {
84
+ for (let i = 0; i < 10; i++) {
85
+ publishDocumentsChanged();
86
+ }
87
+ });
88
+
89
+ expect(notifyCount).toBe(1);
90
+ });
91
+
92
+ test("publishes on the local hub in the daemon, where subscribers live", async () => {
93
+ const events = await captureLocalPublishes(() => {
94
+ publishDocumentsChanged();
95
+ });
96
+
97
+ expect(notifyCount).toBe(0);
98
+ expect(events).toHaveLength(1);
99
+ expect(events[0].message).toMatchObject({ type: "sync_changed" });
100
+ });
101
+ });
@@ -9,7 +9,10 @@ import { getAvatarImagePath } from "../../util/platform.js";
9
9
  import { broadcastMessage } from "../assistant-event-hub.js";
10
10
  import { isStreamSeqStampingDisabled } from "../assistant-stream-state.js";
11
11
  import { publishSyncInvalidation } from "./sync-publisher.js";
12
- import { notifyDaemonConversationPersisted } from "./worker-daemon-notify.js";
12
+ import {
13
+ notifyDaemonConversationPersisted,
14
+ notifyDaemonDocumentsChanged,
15
+ } from "./worker-daemon-notify.js";
13
16
 
14
17
  /**
15
18
  * Derive the initiating client's id from request headers so a sync publish can
@@ -67,6 +70,81 @@ export function publishAppsChanged(originClientId?: string): void {
67
70
  void publishSyncInvalidation([SYNC_TAGS.appsList], originClientId);
68
71
  }
69
72
 
73
+ /**
74
+ * How long a documents-list invalidation waits for the writes around it to
75
+ * settle before it is broadcast.
76
+ *
77
+ * Document writes arrive in bursts: the document-editor skill is instructed to
78
+ * stream a document as many small `document_update` calls, and the web editor
79
+ * autosaves as the user types. Every burst member publishes, and every publish
80
+ * makes each client drain its `documentsGet` queries (the per-conversation
81
+ * assets pill, the Library, the intelligence stats), during an SSE stream that
82
+ * is already saturated. Open editors track each individual write through
83
+ * `document_editor_update`, so the list-level invalidation only has to land
84
+ * once the burst settles.
85
+ */
86
+ export const DOCUMENTS_CHANGED_COALESCE_MS = 150;
87
+
88
+ /**
89
+ * Ceiling on how long an unbroken run of writes can defer the invalidation, so
90
+ * a document streamed in sub-window chunks still refreshes the list while it is
91
+ * being written instead of only when the turn ends.
92
+ */
93
+ export const DOCUMENTS_CHANGED_MAX_DEFER_MS = 1_000;
94
+
95
+ let documentsPublishTimer: ReturnType<typeof setTimeout> | undefined;
96
+ let documentsPublishDeadline = 0;
97
+ let documentsPublishOrigin: string | undefined;
98
+ let documentsPublishOriginSeen = false;
99
+
100
+ function flushDocumentsChanged(): void {
101
+ documentsPublishTimer = undefined;
102
+ documentsPublishDeadline = 0;
103
+ const originClientId = documentsPublishOrigin;
104
+ documentsPublishOrigin = undefined;
105
+ documentsPublishOriginSeen = false;
106
+
107
+ // In a sidecar worker (seq stamping disabled) the local hub has no SSE
108
+ // subscribers, so a local publish reaches no client and a document edited by
109
+ // a scheduled or background turn stays invisible. Hand off to the daemon,
110
+ // which republishes the invalidation where subscribers observe it. Such a
111
+ // turn has no originating client, so no `originClientId` is forwarded.
112
+ if (isStreamSeqStampingDisabled()) {
113
+ void notifyDaemonDocumentsChanged();
114
+ return;
115
+ }
116
+ void publishSyncInvalidation([SYNC_TAGS.documentsList], originClientId);
117
+ }
118
+
119
+ /**
120
+ * Invalidate the document list on every client, coalescing a burst of writes
121
+ * into a single broadcast (see {@link DOCUMENTS_CHANGED_COALESCE_MS}).
122
+ */
123
+ export function publishDocumentsChanged(originClientId?: string): void {
124
+ if (!documentsPublishOriginSeen) {
125
+ documentsPublishOrigin = originClientId;
126
+ documentsPublishOriginSeen = true;
127
+ } else if (documentsPublishOrigin !== originClientId) {
128
+ // Writes from more than one client merged into this broadcast, so no single
129
+ // client may suppress it as its own echo.
130
+ documentsPublishOrigin = undefined;
131
+ }
132
+
133
+ const now = Date.now();
134
+ if (documentsPublishTimer) {
135
+ clearTimeout(documentsPublishTimer);
136
+ } else {
137
+ documentsPublishDeadline = now + DOCUMENTS_CHANGED_MAX_DEFER_MS;
138
+ }
139
+ documentsPublishTimer = setTimeout(
140
+ flushDocumentsChanged,
141
+ Math.max(
142
+ 0,
143
+ Math.min(DOCUMENTS_CHANGED_COALESCE_MS, documentsPublishDeadline - now),
144
+ ),
145
+ );
146
+ }
147
+
70
148
  export function publishPluginsChanged(originClientId?: string): void {
71
149
  void publishSyncInvalidation([SYNC_TAGS.pluginsList], originClientId);
72
150
  }
@@ -33,7 +33,9 @@ mock.module("../../ipc/cli-client.js", () => ({
33
33
 
34
34
  import {
35
35
  NOTIFY_CONVERSATION_PERSISTED_IPC_METHOD,
36
+ NOTIFY_DOCUMENTS_CHANGED_IPC_METHOD,
36
37
  notifyDaemonConversationPersisted,
38
+ notifyDaemonDocumentsChanged,
37
39
  } from "./worker-daemon-notify.js";
38
40
 
39
41
  describe("notifyDaemonConversationPersisted", () => {
@@ -67,3 +69,35 @@ describe("notifyDaemonConversationPersisted", () => {
67
69
  expect(calls).toHaveLength(1);
68
70
  });
69
71
  });
72
+
73
+ describe("notifyDaemonDocumentsChanged", () => {
74
+ beforeEach(() => {
75
+ calls.length = 0;
76
+ nextResult = { ok: true };
77
+ shouldThrow = false;
78
+ });
79
+
80
+ test("asks the daemon to republish over the shared IPC method", async () => {
81
+ await notifyDaemonDocumentsChanged();
82
+
83
+ expect(calls).toHaveLength(1);
84
+ expect(calls[0]!.method).toBe(NOTIFY_DOCUMENTS_CHANGED_IPC_METHOD);
85
+ expect(calls[0]!.params).toEqual({ body: {} });
86
+ });
87
+
88
+ test("swallows a failed IPC result (best-effort, no throw)", async () => {
89
+ nextResult = { ok: false, error: "daemon unreachable" };
90
+
91
+ const result = await notifyDaemonDocumentsChanged();
92
+ expect(result).toBeUndefined();
93
+ expect(calls).toHaveLength(1);
94
+ });
95
+
96
+ test("swallows a thrown IPC error (best-effort, no throw)", async () => {
97
+ shouldThrow = true;
98
+
99
+ const result = await notifyDaemonDocumentsChanged();
100
+ expect(result).toBeUndefined();
101
+ expect(calls).toHaveLength(1);
102
+ });
103
+ });
@@ -1,13 +1,13 @@
1
1
  /**
2
- * Worker → daemon hand-off for conversation message-persist notifications.
2
+ * Worker → daemon hand-off for sync notifications a sidecar cannot deliver.
3
3
  *
4
4
  * Sidecar worker processes (schedule, memory) disable SSE seq stamping — the
5
5
  * daemon is the sole seq authority — so a worker's own `getCurrentSeq()`
6
- * reports `0` and its `publishConversationMessagesChanged` broadcast reaches no
7
- * SSE subscriber (those live in the daemon). After a worker persists a turn's
8
- * rows it hands the notification here; the daemon records an honest snapshot
9
- * anchor at its own seq and republishes the invalidation to real subscribers
10
- * (see `ipc/routes/conversation-sync-ipc-routes.ts`).
6
+ * reports `0` and its `sync_changed` broadcasts reach no SSE subscriber (those
7
+ * live in the daemon). A worker hands the notification here instead and the
8
+ * daemon republishes it to real subscribers (see
9
+ * `ipc/routes/conversation-sync-ipc-routes.ts`,
10
+ * `ipc/routes/documents-sync-ipc-routes.ts`).
11
11
  */
12
12
 
13
13
  import { cliIpcCall } from "../../ipc/cli-client.js";
@@ -22,6 +22,14 @@ const log = getLogger("worker-daemon-notify");
22
22
  export const NOTIFY_CONVERSATION_PERSISTED_IPC_METHOD =
23
23
  "notify_conversation_persisted_externally";
24
24
 
25
+ /**
26
+ * IPC method the daemon exposes for the documents-changed hand-off. Shared by
27
+ * the worker caller and the daemon route registration so the wire name cannot
28
+ * drift.
29
+ */
30
+ export const NOTIFY_DOCUMENTS_CHANGED_IPC_METHOD =
31
+ "notify_documents_changed_externally";
32
+
25
33
  /**
26
34
  * Short timeout: this is a fire-and-forget invalidation, not a request whose
27
35
  * result the worker consumes. A busy or unreachable daemon simply leaves the
@@ -61,3 +69,33 @@ export async function notifyDaemonConversationPersisted(
61
69
  );
62
70
  }
63
71
  }
72
+
73
+ /**
74
+ * Ask the daemon to republish the documents-list invalidation.
75
+ *
76
+ * A scheduled or background turn runs its document tools in a sidecar worker,
77
+ * so the edit is real but the worker's own broadcast reaches nobody. This is
78
+ * the path that lets the user learn a document changed while they were not
79
+ * looking.
80
+ *
81
+ * Best-effort by design, like the conversation hand-off: an unreachable daemon
82
+ * is logged at debug and swallowed, and the client picks the change up on its
83
+ * next document-list fetch.
84
+ */
85
+ export async function notifyDaemonDocumentsChanged(): Promise<void> {
86
+ try {
87
+ const result = await cliIpcCall(
88
+ NOTIFY_DOCUMENTS_CHANGED_IPC_METHOD,
89
+ { body: {} },
90
+ { timeoutMs: NOTIFY_TIMEOUT_MS },
91
+ );
92
+ if (!result.ok) {
93
+ log.debug(
94
+ { error: result.error },
95
+ "daemon documents-changed notify was not acknowledged",
96
+ );
97
+ }
98
+ } catch (err) {
99
+ log.debug({ err }, "daemon documents-changed notify failed");
100
+ }
101
+ }