@vellumai/assistant 0.11.2 → 0.11.3-staging.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (720) hide show
  1. package/ARCHITECTURE.md +58 -3
  2. package/docs/trusted-contact-access.md +9 -5
  3. package/docs/vellum-doctor.md +195 -0
  4. package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/channels.ts +39 -0
  5. package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/ingress.ts +10 -0
  6. package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/remote-web-pairing.ts +6 -0
  7. package/node_modules/@vellumai/ces-client/src/index.ts +1 -0
  8. package/node_modules/@vellumai/ces-client/src/rpc-client.ts +19 -2
  9. package/node_modules/@vellumai/environments/package.json +2 -1
  10. package/node_modules/@vellumai/environments/src/__tests__/cloud-assistant-hub-url.test.ts +38 -0
  11. package/node_modules/@vellumai/environments/src/__tests__/install-layout.test.ts +98 -0
  12. package/node_modules/@vellumai/environments/src/__tests__/package-boundary.test.ts +5 -5
  13. package/node_modules/@vellumai/environments/src/index.ts +17 -6
  14. package/node_modules/@vellumai/environments/src/install-layout.ts +49 -0
  15. package/node_modules/@vellumai/environments/src/seeds.ts +29 -0
  16. package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/channels.ts +39 -0
  17. package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/ingress.ts +10 -0
  18. package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/remote-web-pairing.ts +6 -0
  19. package/node_modules/@vellumai/gateway-client/src/index.ts +4 -0
  20. package/node_modules/@vellumai/gateway-client/src/verification-session-contract.ts +91 -6
  21. package/node_modules/@vellumai/ipc-server-utils/src/endpoint.test.ts +36 -0
  22. package/node_modules/@vellumai/ipc-server-utils/src/endpoint.ts +142 -0
  23. package/node_modules/@vellumai/ipc-server-utils/src/index.ts +2 -0
  24. package/node_modules/@vellumai/ipc-server-utils/src/listen-options.ts +3 -0
  25. package/node_modules/@vellumai/ipc-server-utils/src/socket-watchdog.test.ts +15 -1
  26. package/node_modules/@vellumai/ipc-server-utils/src/socket-watchdog.ts +17 -2
  27. package/node_modules/@vellumai/service-contracts/src/channels.ts +39 -0
  28. package/node_modules/@vellumai/service-contracts/src/ingress.ts +10 -0
  29. package/node_modules/@vellumai/service-contracts/src/remote-web-pairing.ts +6 -0
  30. package/node_modules/@vellumai/slack-text/src/index.ts +16 -2
  31. package/openapi.yaml +589 -30
  32. package/package.json +1 -1
  33. package/src/__tests__/agent-image-optimize.test.ts +4 -4
  34. package/src/__tests__/agent-loop-mutable-latest-user-message.test.ts +113 -66
  35. package/src/__tests__/agent-loop-override-profile.test.ts +38 -48
  36. package/src/__tests__/agent-loop-subagent-billing-headers.test.ts +166 -0
  37. package/src/__tests__/agent-wake-disk-pressure-callsite.test.ts +1 -0
  38. package/src/__tests__/agent-wake-override-profile.test.ts +1 -0
  39. package/src/__tests__/answered-question-persistence.test.ts +240 -0
  40. package/src/__tests__/anthropic-provider.test.ts +165 -30
  41. package/src/__tests__/app-control-flow.test.ts +9 -8
  42. package/src/__tests__/assistant-event-hub.test.ts +48 -0
  43. package/src/__tests__/attachments-store-heic-normalize.test.ts +16 -12
  44. package/src/__tests__/attachments-store.test.ts +130 -68
  45. package/src/__tests__/attachments.test.ts +36 -36
  46. package/src/__tests__/background-workers-disk-pressure.test.ts +1 -1
  47. package/src/__tests__/call-start-guardian-guard.test.ts +18 -4
  48. package/src/__tests__/canned-reply-release.test.ts +86 -0
  49. package/src/__tests__/channel-guardian.test.ts +59 -29
  50. package/src/__tests__/channel-readiness-telegram-remote.test.ts +154 -0
  51. package/src/__tests__/channel-setup-panel-ack.test.ts +6 -5
  52. package/src/__tests__/client-os-metadata-persistence.test.ts +103 -4
  53. package/src/__tests__/compactor-image-manifest-trust.test.ts +1 -1
  54. package/src/__tests__/consult-deadline.test.ts +65 -0
  55. package/src/__tests__/conversation-agent-loop.test.ts +438 -2
  56. package/src/__tests__/conversation-disk-view-integration.test.ts +5 -1
  57. package/src/__tests__/conversation-disk-view.test.ts +4 -4
  58. package/src/__tests__/conversation-error.test.ts +22 -3
  59. package/src/__tests__/conversation-fork-crud.test.ts +5 -1
  60. package/src/__tests__/conversation-fork-referential.test.ts +352 -0
  61. package/src/__tests__/conversation-fork-retrospective.test.ts +5 -1
  62. package/src/__tests__/conversation-group-tools.test.ts +32 -2
  63. package/src/__tests__/conversation-inference-profile-route.test.ts +27 -1
  64. package/src/__tests__/conversation-initial-prompt.test.ts +51 -22
  65. package/src/__tests__/conversation-key-store-bootstrap-cleanup.test.ts +119 -0
  66. package/src/__tests__/conversation-key-store-origin.test.ts +105 -0
  67. package/src/__tests__/conversation-list-recency-ordering.test.ts +104 -0
  68. package/src/__tests__/conversation-origin-at-creation.test.ts +153 -0
  69. package/src/__tests__/conversation-origin-channel-filter.test.ts +95 -0
  70. package/src/__tests__/conversation-placement-promotion.test.ts +172 -0
  71. package/src/__tests__/conversation-process-app-control-preactivation.test.ts +123 -0
  72. package/src/__tests__/conversation-queue.test.ts +433 -1
  73. package/src/__tests__/conversation-retry-route.test.ts +3 -1
  74. package/src/__tests__/conversation-routes-hidden-queue.test.ts +64 -1
  75. package/src/__tests__/conversation-store-ephemeral.test.ts +4 -0
  76. package/src/__tests__/conversation-store.test.ts +26 -10
  77. package/src/__tests__/conversation-summarize-route.test.ts +1 -1
  78. package/src/__tests__/conversation-surfaces-action-delivery.test.ts +15 -6
  79. package/src/__tests__/conversation-surfaces-activation-emit.test.ts +6 -5
  80. package/src/__tests__/conversation-surfaces-app-control.test.ts +12 -11
  81. package/src/__tests__/conversation-surfaces-app-open.test.ts +11 -5
  82. package/src/__tests__/conversation-surfaces-data-persist.test.ts +5 -4
  83. package/src/__tests__/conversation-surfaces-history-restored-completion.test.ts +967 -0
  84. package/src/__tests__/conversation-surfaces-persisted-info.test.ts +312 -0
  85. package/src/__tests__/conversation-surfaces-queued-emit.test.ts +6 -5
  86. package/src/__tests__/conversation-surfaces-standalone-payloads.test.ts +12 -8
  87. package/src/__tests__/conversation-surfaces-standalone.test.ts +21 -11
  88. package/src/__tests__/conversation-surfaces-state-update.test.ts +7 -11
  89. package/src/__tests__/conversation-surfaces-table-action.test.ts +7 -6
  90. package/src/__tests__/conversation-surfaces-task-progress.test.ts +11 -6
  91. package/src/__tests__/conversation-tool-setup-app-refresh.test.ts +9 -6
  92. package/src/__tests__/conversation-tool-setup-attribution.test.ts +70 -7
  93. package/src/__tests__/conversation-wait-for-idle.test.ts +6 -11
  94. package/src/__tests__/credential-prompt-route.test.ts +57 -7
  95. package/src/__tests__/credential-security-invariants.test.ts +1 -0
  96. package/src/__tests__/cu-unified-flow.test.ts +89 -28
  97. package/src/__tests__/custom-group-listing-visibility.test.ts +1 -1
  98. package/src/__tests__/db-schedule-syntax-migration.test.ts +4 -1
  99. package/src/__tests__/discord-access-request-privacy.test.ts +366 -0
  100. package/src/__tests__/discord-requester-notice-privacy.test.ts +87 -0
  101. package/src/__tests__/disk-pressure-guard.test.ts +55 -55
  102. package/src/__tests__/disk-pressure-routes.test.ts +10 -10
  103. package/src/__tests__/disk-pressure-tools.test.ts +2 -2
  104. package/src/__tests__/disk-usage.test.ts +35 -20
  105. package/src/__tests__/document-sync-tags.test.ts +379 -0
  106. package/src/__tests__/document-update-default-surface.test.ts +28 -0
  107. package/src/__tests__/dynamic-skill-background-guard.test.ts +2 -2
  108. package/src/__tests__/emit-signal-routing-intent.test.ts +3 -4
  109. package/src/__tests__/ensure-conversation-exists.test.ts +8 -5
  110. package/src/__tests__/events-dev-bypass-actor.test.ts +183 -1
  111. package/src/__tests__/evict-conversations-for-reload.test.ts +90 -0
  112. package/src/__tests__/file-list-tool.test.ts +12 -10
  113. package/src/__tests__/file-ops-service.test.ts +151 -44
  114. package/src/__tests__/file-read-container-boundary.test.ts +137 -0
  115. package/src/__tests__/file-read-tool.test.ts +21 -9
  116. package/src/__tests__/file-url-path-guard.test.ts +83 -0
  117. package/src/__tests__/filesystem-tools.test.ts +82 -66
  118. package/src/__tests__/guardian-outbound-http.test.ts +8 -8
  119. package/src/__tests__/helpers/gateway-verification-sessions-stub.ts +14 -1
  120. package/src/__tests__/helpers/hub-clients.ts +42 -0
  121. package/src/__tests__/helpers/mock-conversation.ts +46 -0
  122. package/src/__tests__/helpers/verification-sessions-ipc-sim.ts +61 -57
  123. package/src/__tests__/history-repair.test.ts +112 -12
  124. package/src/__tests__/host-bash-proxy.test.ts +10 -5
  125. package/src/__tests__/host-bash-routes.test.ts +67 -0
  126. package/src/__tests__/host-browser-proxy.test.ts +2 -2
  127. package/src/__tests__/host-cu-proxy.test.ts +3 -7
  128. package/src/__tests__/host-file-proxy.test.ts +3 -7
  129. package/src/__tests__/host-file-read-tool.test.ts +5 -1
  130. package/src/__tests__/host-transfer-proxy-targeted.test.ts +6 -4
  131. package/src/__tests__/http-conversation-lineage.test.ts +2 -5
  132. package/src/__tests__/http-user-message-parity.test.ts +44 -0
  133. package/src/__tests__/identity-routes.test.ts +13 -13
  134. package/src/__tests__/image-conversion.test.ts +29 -26
  135. package/src/__tests__/image-source-path-reinject.test.ts +2 -2
  136. package/src/__tests__/inference-profile-session-handler.test.ts +95 -0
  137. package/src/__tests__/inference-profile-session-ipc.test.ts +25 -0
  138. package/src/__tests__/introduction-card-resolver.test.ts +6 -1
  139. package/src/__tests__/list-messages-attachments.test.ts +56 -32
  140. package/src/__tests__/list-messages-background-tool-completion.test.ts +4 -4
  141. package/src/__tests__/list-messages-client-message-id.test.ts +2 -2
  142. package/src/__tests__/list-messages-hidden-metadata.test.ts +16 -16
  143. package/src/__tests__/list-messages-page-latest.test.ts +61 -61
  144. package/src/__tests__/list-messages-provider-error.test.ts +6 -6
  145. package/src/__tests__/list-messages-queued.test.ts +49 -6
  146. package/src/__tests__/list-messages-system-card.test.ts +4 -4
  147. package/src/__tests__/list-messages-tool-merge.test.ts +9 -9
  148. package/src/__tests__/llm-resolver-override-or-default.test.ts +141 -0
  149. package/src/__tests__/llm-usage-store.test.ts +55 -0
  150. package/src/__tests__/manual-token-reconciliation.test.ts +45 -0
  151. package/src/__tests__/mcp-tool-annotations-risk.test.ts +133 -0
  152. package/src/__tests__/media-stream-server-integration.test.ts +36 -0
  153. package/src/__tests__/migration-export-http.test.ts +1 -1
  154. package/src/__tests__/migration-import-commit-http.test.ts +1 -1
  155. package/src/__tests__/migration-import-preflight-http.test.ts +1 -1
  156. package/src/__tests__/migration-validate-http.test.ts +1 -1
  157. package/src/__tests__/mtime-cache.test.ts +65 -3
  158. package/src/__tests__/notification-platform-adapter.test.ts +104 -0
  159. package/src/__tests__/notification-schedule-notify-dedup.test.ts +1 -1
  160. package/src/__tests__/oauth-provider-profiles.test.ts +4 -2
  161. package/src/__tests__/oauth-provider-seed-logos.test.ts +65 -0
  162. package/src/__tests__/oauth-provider-serializer.test.ts +17 -0
  163. package/src/__tests__/oauth-providers-routes.test.ts +1 -0
  164. package/src/__tests__/openai-responses-prompt-cache.test.ts +5 -2
  165. package/src/__tests__/path-policy.test.ts +164 -1
  166. package/src/__tests__/persist-media-references.test.ts +2 -2
  167. package/src/__tests__/persist-unsendable-image-downscale.test.ts +5 -5
  168. package/src/__tests__/persist-unsendable-image.test.ts +23 -23
  169. package/src/__tests__/persona-resolver.test.ts +154 -0
  170. package/src/__tests__/plugin-api-webhook-url.test.ts +170 -0
  171. package/src/__tests__/plugin-import-boundary-guard.test.ts +6 -0
  172. package/src/__tests__/processing-flag-persist-failure.test.ts +15 -10
  173. package/src/__tests__/profile-availability-incomplete.test.ts +113 -0
  174. package/src/__tests__/profiler-routes.test.ts +13 -13
  175. package/src/__tests__/prompt-cache-cross-turn-stability.test.ts +593 -0
  176. package/src/__tests__/proxy-approval-callback.test.ts +3 -3
  177. package/src/__tests__/queued-message-cancel-and-steer.test.ts +189 -0
  178. package/src/__tests__/queued-message-steer-actor-scoping.test.ts +164 -0
  179. package/src/__tests__/resolve-trust-class.test.ts +20 -0
  180. package/src/__tests__/run-conversation-turn-persistence.test.ts +1 -1
  181. package/src/__tests__/run-due-schedules.test.ts +174 -0
  182. package/src/__tests__/runtime-attachment-metadata.test.ts +15 -7
  183. package/src/__tests__/scaffold-managed-skill-tool.test.ts +166 -0
  184. package/src/__tests__/schedule-routes.test.ts +730 -8
  185. package/src/__tests__/schedule-store.test.ts +540 -0
  186. package/src/__tests__/schedule-tools.test.ts +103 -0
  187. package/src/__tests__/scheduler-wake.test.ts +5 -0
  188. package/src/__tests__/secret-routes-platform-proxy.test.ts +29 -1
  189. package/src/__tests__/secure-keys.test.ts +91 -0
  190. package/src/__tests__/server-history-render.test.ts +97 -7
  191. package/src/__tests__/size-guard.test.ts +8 -8
  192. package/src/__tests__/skill-docs-sync-guard.test.ts +3 -2
  193. package/src/__tests__/skill-tool-factory.test.ts +247 -0
  194. package/src/__tests__/skills-install-staging.test.ts +15 -6
  195. package/src/__tests__/slack-edit-ordering-characterization.test.ts +103 -0
  196. package/src/__tests__/slack-inbound-verification.test.ts +4 -1
  197. package/src/__tests__/slack-mention-binding-invariance.test.ts +103 -0
  198. package/src/__tests__/slack-mention-provider-leak-audit.test.ts +135 -0
  199. package/src/__tests__/starter-task-flow.test.ts +5 -4
  200. package/src/__tests__/steer-on-enqueue-question.test.ts +3 -2
  201. package/src/__tests__/stt-language-catalog-parity.test.ts +38 -0
  202. package/src/__tests__/subagent-allowlist-validation.test.ts +26 -4
  203. package/src/__tests__/subagent-cron-run-attribution.test.ts +301 -0
  204. package/src/__tests__/subagent-disposal.test.ts +10 -0
  205. package/src/__tests__/subagent-fork-notifications.test.ts +10 -0
  206. package/src/__tests__/subagent-fork-prompt-role.test.ts +85 -11
  207. package/src/__tests__/subagent-fork-spawn.test.ts +15 -7
  208. package/src/__tests__/subagent-manager-notify.test.ts +148 -0
  209. package/src/__tests__/subagent-notify-parent.test.ts +1 -1
  210. package/src/__tests__/subagent-persistence.test.ts +141 -0
  211. package/src/__tests__/subagent-role-registry.test.ts +172 -63
  212. package/src/__tests__/subagent-role-resolution.test.ts +88 -0
  213. package/src/__tests__/subagent-spawn-and-await.test.ts +48 -0
  214. package/src/__tests__/subagent-spawn-tool-fork.test.ts +111 -10
  215. package/src/__tests__/subagent-terminal-message.test.ts +88 -1
  216. package/src/__tests__/subagent-tool-gate-mode.test.ts +225 -10
  217. package/src/__tests__/subagent-tools.test.ts +1901 -108
  218. package/src/__tests__/surface-completion-compaction-boundary.test.ts +124 -0
  219. package/src/__tests__/surface-completion-in-flight-snapshot.test.ts +298 -0
  220. package/src/__tests__/system-prompt.test.ts +82 -7
  221. package/src/__tests__/tool-grant-request-escalation.test.ts +40 -1
  222. package/src/__tests__/tool-result-spool.test.ts +74 -18
  223. package/src/__tests__/tool-side-effects-documents.test.ts +174 -0
  224. package/src/__tests__/tool-side-effects-hook-failures.test.ts +129 -0
  225. package/src/__tests__/tools-audio-read.test.ts +10 -10
  226. package/src/__tests__/tools-get-route.test.ts +85 -12
  227. package/src/__tests__/tools-list-cli.test.ts +171 -0
  228. package/src/__tests__/top-level-renderer.test.ts +54 -1
  229. package/src/__tests__/trusted-contact-inline-approval-integration.test.ts +68 -1
  230. package/src/__tests__/trusted-contact-multichannel.test.ts +4 -1
  231. package/src/__tests__/turn-tail-chain.test.ts +135 -0
  232. package/src/__tests__/ui-choice-copy-surfaces.test.ts +6 -4
  233. package/src/__tests__/ui-visual-surface.test.ts +5 -4
  234. package/src/__tests__/ui-work-result-surface.test.ts +5 -4
  235. package/src/__tests__/unified-turn-context-visible-app.test.ts +7 -8
  236. package/src/__tests__/usage-routes.test.ts +62 -0
  237. package/src/__tests__/verification-outbound-delivery.test.ts +176 -0
  238. package/src/__tests__/visible-app-context.test.ts +20 -4
  239. package/src/__tests__/web-search-history.test.ts +96 -0
  240. package/src/__tests__/workspace-migration-139-clear-renamed-cost-profile-label.test.ts +6 -6
  241. package/src/__tests__/workspace-migration-140-repair-seed-pinned-memory-v3-live.test.ts +330 -0
  242. package/src/acp/failure-error.ts +3 -5
  243. package/src/agent/attachments.ts +26 -24
  244. package/src/agent/history-repair/history-repair.ts +27 -26
  245. package/src/agent/image-optimize.ts +6 -6
  246. package/src/agent/loop.ts +40 -22
  247. package/src/agent/message-types.ts +3 -3
  248. package/src/api/constants/document-tools.ts +55 -0
  249. package/src/api/events/question-answered.ts +53 -0
  250. package/src/api/events/tool-result.ts +7 -0
  251. package/src/api/index.ts +13 -0
  252. package/src/api/responses/conversation-message.ts +11 -0
  253. package/src/api/surfaces-oauth-connect.test.ts +96 -0
  254. package/src/api/surfaces.ts +36 -8
  255. package/src/approvals/guardian-channel-delivery.ts +83 -0
  256. package/src/approvals/guardian-expiry-notifier.ts +13 -11
  257. package/src/approvals/guardian-request-resolvers.ts +222 -135
  258. package/src/calls/__tests__/voice-session-bridge.test.ts +50 -0
  259. package/src/calls/media-stream-server.ts +9 -1
  260. package/src/calls/voice-session-bridge.ts +38 -0
  261. package/src/channels/__tests__/gateway-verification-sessions.test.ts +3 -3
  262. package/src/channels/config.ts +7 -2
  263. package/src/channels/gateway-verification-sessions.ts +7 -2
  264. package/src/channels/types.ts +7 -1
  265. package/src/cli/commands/__tests__/cache.test.ts +7 -4
  266. package/src/cli/commands/__tests__/cli-test-harness.ts +82 -0
  267. package/src/cli/commands/__tests__/conversations-wake.test.ts +51 -6
  268. package/src/cli/commands/__tests__/inference-models.test.ts +14 -0
  269. package/src/cli/commands/__tests__/inference-profiles.test.ts +44 -1
  270. package/src/cli/commands/__tests__/inference-providers.test.ts +7 -0
  271. package/src/cli/commands/__tests__/plugins.test.ts +692 -0
  272. package/src/cli/commands/__tests__/schedules.test.ts +136 -37
  273. package/src/cli/commands/conversations.ts +7 -3
  274. package/src/cli/commands/credentials.help.ts +7 -0
  275. package/src/cli/commands/inference-profiles.ts +9 -3
  276. package/src/cli/commands/inference.help.ts +16 -2
  277. package/src/cli/commands/mcp.help.ts +4 -2
  278. package/src/cli/commands/memory/memory-retrospective.ts +18 -0
  279. package/src/cli/commands/notifications.ts +3 -6
  280. package/src/cli/commands/oauth/connect-surface-guidance.test.ts +16 -0
  281. package/src/cli/commands/oauth/connect-surface-guidance.ts +25 -7
  282. package/src/cli/commands/oauth/connect.test.ts +26 -1
  283. package/src/cli/commands/oauth/connect.ts +4 -1
  284. package/src/cli/commands/oauth/index.help.ts +3 -1
  285. package/src/cli/commands/oauth/providers.ts +3 -0
  286. package/src/cli/commands/plugins.help.ts +25 -2
  287. package/src/cli/commands/plugins.ts +289 -10
  288. package/src/cli/commands/schedules.help.ts +14 -12
  289. package/src/cli/commands/schedules.ts +19 -5
  290. package/src/cli/commands/tools.help.ts +4 -2
  291. package/src/cli/commands/tools.ts +38 -0
  292. package/src/cli/lib/__tests__/inspect-plugin.test.ts +1 -0
  293. package/src/cli/lib/__tests__/install-from-github.test.ts +79 -0
  294. package/src/cli/lib/__tests__/install-from-platform.test.ts +47 -1
  295. package/src/cli/lib/__tests__/plugin-surfaces.test.ts +86 -7
  296. package/src/cli/lib/__tests__/upgrade-plugin.test.ts +122 -0
  297. package/src/cli/lib/install-from-github.ts +61 -0
  298. package/src/cli/lib/install-from-platform.ts +13 -2
  299. package/src/cli/lib/plugin-surfaces.ts +124 -4
  300. package/src/cli/lib/upgrade-plugin.ts +18 -0
  301. package/src/cli.ts +1 -1
  302. package/src/config/__tests__/deployment-context-defaults.test.ts +73 -0
  303. package/src/config/__tests__/memory-retrospective-schema.test.ts +40 -0
  304. package/src/config/__tests__/plugin-updates-schema.test.ts +43 -0
  305. package/src/config/__tests__/profile-tool-support.test.ts +131 -0
  306. package/src/config/bundled-skills/AGENTS.md +1 -1
  307. package/src/config/bundled-skills/app-builder/SKILL.md +1 -1
  308. package/src/config/bundled-skills/app-builder/references/RESPONSIVE.md +3 -3
  309. package/src/config/bundled-skills/schedule/SKILL.md +5 -1
  310. package/src/config/bundled-skills/schedule/TOOLS.json +2 -2
  311. package/src/config/bundled-skills/settings/TOOLS.json +2 -2
  312. package/src/config/bundled-skills/settings/tools/voice-config-update.ts +1 -0
  313. package/src/config/bundled-skills/subagent/SKILL.md +53 -22
  314. package/src/config/bundled-skills/subagent/TOOLS.json +14 -13
  315. package/src/config/bundled-skills/visualize/SKILL.md +1 -1
  316. package/src/config/default-profile-catalog.ts +6 -2
  317. package/src/config/feature-flag-registry.json +25 -17
  318. package/src/config/llm-resolver.ts +45 -15
  319. package/src/config/loader.ts +70 -1
  320. package/src/config/profile-tool-support.ts +110 -0
  321. package/src/config/sanitize-for-transfer.ts +11 -0
  322. package/src/config/schema.ts +4 -0
  323. package/src/config/schemas/__tests__/stt.test.ts +20 -5
  324. package/src/config/schemas/channels.ts +13 -0
  325. package/src/config/schemas/memory-retrospective.ts +25 -0
  326. package/src/config/schemas/plugin-updates.ts +55 -0
  327. package/src/config/schemas/services.ts +8 -4
  328. package/src/config/schemas/stt.ts +17 -5
  329. package/src/config/schemas/timeouts.ts +1 -1
  330. package/src/contacts/__tests__/guardian-delivery-reader.test.ts +16 -0
  331. package/src/contacts/guardian-delivery-reader.ts +29 -2
  332. package/src/context/compactor.ts +4 -4
  333. package/src/context/post-turn-tool-result-truncation.ts +32 -16
  334. package/src/context/strip-injections.ts +42 -0
  335. package/src/context/tool-result-spool.ts +32 -18
  336. package/src/credential-execution/ces-runtime.ts +3 -0
  337. package/src/credential-execution/client.ts +22 -1
  338. package/src/credential-execution/process-manager.test.ts +99 -0
  339. package/src/credential-execution/process-manager.ts +62 -16
  340. package/src/daemon/__tests__/abort-null-controller.test.ts +141 -5
  341. package/src/daemon/__tests__/conversation-surfaces-launch.test.ts +8 -12
  342. package/src/daemon/__tests__/install-assistant-command.test.ts +234 -0
  343. package/src/daemon/__tests__/turn-tail-assistant-reply-notify.test.ts +2 -4
  344. package/src/daemon/__tests__/turn-tail-deleted-conversation.test.ts +20 -29
  345. package/src/daemon/channel-ui-capability.ts +4 -4
  346. package/src/daemon/conversation-agent-loop-handlers.ts +117 -61
  347. package/src/daemon/conversation-agent-loop.ts +318 -148
  348. package/src/daemon/conversation-attachments.ts +1 -1
  349. package/src/daemon/conversation-error.ts +8 -1
  350. package/src/daemon/conversation-initial-prompt.ts +16 -38
  351. package/src/daemon/conversation-lifecycle.ts +154 -32
  352. package/src/daemon/conversation-messaging.ts +106 -43
  353. package/src/daemon/conversation-process.ts +131 -33
  354. package/src/daemon/conversation-queue-manager.ts +10 -0
  355. package/src/daemon/conversation-runtime-assembly.ts +9 -4
  356. package/src/daemon/conversation-store.ts +52 -3
  357. package/src/daemon/conversation-surface-state.ts +9 -0
  358. package/src/daemon/conversation-surfaces.ts +403 -210
  359. package/src/daemon/conversation-tool-setup.ts +56 -7
  360. package/src/daemon/conversation-turn-finalize.ts +134 -89
  361. package/src/daemon/conversation.ts +111 -27
  362. package/src/daemon/disk-pressure-guard-lifecycle.ts +2 -2
  363. package/src/daemon/disk-pressure-guard.ts +3 -3
  364. package/src/daemon/doordash-steps.ts +5 -5
  365. package/src/daemon/external-plugins-bootstrap.ts +7 -1
  366. package/src/daemon/handlers/config-channels.ts +48 -5
  367. package/src/daemon/handlers/conversation-history.ts +1 -1
  368. package/src/daemon/handlers/conversations.ts +62 -17
  369. package/src/daemon/handlers/shared.ts +12 -0
  370. package/src/daemon/handlers/skills.ts +11 -1
  371. package/src/daemon/identity-helpers.ts +38 -10
  372. package/src/daemon/install-assistant-command.ts +322 -0
  373. package/src/daemon/lifecycle.ts +14 -3
  374. package/src/daemon/message-types/conversations.ts +6 -7
  375. package/src/daemon/message-types/sync.ts +1 -0
  376. package/src/daemon/persist-media-references.ts +23 -14
  377. package/src/daemon/process-message.ts +6 -6
  378. package/src/daemon/tool-setup-types.ts +19 -122
  379. package/src/daemon/tool-side-effects.ts +17 -94
  380. package/src/daemon/trust-context-types.ts +39 -0
  381. package/src/daemon/trust-context.ts +14 -6
  382. package/src/daemon/turn-tail-chain.ts +73 -0
  383. package/src/daemon/web-search-history.ts +47 -33
  384. package/src/heartbeat/heartbeat-service.ts +6 -6
  385. package/src/ipc/__tests__/socket-path.test.ts +21 -0
  386. package/src/ipc/assistant-server.ts +17 -10
  387. package/src/ipc/cli-client.ts +10 -1
  388. package/src/ipc/routes/__tests__/documents-sync-ipc-routes.test.ts +56 -0
  389. package/src/ipc/routes/documents-sync-ipc-routes.ts +42 -0
  390. package/src/ipc/socket-cleanup.ts +9 -7
  391. package/src/ipc/socket-path.ts +4 -96
  392. package/src/live-voice/__tests__/live-voice-archive.test.ts +16 -16
  393. package/src/live-voice/__tests__/live-voice-attach-image.test.ts +209 -0
  394. package/src/live-voice/__tests__/live-voice-connection.test.ts +20 -0
  395. package/src/live-voice/__tests__/live-voice-integration.test.ts +7 -5
  396. package/src/live-voice/__tests__/live-voice-session-telemetry.test.ts +265 -0
  397. package/src/live-voice/__tests__/live-voice-stt.test.ts +6 -1
  398. package/src/live-voice/__tests__/live-voice-vad.test.ts +35 -15
  399. package/src/live-voice/__tests__/protocol.test.ts +52 -0
  400. package/src/live-voice/__tests__/runtime-websocket-shell.test.ts +3 -0
  401. package/src/live-voice/live-voice-archive.ts +13 -13
  402. package/src/live-voice/live-voice-connection.ts +7 -1
  403. package/src/live-voice/live-voice-photo.ts +169 -0
  404. package/src/live-voice/live-voice-session.ts +108 -4
  405. package/src/live-voice/protocol.ts +89 -1
  406. package/src/mcp/client.ts +10 -0
  407. package/src/messaging/providers/__tests__/callback-routing.test.ts +5 -1
  408. package/src/messaging/providers/__tests__/transport-dispatch.test.ts +66 -4
  409. package/src/messaging/providers/callback-routing.ts +1 -0
  410. package/src/messaging/providers/discord/api.ts +312 -0
  411. package/src/messaging/providers/discord/dm-delivery.test.ts +204 -0
  412. package/src/messaging/providers/discord/render.test.ts +204 -0
  413. package/src/messaging/providers/discord/render.ts +195 -0
  414. package/src/messaging/providers/discord/send.test.ts +179 -0
  415. package/src/messaging/providers/discord/send.ts +212 -0
  416. package/src/messaging/providers/discord/transport.ts +81 -0
  417. package/src/messaging/providers/index.ts +3 -1
  418. package/src/messaging/providers/retry-policy.test.ts +247 -0
  419. package/src/messaging/providers/retry-policy.ts +206 -0
  420. package/src/messaging/providers/slack/mention-source.test.ts +285 -0
  421. package/src/messaging/providers/slack/mention-source.ts +287 -0
  422. package/src/messaging/providers/telegram-bot/api.ts +47 -130
  423. package/src/messaging/providers/whatsapp/api.ts +47 -136
  424. package/src/monitoring/__tests__/db-integrity-sample.test.ts +4 -1
  425. package/src/monitoring/__tests__/plugin-auto-update.test.ts +332 -0
  426. package/src/monitoring/db-integrity-sample.ts +6 -1
  427. package/src/monitoring/plugin-auto-update.ts +359 -0
  428. package/src/monitoring/resource-sampler.ts +21 -5
  429. package/src/monitoring/worker.ts +13 -0
  430. package/src/notifications/__tests__/assistant-reply-producer.test.ts +375 -2
  431. package/src/notifications/__tests__/broadcaster.test.ts +37 -1
  432. package/src/notifications/__tests__/copy-composer.test.ts +165 -0
  433. package/src/notifications/__tests__/emit-signal-pipeline-failure.test.ts +205 -0
  434. package/src/notifications/__tests__/notification-utils.test.ts +249 -0
  435. package/src/notifications/adapters/platform.ts +31 -2
  436. package/src/notifications/assistant-reply-producer.ts +79 -32
  437. package/src/notifications/broadcaster.ts +30 -10
  438. package/src/notifications/copy-composer.ts +69 -0
  439. package/src/notifications/emit-signal.ts +82 -15
  440. package/src/notifications/events-store.ts +10 -2
  441. package/src/notifications/home-feed-side-effect.ts +6 -3
  442. package/src/notifications/notification-utils.ts +173 -5
  443. package/src/notifications/signal.ts +12 -0
  444. package/src/oauth/AGENTS.md +4 -2
  445. package/src/oauth/byo-connection.ts +6 -1
  446. package/src/oauth/credential-token-resolver.ts +3 -10
  447. package/src/oauth/manual-token-connection.ts +29 -64
  448. package/src/oauth/manual-token-providers.ts +62 -0
  449. package/src/oauth/provider-serializer.ts +13 -0
  450. package/src/oauth/seed-providers.ts +62 -11
  451. package/src/onboarding/onboarding-events-store.ts +91 -1
  452. package/src/permissions/question-prompter.test.ts +4 -0
  453. package/src/permissions/question-prompter.ts +21 -4
  454. package/src/persistence/__tests__/conversation-lineage.test.ts +248 -0
  455. package/src/persistence/attachments-store.ts +15 -15
  456. package/src/persistence/conversation-attention-store.ts +30 -10
  457. package/src/persistence/conversation-bootstrap.ts +17 -0
  458. package/src/persistence/conversation-crud.ts +363 -97
  459. package/src/persistence/conversation-group-migration.ts +1 -1
  460. package/src/persistence/conversation-key-store.ts +38 -10
  461. package/src/persistence/conversation-lineage.ts +226 -0
  462. package/src/persistence/conversation-plugin-facade.ts +28 -3
  463. package/src/persistence/conversation-queries.ts +207 -33
  464. package/src/persistence/conversation-types.ts +107 -1
  465. package/src/persistence/jobs-store.ts +32 -8
  466. package/src/persistence/llm-usage-store.ts +22 -0
  467. package/src/persistence/migrations/361-normalize-managed-connection-rows.test.ts +168 -0
  468. package/src/persistence/migrations/361-normalize-managed-connection-rows.ts +76 -0
  469. package/src/persistence/migrations/362-add-conversation-subagent-kind.test.ts +187 -0
  470. package/src/persistence/migrations/362-add-conversation-subagent-kind.ts +93 -0
  471. package/src/persistence/migrations/363-backfill-schedule-inference-profile.test.ts +229 -0
  472. package/src/persistence/migrations/363-backfill-schedule-inference-profile.ts +127 -0
  473. package/src/persistence/migrations/364-add-schedule-source-key.test.ts +120 -0
  474. package/src/persistence/migrations/364-add-schedule-source-key.ts +43 -0
  475. package/src/persistence/migrations/365-add-conversation-fork-strategy.ts +27 -0
  476. package/src/persistence/schema/conversations.ts +32 -0
  477. package/src/persistence/schema/infrastructure.ts +3 -0
  478. package/src/persistence/steps.ts +30 -0
  479. package/src/persistence/subagent-store.ts +148 -6
  480. package/src/platform/managed-speech.test.ts +20 -0
  481. package/src/platform/managed-speech.ts +9 -0
  482. package/src/plugin-api/index.ts +8 -0
  483. package/src/plugin-api/webhook-url.ts +151 -0
  484. package/src/plugins/__tests__/installed-plugin-dirs.test.ts +82 -0
  485. package/src/plugins/__tests__/source-fingerprint.test.ts +5 -1
  486. package/src/plugins/collect-source-versions.ts +4 -27
  487. package/src/plugins/defaults/exploration-drift/hooks/post-tool-use.ts +5 -5
  488. package/src/plugins/defaults/exploration-drift/package.json +1 -1
  489. package/src/plugins/defaults/image-recovery/hooks/post-model-call.ts +4 -2
  490. package/src/plugins/defaults/image-recovery/recover.ts +67 -57
  491. package/src/plugins/defaults/index.ts +1 -1
  492. package/src/plugins/defaults/memory/__tests__/conversation-queries.test.ts +64 -18
  493. package/src/plugins/defaults/memory/__tests__/jobs-worker-outcome-contract.test.ts +242 -0
  494. package/src/plugins/defaults/memory/__tests__/jobs-worker-retrospective-sweep.test.ts +24 -1
  495. package/src/plugins/defaults/memory/__tests__/memory-retrospective-enqueue.test.ts +48 -3
  496. package/src/plugins/defaults/memory/__tests__/memory-retrospective-job.test.ts +511 -19
  497. package/src/plugins/defaults/memory/__tests__/memory-retrospective-prompt.test.ts +39 -3
  498. package/src/plugins/defaults/memory/__tests__/memory-retrospective-provider-path.test.ts +493 -0
  499. package/src/plugins/defaults/memory/__tests__/memory-retrospective-wake-chain.test.ts +5 -4
  500. package/src/plugins/defaults/memory/__tests__/memory-tier-boundary-guard.test.ts +5 -1
  501. package/src/plugins/defaults/memory/job-handlers.ts +87 -5
  502. package/src/plugins/defaults/memory/jobs-worker.ts +60 -6
  503. package/src/plugins/defaults/memory/memory-retrospective-constants.ts +11 -0
  504. package/src/plugins/defaults/memory/memory-retrospective-enqueue.ts +29 -0
  505. package/src/plugins/defaults/memory/memory-retrospective-job.ts +342 -60
  506. package/src/plugins/defaults/memory/memory-retrospective-prompt.ts +23 -3
  507. package/src/plugins/defaults/memory/substrate/consolidation-lock.ts +9 -4
  508. package/src/plugins/defaults/memory/substrate/sweep-job.ts +7 -0
  509. package/src/plugins/defaults/memory/v1/graph/injection.ts +1 -1
  510. package/src/plugins/defaults/memory/v2/router.ts +8 -0
  511. package/src/plugins/defaults/turn-context/unified-turn-context.ts +1 -2
  512. package/src/plugins/external-plugin-loader.ts +17 -8
  513. package/src/plugins/installed-plugin-dirs.ts +101 -0
  514. package/src/plugins/mtime-cache.ts +34 -22
  515. package/src/plugins/types.ts +69 -8
  516. package/src/prompts/persona-resolver.ts +6 -2
  517. package/src/prompts/system-prompt.ts +72 -17
  518. package/src/providers/__tests__/inference.test.ts +40 -0
  519. package/src/providers/__tests__/preflight-resolved-config.test.ts +58 -1
  520. package/src/providers/__tests__/retry-callsite.test.ts +67 -0
  521. package/src/providers/__tests__/vellum-connection-routing.test.ts +42 -11
  522. package/src/providers/anthropic/client.ts +112 -95
  523. package/src/providers/connection-resolution.ts +35 -1
  524. package/src/providers/gemini/client.ts +5 -5
  525. package/src/providers/inference/adapter-factory.ts +16 -10
  526. package/src/providers/inference/auth.ts +16 -0
  527. package/src/providers/inference/connection-availability.ts +164 -12
  528. package/src/providers/inference/connections.ts +25 -1
  529. package/src/providers/media-resolve.ts +21 -11
  530. package/src/providers/model-catalog.ts +15 -0
  531. package/src/providers/openai/chat-completions-provider.ts +4 -4
  532. package/src/providers/openai/responses-provider.ts +12 -25
  533. package/src/providers/registry.ts +9 -4
  534. package/src/providers/retry.ts +25 -0
  535. package/src/providers/server-tool-pairing.ts +134 -0
  536. package/src/providers/speech-to-text/__tests__/resolve.test.ts +187 -6
  537. package/src/providers/speech-to-text/deepgram.ts +1 -0
  538. package/src/providers/speech-to-text/resolve.ts +80 -10
  539. package/src/providers/speech-to-text/vellum-managed.ts +7 -1
  540. package/src/providers/speech-to-text/vellum-speech-relay-connection.ts +3 -1
  541. package/src/providers/types.ts +26 -6
  542. package/src/providers/vellum-model-routing.ts +6 -7
  543. package/src/runtime/AGENTS.md +2 -0
  544. package/src/runtime/__tests__/agent-wake.test.ts +16 -16
  545. package/src/runtime/__tests__/background-job-runner.test.ts +0 -144
  546. package/src/runtime/__tests__/desktop-presence.test.ts +162 -0
  547. package/src/runtime/agent-wake.ts +63 -24
  548. package/src/runtime/assistant-event-hub.ts +87 -12
  549. package/src/runtime/auth/__tests__/guard-tests.test.ts +2 -0
  550. package/src/runtime/auth/__tests__/scopes.test.ts +20 -0
  551. package/src/runtime/auth/same-actor.ts +56 -8
  552. package/src/runtime/auth/scopes.ts +12 -0
  553. package/src/runtime/auth/types.ts +2 -0
  554. package/src/runtime/background-job-runner.ts +5 -27
  555. package/src/runtime/channel-readiness-service.ts +59 -3
  556. package/src/runtime/channel-readiness-types.ts +18 -0
  557. package/src/runtime/desktop-presence.ts +69 -0
  558. package/src/runtime/http-server.ts +12 -0
  559. package/src/runtime/local-actor-identity.ts +11 -9
  560. package/src/runtime/migrations/__tests__/vbundle-symlink-streaming-importer.test.ts +0 -0
  561. package/src/runtime/routes/__tests__/channel-verification-routes.test.ts +1 -1
  562. package/src/runtime/routes/__tests__/client-routes.test.ts +186 -14
  563. package/src/runtime/routes/__tests__/conversation-list-routes.test.ts +547 -1
  564. package/src/runtime/routes/__tests__/conversation-management-routes.test.ts +162 -7
  565. package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +14 -10
  566. package/src/runtime/routes/__tests__/inference-profiles-routes.test.ts +340 -3
  567. package/src/runtime/routes/__tests__/llm-call-sites-routes.test.ts +28 -1
  568. package/src/runtime/routes/__tests__/plugins-routes.test.ts +173 -1
  569. package/src/runtime/routes/__tests__/question-routes.test.ts +64 -0
  570. package/src/runtime/routes/__tests__/retrospective-routes.test.ts +20 -0
  571. package/src/runtime/routes/__tests__/skill-history-route.test.ts +116 -0
  572. package/src/runtime/routes/__tests__/wake-conversation-routes.test.ts +60 -2
  573. package/src/runtime/routes/archive-utils.ts +16 -12
  574. package/src/runtime/routes/attachment-routes.ts +13 -6
  575. package/src/runtime/routes/canned-reply-release.ts +54 -0
  576. package/src/runtime/routes/channel-verification-routes.ts +2 -27
  577. package/src/runtime/routes/chatgpt-subscription-auth-routes.ts +5 -0
  578. package/src/runtime/routes/client-routes.ts +114 -2
  579. package/src/runtime/routes/conversation-cli-routes.ts +10 -12
  580. package/src/runtime/routes/conversation-list-routes.ts +59 -10
  581. package/src/runtime/routes/conversation-management-routes.ts +47 -18
  582. package/src/runtime/routes/conversation-query-routes.ts +32 -53
  583. package/src/runtime/routes/conversation-routes.ts +360 -293
  584. package/src/runtime/routes/credential-prompt-routes.ts +23 -2
  585. package/src/runtime/routes/debug-routes.ts +4 -1
  586. package/src/runtime/routes/default-provider-routes.ts +7 -11
  587. package/src/runtime/routes/documents-routes.ts +44 -1
  588. package/src/runtime/routes/events-routes.ts +28 -1
  589. package/src/runtime/routes/host-app-control-routes.ts +2 -0
  590. package/src/runtime/routes/host-bash-routes.ts +2 -0
  591. package/src/runtime/routes/host-browser-routes.ts +2 -0
  592. package/src/runtime/routes/host-cu-routes.ts +2 -0
  593. package/src/runtime/routes/host-file-routes.ts +2 -0
  594. package/src/runtime/routes/host-transfer-routes.ts +4 -0
  595. package/src/runtime/routes/identity-routes.ts +6 -6
  596. package/src/runtime/routes/inbound-message-handler.ts +5 -5
  597. package/src/runtime/routes/inbound-stages/acl-enforcement.ts +7 -3
  598. package/src/runtime/routes/inbound-stages/background-dispatch.ts +14 -0
  599. package/src/runtime/routes/inbound-stages/bootstrap-intercept.ts +2 -2
  600. package/src/runtime/routes/inbound-stages/guardian-activation-intercept.test.ts +14 -11
  601. package/src/runtime/routes/inbound-stages/guardian-activation-intercept.ts +14 -17
  602. package/src/runtime/routes/inference-profile-availability-guard.ts +171 -0
  603. package/src/runtime/routes/inference-profile-session-handler.ts +26 -2
  604. package/src/runtime/routes/inference-profile-session-routes.ts +2 -1
  605. package/src/runtime/routes/inference-profiles-routes.ts +152 -73
  606. package/src/runtime/routes/inference-provider-connection-routes.ts +0 -16
  607. package/src/runtime/routes/llm-call-sites-routes.ts +13 -0
  608. package/src/runtime/routes/log-export-routes.ts +1 -1
  609. package/src/runtime/routes/notification-routes.ts +0 -14
  610. package/src/runtime/routes/oauth-providers.ts +1 -0
  611. package/src/runtime/routes/plugins-routes.ts +106 -1
  612. package/src/runtime/routes/profiler-routes.ts +4 -2
  613. package/src/runtime/routes/question-routes.ts +28 -10
  614. package/src/runtime/routes/retrospective-routes.ts +9 -3
  615. package/src/runtime/routes/schedule-routes.ts +210 -12
  616. package/src/runtime/routes/secret-routes.ts +40 -23
  617. package/src/runtime/routes/settings-routes.ts +41 -13
  618. package/src/runtime/routes/skills-routes.ts +79 -0
  619. package/src/runtime/routes/ui-snapshot-routes.ts +1 -0
  620. package/src/runtime/routes/wake-conversation-routes.ts +23 -0
  621. package/src/runtime/services/__tests__/conversation-serializer.test.ts +1 -0
  622. package/src/runtime/services/conversation-serializer.ts +24 -9
  623. package/src/runtime/sync/documents-sidecar-publish.test.ts +101 -0
  624. package/src/runtime/sync/resource-sync-events.ts +79 -1
  625. package/src/runtime/sync/worker-daemon-notify.test.ts +34 -0
  626. package/src/runtime/sync/worker-daemon-notify.ts +44 -6
  627. package/src/runtime/verification-outbound-actions.ts +250 -222
  628. package/src/runtime/verification-templates.ts +41 -30
  629. package/src/schedule/__tests__/plugin-schedule-declarations.test.ts +614 -0
  630. package/src/schedule/__tests__/plugin-schedule-reconciler.test.ts +1066 -0
  631. package/src/schedule/inference-profile.ts +64 -3
  632. package/src/schedule/plugin-schedule-declarations.ts +518 -0
  633. package/src/schedule/plugin-schedule-reconciler.ts +605 -0
  634. package/src/schedule/plugin-schedules-gate.ts +23 -0
  635. package/src/schedule/schedule-store.ts +520 -26
  636. package/src/schedule/scheduler.ts +38 -0
  637. package/src/security/credential-backend.ts +51 -4
  638. package/src/security/encrypted-store.ts +64 -42
  639. package/src/security/secure-keys.ts +55 -7
  640. package/src/skills/catalog-install.ts +20 -7
  641. package/src/skills/inline-command-runner.ts +2 -7
  642. package/src/skills/skill-history.test.ts +269 -0
  643. package/src/skills/skill-history.ts +250 -0
  644. package/src/skills/skillssh-registry.ts +1 -1
  645. package/src/stt/daemon-batch-transcriber.ts +8 -6
  646. package/src/subagent/__tests__/consult-prompt.test.ts +25 -0
  647. package/src/subagent/consult-prompt.ts +3 -1
  648. package/src/subagent/consult-transcript.ts +1 -1
  649. package/src/subagent/manager.ts +304 -44
  650. package/src/subagent/progress-events.ts +35 -0
  651. package/src/subagent/role-resolution.ts +88 -0
  652. package/src/subagent/types.ts +302 -67
  653. package/src/subagent/validate-allowlists.ts +3 -4
  654. package/src/telegram/__tests__/webhook-health.test.ts +73 -1
  655. package/src/telegram/webhook-health.ts +101 -3
  656. package/src/telemetry/__tests__/telemetry-event-fixtures.ts +2 -0
  657. package/src/telemetry/activation-funnel.ts +6 -1
  658. package/src/telemetry/live-voice-funnel.ts +75 -0
  659. package/src/telemetry/telemetry-event-sources.ts +6 -0
  660. package/src/telemetry/telemetry-wire-source.json +1 -1
  661. package/src/telemetry/telemetry-wire.generated.ts +2 -0
  662. package/src/telemetry/types.ts +23 -4
  663. package/src/telemetry/usage-telemetry-reporter.test.ts +4 -0
  664. package/src/tools/__tests__/subagent-workflow-tool-input-schemas.test.ts +1 -1
  665. package/src/tools/ask-question/ask-question-tool.test.ts +118 -3
  666. package/src/tools/ask-question/ask-question-tool.ts +56 -2
  667. package/src/tools/browser/browser-execution.ts +1 -1
  668. package/src/tools/calls/call-start.ts +8 -5
  669. package/src/tools/conversation-groups/move_to_group.ts +10 -12
  670. package/src/tools/document/document-tool.ts +11 -4
  671. package/src/tools/execution-timeout.ts +9 -2
  672. package/src/tools/executor.ts +7 -6
  673. package/src/tools/filesystem/edit.ts +1 -1
  674. package/src/tools/filesystem/list.ts +1 -1
  675. package/src/tools/filesystem/read.ts +7 -13
  676. package/src/tools/filesystem/search.ts +23 -21
  677. package/src/tools/filesystem/write.ts +4 -1
  678. package/src/tools/host-filesystem/edit.ts +7 -2
  679. package/src/tools/host-filesystem/read.ts +21 -8
  680. package/src/tools/host-filesystem/write.ts +10 -2
  681. package/src/tools/host-terminal/host-shell.ts +2 -2
  682. package/src/tools/mcp/mcp-tool-factory.ts +25 -1
  683. package/src/tools/permission-checker.ts +8 -3
  684. package/src/tools/schedule/delete.ts +13 -1
  685. package/src/tools/schedule/list.ts +14 -3
  686. package/src/tools/schedule/update.ts +56 -33
  687. package/src/tools/shared/filesystem/audio-read.ts +6 -4
  688. package/src/tools/shared/filesystem/file-ops-service.ts +176 -116
  689. package/src/tools/shared/filesystem/image-read.ts +12 -12
  690. package/src/tools/shared/filesystem/path-policy.ts +70 -5
  691. package/src/tools/shared/filesystem/size-guard.ts +6 -6
  692. package/src/tools/shared/input-misuse.ts +72 -0
  693. package/src/tools/skills/scaffold-managed.ts +82 -0
  694. package/src/tools/skills/skill-tool-factory.ts +14 -1
  695. package/src/tools/subagent/consult-deadline.ts +12 -7
  696. package/src/tools/subagent/message.ts +3 -1
  697. package/src/tools/subagent/read.ts +66 -4
  698. package/src/tools/subagent/spawn.ts +500 -78
  699. package/src/tools/tool-approval-handler.ts +45 -4
  700. package/src/tools/types.ts +22 -3
  701. package/src/tools/ui-surface/surface-shape-docs.ts +1 -1
  702. package/src/tools/ui-surface/visual-validation.ts +2 -0
  703. package/src/usage/__tests__/subagent-attribution.test.ts +109 -0
  704. package/src/usage/subagent-attribution.ts +108 -0
  705. package/src/util/__tests__/sqlite-retry.test.ts +112 -0
  706. package/src/util/abort-reasons.ts +16 -0
  707. package/src/util/ansi.ts +44 -0
  708. package/src/util/disk-usage.ts +17 -13
  709. package/src/util/image-conversion.ts +20 -13
  710. package/src/util/schedule-source-key.ts +23 -0
  711. package/src/util/worker-process.ts +7 -1
  712. package/src/workspace/git-service.ts +33 -9
  713. package/src/workspace/migrations/140-repair-seed-pinned-memory-v3-live.ts +260 -0
  714. package/src/workspace/migrations/141-stt-english-default-to-multilingual.ts +111 -0
  715. package/src/workspace/migrations/__tests__/141-stt-english-default-to-multilingual.test.ts +126 -0
  716. package/src/workspace/migrations/registry.ts +4 -0
  717. package/src/workspace/top-level-renderer.ts +15 -2
  718. package/src/__tests__/tool-side-effects-slack-dm.test.ts +0 -357
  719. package/src/daemon/install-symlink.ts +0 -207
  720. package/src/notifications/deferred-emit.ts +0 -147
@@ -95,6 +95,20 @@ interface BaseSubscriberEntry {
95
95
  connectionId: string;
96
96
  }
97
97
 
98
+ /**
99
+ * The presence states a desktop client may report. Single runtime source: the
100
+ * stored type and the route's wire enum both derive from this tuple.
101
+ */
102
+ export const DESKTOP_PRESENCE_STATES = ["active", "idle", "away"] as const;
103
+
104
+ export type DesktopPresenceState = (typeof DESKTOP_PRESENCE_STATES)[number];
105
+
106
+ export interface ClientPresence {
107
+ state: DesktopPresenceState;
108
+ /** When the daemon last heard from the client. Drives staleness. */
109
+ reportedAt: Date;
110
+ }
111
+
98
112
  interface ClientEntry extends BaseSubscriberEntry {
99
113
  type: "client";
100
114
  clientId: string;
@@ -111,6 +125,11 @@ interface ClientEntry extends BaseSubscriberEntry {
111
125
  * service-token connections that have no principal.
112
126
  */
113
127
  actorPrincipalId?: string;
128
+ /**
129
+ * Last desktop presence reported by this client, for clients that report it.
130
+ * In-memory only, so consumers must fail open when it is absent.
131
+ */
132
+ presence?: ClientPresence;
114
133
  }
115
134
 
116
135
  interface ProcessEntry extends BaseSubscriberEntry {
@@ -407,19 +426,30 @@ export class AssistantEventHub {
407
426
  }
408
427
 
409
428
  /**
410
- * Return the active client subscriber with the given clientId, or
411
- * `undefined` if no such subscriber exists.
429
+ * Yield every active client entry with the given clientId. `subscribe`
430
+ * disposes any prior entries for the same clientId, so at most one entry
431
+ * matches.
412
432
  */
413
- getClientById(clientId: string): ClientEntry | undefined {
433
+ private *activeClientEntries(clientId: string): Generator<ClientEntry> {
414
434
  for (const entry of this.subscribers) {
415
435
  if (
416
436
  entry.active &&
417
437
  entry.type === "client" &&
418
438
  entry.clientId === clientId
419
439
  ) {
420
- return entry;
440
+ yield entry;
421
441
  }
422
442
  }
443
+ }
444
+
445
+ /**
446
+ * Return the active client subscriber with the given clientId, or
447
+ * `undefined` if no such subscriber exists.
448
+ */
449
+ getClientById(clientId: string): ClientEntry | undefined {
450
+ for (const entry of this.activeClientEntries(clientId)) {
451
+ return entry;
452
+ }
423
453
  return undefined;
424
454
  }
425
455
 
@@ -435,6 +465,42 @@ export class AssistantEventHub {
435
465
  return this.getClientById(clientId)?.actorPrincipalId;
436
466
  }
437
467
 
468
+ /**
469
+ * Fill a missing `actorPrincipalId` on a live client subscription.
470
+ *
471
+ * Used by the SSE dev-bypass self-heal: when the guardian-delivery cache is
472
+ * cold at subscribe time, the registration lands without a principal; the
473
+ * route resolves it async and patches the record here. Keyed by
474
+ * `connectionId` (not clientId) so a reconnect race cannot patch the
475
+ * subscription that replaced the one being healed. No-op when the
476
+ * connection is gone or already carries a principal: this only fills a
477
+ * missing value, never overwrites one. The value must come from the
478
+ * daemon's own server-side guardian lookup, never from client input.
479
+ */
480
+ fillClientActorPrincipalId(
481
+ connectionId: string,
482
+ actorPrincipalId: string,
483
+ ): void {
484
+ for (const entry of this.subscribers) {
485
+ if (entry.connectionId !== connectionId) {
486
+ continue;
487
+ }
488
+ if (
489
+ !entry.active ||
490
+ entry.type !== "client" ||
491
+ entry.actorPrincipalId != null
492
+ ) {
493
+ return;
494
+ }
495
+ entry.actorPrincipalId = actorPrincipalId;
496
+ log.info(
497
+ { clientId: entry.clientId, connectionId },
498
+ "filled missing actorPrincipalId for client subscription",
499
+ );
500
+ return;
501
+ }
502
+ }
503
+
438
504
  /**
439
505
  * Returns true when at least one active subscriber would receive the given
440
506
  * event based on the same conversation matching rules as publish().
@@ -512,15 +578,24 @@ export class AssistantEventHub {
512
578
  */
513
579
  touchClient(clientId: string): void {
514
580
  const now = new Date();
515
- for (const entry of this.subscribers) {
516
- if (
517
- entry.active &&
518
- entry.type === "client" &&
519
- entry.clientId === clientId
520
- ) {
521
- entry.lastActiveAt = now;
522
- }
581
+ for (const entry of this.activeClientEntries(clientId)) {
582
+ entry.lastActiveAt = now;
583
+ }
584
+ }
585
+
586
+ /**
587
+ * Record the desktop presence state reported by a client. `reportedAt`
588
+ * always advances. Returns true when at least one active client entry
589
+ * matched.
590
+ */
591
+ setClientPresence(clientId: string, state: DesktopPresenceState): boolean {
592
+ const now = new Date();
593
+ let matched = false;
594
+ for (const entry of this.activeClientEntries(clientId)) {
595
+ entry.presence = { state, reportedAt: now };
596
+ matched = true;
523
597
  }
598
+ return matched;
524
599
  }
525
600
 
526
601
  /**
@@ -276,6 +276,7 @@ describe("scope profile contract", () => {
276
276
  "internal.write",
277
277
  ],
278
278
  local_v1: ["local.all"],
279
+ speech_relay_v1: ["speech.relay"],
279
280
  ui_page_v1: ["settings.read"],
280
281
  };
281
282
 
@@ -299,6 +300,7 @@ describe("scope profile contract", () => {
299
300
  "gateway_ingress_v1",
300
301
  "gateway_service_v1",
301
302
  "local_v1",
303
+ "speech_relay_v1",
302
304
  "ui_page_v1",
303
305
  ];
304
306
 
@@ -64,6 +64,26 @@ describe("resolveScopeProfile", () => {
64
64
  expect(scopes.size).toBe(7);
65
65
  });
66
66
 
67
+ test("speech_relay_v1 includes only speech.relay (ATL-1033 least-privilege)", () => {
68
+ const scopes = resolveScopeProfile("speech_relay_v1");
69
+ expect(scopes.has("speech.relay")).toBe(true);
70
+ expect(scopes.size).toBe(1);
71
+ });
72
+
73
+ test("unknown profiles resolve to no scopes, including prototype keys", () => {
74
+ // Claims come from JSON, so the ScopeProfile type does not protect at
75
+ // runtime. Unknown names and Object.prototype keys must both fail closed.
76
+ for (const profile of [
77
+ "bogus_v1",
78
+ "toString",
79
+ "constructor",
80
+ "__proto__",
81
+ ]) {
82
+ const scopes = resolveScopeProfile(profile as never);
83
+ expect(scopes.size).toBe(0);
84
+ }
85
+ });
86
+
67
87
  test("local_v1 includes only local.all", () => {
68
88
  const scopes = resolveScopeProfile("local_v1");
69
89
  expect(scopes.has("local.all")).toBe(true);
@@ -18,9 +18,12 @@
18
18
  * Both paths log a single structured warn line on rejection with the
19
19
  * shape `{ sourceActorPrincipalId, targetClientId, targetActorPrincipalId,
20
20
  * op, reason }` so that bash, file, and CU rejections render identically
21
- * in the audit log.
21
+ * in the audit log. The user-facing wording differs by path: the proxy
22
+ * result names the failing comparison, while the HTTP 403 stays generic
23
+ * because it can reach a caller who does not own the target client.
22
24
  */
23
25
  import type { HostProxyCapability } from "../../channels/types.js";
26
+ import { isHttpAuthDisabled } from "../../config/env.js";
24
27
  import { getLogger } from "../../util/logger.js";
25
28
  import type { AssistantEventHub } from "../assistant-event-hub.js";
26
29
  import { ForbiddenError } from "../routes/errors.js";
@@ -28,14 +31,39 @@ import { ForbiddenError } from "../routes/errors.js";
28
31
  const log = getLogger("same-actor");
29
32
 
30
33
  /**
31
- * Canonical user-facing rejection message. Used by both the proxy and
32
- * route paths so operators and auditors see identical wording regardless
33
- * of whether the failure surfaced as a tool-execution result or an HTTP
34
- * 403.
34
+ * Canonical user-facing rejection message for the HTTP/IPC route path,
35
+ * where the caller may be any principal and the reason must not describe
36
+ * the target client's internal state.
35
37
  */
36
38
  const REJECTION_MESSAGE =
37
39
  "Submitting actor does not match the target client's actor for this request. The targeted client's authenticated user must submit the result.";
38
40
 
41
+ /**
42
+ * Per-reason rejection messages for the proxy path.
43
+ *
44
+ * The proxy's rejection is read by the agent (and, through the transcript,
45
+ * by the user) on their own turn, so it can and should name which of the
46
+ * three comparisons failed. Collapsing them into one sentence made a
47
+ * single-user desktop report that "the submitting actor does not match the
48
+ * target client's actor" in two cases where the two actors were never
49
+ * compared at all: the turn carried no actor principal, or the target
50
+ * client registered its SSE stream without one. Those have different fixes
51
+ * — re-establish the caller's identity versus reconnect the client — and
52
+ * neither is "sign in as the other user".
53
+ *
54
+ * The route path keeps {@link REJECTION_MESSAGE}: an HTTP 403 can reach a
55
+ * caller who is not the owner, and `missing_target` versus `mismatch` would
56
+ * describe a client that is not theirs.
57
+ */
58
+ const PROXY_REJECTION_MESSAGES: Record<RejectionReason, string> = {
59
+ missing_source:
60
+ "This turn has no authenticated actor, so it cannot target a connected client. Host-proxy execution binds each request to the user who sent the message; retry from a signed-in client, or check that the assistant's guardian binding resolved.",
61
+ missing_target:
62
+ "The target client is connected but registered without an authenticated user, so it cannot be targeted. Quit and reopen the desktop app to re-register it, then retry.",
63
+ mismatch:
64
+ "Submitting actor does not match the target client's actor for this request. The target client is signed in as a different user — run `assistant clients list` to see which connected clients you own.",
65
+ };
66
+
39
67
  /** OpenAPI 403 description for `*-result` endpoints, kept identical. */
40
68
  export const SAME_ACTOR_FORBIDDEN_DESCRIPTION =
41
69
  "Submitting client does not match the targeted client, or the submitting actor's principal does not match the target client's actor.";
@@ -74,6 +102,17 @@ export interface SameActorPersistedArgs {
74
102
  targetActorPrincipalId: string | undefined;
75
103
  targetClientId: string;
76
104
  op: SameActorOp;
105
+ /**
106
+ * Fill-if-missing fallback for dev-bypass deployments: when the PERSISTED
107
+ * target principal is absent (the request was registered before the SSE
108
+ * self-heal filled the target client's hub record), re-read the live hub
109
+ * record. Only consulted when `targetActorPrincipalId` is nullish AND HTTP
110
+ * auth is disabled. A present persisted value always wins, so a
111
+ * present-but-mismatched principal still rejects, and JWT-auth deployments
112
+ * are unaffected. The hub value is set server-side at SSE registration (or
113
+ * by the self-heal), never from client input.
114
+ */
115
+ hubForMissingTarget?: Pick<AssistantEventHub, "getActorPrincipalIdForClient">;
77
116
  }
78
117
 
79
118
  export type SameActorArgs = SameActorLiveArgs;
@@ -97,7 +136,12 @@ function detectRejection(
97
136
  const { sourceActorPrincipalId, targetClientId, op } = args;
98
137
  const targetActorPrincipalId = isLive(args)
99
138
  ? args.hub.getActorPrincipalIdForClient(targetClientId)
100
- : args.targetActorPrincipalId;
139
+ : (args.targetActorPrincipalId ??
140
+ (isHttpAuthDisabled()
141
+ ? args.hubForMissingTarget?.getActorPrincipalIdForClient(
142
+ targetClientId,
143
+ )
144
+ : undefined));
101
145
 
102
146
  let reason: RejectionReason | undefined;
103
147
  if (sourceActorPrincipalId == null) {
@@ -147,14 +191,18 @@ export function enforceSameActorOrThrow(
147
191
  * on rejection (so the proxy can pass it directly back to the agent),
148
192
  * or `null` on success. Always uses the live hub lookup — proxy
149
193
  * registration runs while the target SSE subscription is active.
194
+ *
195
+ * The returned message names the failing comparison — see
196
+ * {@link PROXY_REJECTION_MESSAGES}.
150
197
  */
151
198
  export function enforceSameActorOrErrorResult(
152
199
  args: SameActorLiveArgs,
153
200
  ): { content: string; isError: true } | null {
154
- if (detectRejection(args) == null) {
201
+ const reason = detectRejection(args);
202
+ if (reason == null) {
155
203
  return null;
156
204
  }
157
- return { content: REJECTION_MESSAGE, isError: true };
205
+ return { content: PROXY_REJECTION_MESSAGES[reason], isError: true };
158
206
  }
159
207
 
160
208
  /**
@@ -38,15 +38,27 @@ const PROFILE_SCOPES: Record<ScopeProfile, ReadonlySet<Scope>> = {
38
38
  "internal.write",
39
39
  ]),
40
40
  local_v1: new Set<Scope>(["local.all"]),
41
+ // Managed speech relay only (ATL-1033): the daemon's relay-dial token must
42
+ // not open any other edge-scoped route.
43
+ speech_relay_v1: new Set<Scope>(["speech.relay"]),
41
44
  ui_page_v1: new Set<Scope>(["settings.read"]),
42
45
  };
43
46
 
47
+ const EMPTY_SCOPES: ReadonlySet<Scope> = new Set();
48
+
44
49
  // ---------------------------------------------------------------------------
45
50
  // Public API
46
51
  // ---------------------------------------------------------------------------
47
52
 
48
53
  /** Resolve a scope profile name to its set of granted scopes. */
49
54
  export function resolveScopeProfile(profile: ScopeProfile): ReadonlySet<Scope> {
55
+ // Claims come from JSON, so an unrecognized profile (e.g. minted by a newer
56
+ // peer) can reach this despite the type. Resolve it to no scopes, not a
57
+ // TypeError in the caller. The own-property check keeps inherited keys
58
+ // ("toString", "constructor") failing closed too.
59
+ if (!Object.prototype.hasOwnProperty.call(PROFILE_SCOPES, profile)) {
60
+ return EMPTY_SCOPES;
61
+ }
50
62
  return PROFILE_SCOPES[profile];
51
63
  }
52
64
 
@@ -14,6 +14,7 @@ export type ScopeProfile =
14
14
  | "gateway_ingress_v1"
15
15
  | "gateway_service_v1"
16
16
  | "local_v1"
17
+ | "speech_relay_v1"
17
18
  | "ui_page_v1";
18
19
 
19
20
  // ---------------------------------------------------------------------------
@@ -35,6 +36,7 @@ export type Scope =
35
36
  | "internal.write"
36
37
  | "feature_flags.read"
37
38
  | "feature_flags.write"
39
+ | "speech.relay"
38
40
  | "local.all";
39
41
 
40
42
  // ---------------------------------------------------------------------------
@@ -22,11 +22,6 @@ import type { LLMCallSite } from "../config/schemas/llm.js";
22
22
  import { processMessage } from "../daemon/process-message.js";
23
23
  import type { SubagentToolGateMode } from "../daemon/tool-setup-types.js";
24
24
  import type { TrustContext } from "../daemon/trust-context-types.js";
25
- import {
26
- commitDeferredConversation,
27
- discardDeferredConversation,
28
- registerDeferredConversation,
29
- } from "../notifications/deferred-emit.js";
30
25
  import { emitNotificationSignal } from "../notifications/emit-signal.js";
31
26
  import type { AttentionHints } from "../notifications/signal.js";
32
27
  import { bootstrapConversation } from "../persistence/conversation-bootstrap.js";
@@ -188,11 +183,6 @@ export interface RunBackgroundJobOptions {
188
183
  * the `assistant` role and cannot override the action prompt.
189
184
  */
190
185
  assistantSandwich?: { preamble: string; content: string; postamble: string };
191
- /**
192
- * Buffer in-band `notifications send` calls and only flush them after the
193
- * run completes successfully. See `notifications/deferred-emit.ts`.
194
- */
195
- deferNotifications?: boolean;
196
186
  /**
197
187
  * Persist the kickoff `prompt` without indexing it — no memory segments,
198
188
  * no embeddings, no lexical-index entry. Opt-in for jobs whose prompt is a
@@ -304,10 +294,6 @@ export async function runBackgroundJob(
304
294
  ...(opts.scheduleJobId ? { scheduleJobId: opts.scheduleJobId } : {}),
305
295
  });
306
296
 
307
- if (opts.deferNotifications) {
308
- registerDeferredConversation(conversation.id);
309
- }
310
-
311
297
  // Fire the sidebar-creation callback synchronously after bootstrap so
312
298
  // connected clients (macOS sidebar, etc.) see the conversation appear
313
299
  // immediately rather than after `processMessage` returns. Wrapped so a
@@ -382,23 +368,19 @@ export async function runBackgroundJob(
382
368
 
383
369
  const runResult = await Promise.race([work, timeout]);
384
370
  // Symmetric with the `work.catch` above: once `work` has won the race,
385
- // the orphan timeout promise can still reject during the await below
386
- // (commitDeferredConversation). Swallow so it doesn't surface as an
387
- // unhandled rejection that Bun can use to terminate the process.
371
+ // the orphan timeout promise can still reject if the timer fires before
372
+ // the `finally` clears it. Swallow so it doesn't surface as an unhandled
373
+ // rejection that Bun can use to terminate the process.
388
374
  timeout.catch(() => {});
389
375
  // The turn completed but its LLM call failed (e.g. an invalid provider):
390
376
  // `processMessage` reports this via `turnFailure` instead of throwing.
391
- // Rethrow so it flows through the shared failure path below (which
392
- // discards any deferred notifications) rather than committing them and
393
- // returning `ok: true`.
377
+ // Rethrow so it flows through the shared failure path below (logging +
378
+ // `activity.failed` emission) rather than returning `ok: true`.
394
379
  if (runResult.turnFailure) {
395
380
  throw new BackgroundJobTurnFailureError(
396
381
  runResult.turnFailure.failureCode,
397
382
  );
398
383
  }
399
- if (opts.deferNotifications) {
400
- await commitDeferredConversation(conversation.id);
401
- }
402
384
  return { conversationId: conversation.id, ok: true };
403
385
  } catch (err) {
404
386
  const errorKind = classifyError(err);
@@ -411,10 +393,6 @@ export async function runBackgroundJob(
411
393
  // so the structured failure result still flows to the caller.
412
394
  const conversationId = conversation?.id ?? "";
413
395
 
414
- if (opts.deferNotifications && conversationId) {
415
- discardDeferredConversation(conversationId);
416
- }
417
-
418
396
  log.error(
419
397
  {
420
398
  err: error.message,
@@ -212,6 +212,48 @@ const telegramProbe: ChannelProbe = {
212
212
  await checkIngress(true),
213
213
  ];
214
214
  },
215
+ /**
216
+ * Ask Telegram whether it is actually delivering. The local checks above
217
+ * only establish that credentials exist, which is a different claim: a
218
+ * registration that never landed leaves them all present and the channel
219
+ * dark.
220
+ *
221
+ * Three outcomes, not two:
222
+ *
223
+ * - `healthy` is the only verified pass. Telegram holds the URL this
224
+ * deployment recorded on its last successful registration, and reports
225
+ * no recent delivery error.
226
+ * - `skipped`, `unknown` and `unverified` are indeterminate. The
227
+ * preconditions are absent, Telegram could not be reached, or no
228
+ * registration was ever recorded to compare against. None is evidence of
229
+ * a fault, so none may show the channel as broken; none is evidence of
230
+ * delivery either, so none may make it ready.
231
+ * - `not_registered`, `delivery_failing` and `url_mismatch` are failures
232
+ * with a concrete cause in `detail`.
233
+ *
234
+ * The middle row is the point. Passing those three outright let an
235
+ * unreachable Telegram API or a missing precondition read as "Telegram is
236
+ * delivering", which the setup skill then reported to the user as confirmed.
237
+ */
238
+ async runRemoteChecks(): Promise<ReadinessCheckResult[]> {
239
+ // Imported here rather than at module scope: the health module pulls in
240
+ // the credential and config graph, and hoisting that into every consumer
241
+ // of this service makes unrelated tests resolve exports they never mock.
242
+ const { checkTelegramWebhookHealth } =
243
+ await import("../telegram/webhook-health.js");
244
+ const health = await checkTelegramWebhookHealth();
245
+ const indeterminate =
246
+ health.status === "skipped" ||
247
+ health.status === "unknown" ||
248
+ health.status === "unverified";
249
+ const result = check(
250
+ "webhook_delivery",
251
+ health.status === "healthy" || indeterminate,
252
+ "Telegram is delivering to this assistant",
253
+ health.detail,
254
+ );
255
+ return [indeterminate ? { ...result, indeterminate: true } : result];
256
+ },
215
257
  };
216
258
 
217
259
  // ── Email Probe ─────────────────────────────────────────────────────────────
@@ -469,10 +511,18 @@ export class ChannelReadinessService {
469
511
  remoteChecksAffectReadiness = false;
470
512
  }
471
513
 
472
- const allLocalPassed = localChecks.every((c) => c.passed);
514
+ // Readiness requires positive confirmation, not merely the absence of a
515
+ // reported fault. A check that could not establish its claim (provider
516
+ // unreachable, ownership unprovable) is not a fault, so it must not be
517
+ // listed as a reason below, but it is also not evidence, so it cannot
518
+ // make a channel ready. Collapsing the two is how a channel reports
519
+ // itself live on the strength of a check that never ran.
520
+ const verified = (c: ReadinessCheckResult): boolean =>
521
+ c.passed && !c.indeterminate;
522
+ const allLocalPassed = localChecks.every(verified);
473
523
  const allRemotePassed =
474
524
  remoteChecks && remoteChecksAffectReadiness
475
- ? remoteChecks.every((c) => c.passed)
525
+ ? remoteChecks.every(verified)
476
526
  : true;
477
527
  const ready = allLocalPassed && allRemotePassed;
478
528
 
@@ -481,7 +531,13 @@ export class ChannelReadinessService {
481
531
  ...localChecks,
482
532
  ...(remoteChecks && remoteChecksAffectReadiness ? remoteChecks : []),
483
533
  ];
484
- const anyCheckPassed = consideredChecks.some((c) => c.passed);
534
+ // Also `verified`, not `passed`. A check that established nothing is not
535
+ // evidence that setup has begun: an install with no bot token at all
536
+ // returns `skipped` from the Telegram probe, and counting that as
537
+ // progress reports an untouched workspace as `incomplete`, which is what
538
+ // sends the Channels UI down the "finish setup" path instead of the
539
+ // normal setup flow.
540
+ const anyCheckPassed = consideredChecks.some(verified);
485
541
  const setupStatus: SetupStatus = !anyCheckPassed
486
542
  ? "not_configured"
487
543
  : ready
@@ -10,8 +10,26 @@ export type SetupStatus = "not_configured" | "incomplete" | "ready";
10
10
  /** Result of a single readiness check (local or remote). */
11
11
  export interface ReadinessCheckResult {
12
12
  name: string;
13
+ /**
14
+ * Whether the check found a fault. This drives the user-facing fault
15
+ * reporting: a check that did not fail must not be shown as broken.
16
+ */
13
17
  passed: boolean;
14
18
  message: string;
19
+ /**
20
+ * Set when the check could not establish its claim either way, as opposed to
21
+ * establishing that nothing is wrong.
22
+ *
23
+ * `passed` alone cannot express this. A probe that cannot reach a provider,
24
+ * or that lacks the evidence to confirm ownership, has found no fault, so
25
+ * failing it would paint working installs broken over a network blip. But it
26
+ * has also verified nothing, so counting it toward readiness lets a channel
27
+ * report itself live on the strength of a check that never ran. Consumers
28
+ * that report faults read `passed`; consumers that require proof (readiness,
29
+ * and the setup skills that gate their success message on it) additionally
30
+ * require this to be absent or false.
31
+ */
32
+ indeterminate?: boolean;
15
33
  }
16
34
 
17
35
  /** Point-in-time snapshot of a channel's readiness state. */
@@ -0,0 +1,69 @@
1
+ /**
2
+ * Desktop presence policy.
3
+ *
4
+ * Answers exactly one question for notification producers: is the user
5
+ * demonstrably at their Mac right now? Producers use the answer to skip a
6
+ * push that the user would see on screen anyway.
7
+ *
8
+ * Fails open by design. Presence is in-memory, best-effort, and reported by
9
+ * a client that can drop off at any time, so anything short of a fresh
10
+ * `active` report from a `macos` client answers `false` and the push goes
11
+ * out. A missed notification is strictly worse than a redundant one.
12
+ */
13
+
14
+ import { getLogger } from "../util/logger.js";
15
+ import { assistantEventHub } from "./assistant-event-hub.js";
16
+
17
+ const log = getLogger("desktop-presence");
18
+
19
+ /**
20
+ * Bounds how long suppression can outlive the Mac going to sleep or dropping
21
+ * off, while still tolerating two dropped reports at the 30s report interval.
22
+ */
23
+ export const PRESENCE_STALE_AFTER_MS = 90_000;
24
+
25
+ export interface DesktopAttendanceOptions {
26
+ /**
27
+ * Only count macOS clients whose verified actor principal matches this id.
28
+ * A client that connected without a principal (legacy or service token)
29
+ * never matches a supplied id.
30
+ */
31
+ actorPrincipalId?: string;
32
+ /** Clock used for the staleness comparison. */
33
+ now?: Date;
34
+ }
35
+
36
+ /**
37
+ * Whether some macOS client has reported `active` recently enough to trust.
38
+ * Stale, absent, non-macOS, and error reads all answer `false`.
39
+ *
40
+ * Callers whose notification targets one recipient (guardian-scoped or
41
+ * otherwise per-recipient pushes) must pass that recipient's
42
+ * `actorPrincipalId`, or another user's attended Mac suppresses the push.
43
+ * Omitting it treats any attended macOS client as attendance, which only
44
+ * suits notifications with no single recipient.
45
+ */
46
+ export function isDesktopAttended(
47
+ options: DesktopAttendanceOptions = {},
48
+ ): boolean {
49
+ const { actorPrincipalId, now = new Date() } = options;
50
+ try {
51
+ return assistantEventHub.listClientsByInterface("macos").some((client) => {
52
+ if (
53
+ actorPrincipalId !== undefined &&
54
+ client.actorPrincipalId !== actorPrincipalId
55
+ ) {
56
+ return false;
57
+ }
58
+ return (
59
+ client.presence?.state === "active" &&
60
+ now.getTime() - client.presence.reportedAt.getTime() <=
61
+ PRESENCE_STALE_AFTER_MS
62
+ );
63
+ });
64
+ } catch (err) {
65
+ // Returning false sends the push, which is the safe direction.
66
+ log.warn({ err }, "desktop presence read failed; treating as unattended");
67
+ return false;
68
+ }
69
+ }
@@ -33,6 +33,10 @@ import {
33
33
  } from "../live-voice/live-voice-connection.js";
34
34
  import { getLiveVoiceSessionManager } from "../live-voice/live-voice-manager.js";
35
35
  import { resolveStreamingTranscriber } from "../providers/speech-to-text/resolve.js";
36
+ import {
37
+ startPluginScheduleReconcileSweep,
38
+ stopPluginScheduleReconcileSweep,
39
+ } from "../schedule/plugin-schedule-reconciler.js";
36
40
  import {
37
41
  activeSttStreamSessions,
38
42
  SttStreamSession,
@@ -511,12 +515,20 @@ export class RuntimeHttpServer {
511
515
 
512
516
  startTelegramWebhookHealthSweep();
513
517
  log.info("Telegram webhook health sweep started");
518
+
519
+ // Backstop for plugin enable/disable, which flips the `.disabled`
520
+ // sentinel without poking the plugin source reconcile. The readiness
521
+ // guard lives inside reconcilePluginSchedules, which no-ops while
522
+ // migrations are unready.
523
+ startPluginScheduleReconcileSweep();
524
+ log.info("Plugin schedule reconcile sweep started");
514
525
  }
515
526
 
516
527
  async stop(): Promise<void> {
517
528
  stopGuardianExpirySweep();
518
529
  stopInferenceProfileSessionReaper();
519
530
  stopTelegramWebhookHealthSweep();
531
+ stopPluginScheduleReconcileSweep();
520
532
  if (this.retrySweepTimer) {
521
533
  clearInterval(this.retrySweepTimer);
522
534
  this.retrySweepTimer = null;