@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
@@ -0,0 +1,605 @@
1
+ /**
2
+ * Level-based reconciler converging plugin `schedules/` declarations into
3
+ * `cron_jobs` rows.
4
+ *
5
+ * Each pass enumerates the installed, enabled plugins, parses their
6
+ * declarations (see `./plugin-schedule-declarations.ts`), and diffs the
7
+ * desired set against the sourced rows in the store: new declarations are
8
+ * inserted, changed ones (by `definition_hash`) updated, and rows whose
9
+ * declaration is gone (or, for a script row, no longer parses) are disarmed
10
+ * in place. The reconciler owns only the definition columns; the engine owns
11
+ * the runtime columns and the user owns `user_enabled` (both enforced in
12
+ * `./schedule-store.ts`). Rows with a null `source_key` are imperative
13
+ * schedules and are never touched.
14
+ *
15
+ * Triggers: daemon startup (`daemon/lifecycle.ts`), plugin-set convergence
16
+ * (`plugins/mtime-cache.ts` after an imperative source reconcile), the plugin
17
+ * enable/disable routes (`runtime/routes/plugins-routes.ts`), and a periodic
18
+ * backstop sweep (`runtime/http-server.ts`) that covers the sentinel writers
19
+ * outside the daemon, notably the CLI's own enable/disable.
20
+ *
21
+ * Rows this pass has not caught up with are still safe to leave armed: the
22
+ * scheduler re-reads the `.disabled` sentinel at fire time and the run-now
23
+ * route re-probes the declaration, so reconcile lag delays bookkeeping rather
24
+ * than letting a disabled plugin execute.
25
+ */
26
+
27
+ import { existsSync } from "node:fs";
28
+ import { join } from "node:path";
29
+
30
+ import { getDbMigrationReadiness } from "../daemon/daemon-readiness.js";
31
+ import { emitNotificationSignal } from "../notifications/emit-signal.js";
32
+ import type { AttentionHints } from "../notifications/signal.js";
33
+ import { isPluginDisabled } from "../plugins/disabled-state.js";
34
+ import { parsePluginManifest } from "../plugins/external-plugin-loader.js";
35
+ import { listInstalledPluginDirs } from "../plugins/installed-plugin-dirs.js";
36
+ import { getLogger } from "../util/logger.js";
37
+ import {
38
+ type DeclarationError,
39
+ parsePluginScheduleDeclarations,
40
+ type ScheduleDeclaration,
41
+ } from "./plugin-schedule-declarations.js";
42
+ import { isPluginSchedulesEnabled } from "./plugin-schedules-gate.js";
43
+ import {
44
+ type DeclaredScheduleDefinition,
45
+ disarmDeclaredSchedule,
46
+ listDeclaredSchedules,
47
+ type ScheduleJob,
48
+ upsertDeclaredSchedule,
49
+ } from "./schedule-store.js";
50
+
51
+ const log = getLogger("plugin-schedule-reconciler");
52
+
53
+ /** In-flight pass; concurrent triggers await it rather than racing. */
54
+ let reconcileInFlight: Promise<void> | null = null;
55
+
56
+ /**
57
+ * Converge plugin-declared schedules against the current on-disk plugin set.
58
+ *
59
+ * Single-flight: concurrent triggers serialize through the in-flight latch,
60
+ * so two passes never interleave their list/diff/write sequences. Never
61
+ * throws: a failed pass is logged and the next trigger retries from disk.
62
+ * No-ops while DB migrations are unready.
63
+ */
64
+ export async function reconcilePluginSchedules(): Promise<void> {
65
+ while (reconcileInFlight !== null) {
66
+ await reconcileInFlight;
67
+ }
68
+ reconcileInFlight = (async () => {
69
+ try {
70
+ await runReconcilePass();
71
+ } catch (err) {
72
+ log.error({ err }, "Plugin schedule reconcile failed");
73
+ }
74
+ })().finally(() => {
75
+ reconcileInFlight = null;
76
+ });
77
+ await reconcileInFlight;
78
+ }
79
+
80
+ interface DesiredEntry {
81
+ pluginName: string;
82
+ declaration: ScheduleDeclaration;
83
+ }
84
+
85
+ interface CollectedDeclarations {
86
+ desired: Map<string, DesiredEntry>;
87
+ errors: DeclarationError[];
88
+ manifestFailures: DeclarationError[];
89
+ }
90
+
91
+ async function runReconcilePass(): Promise<void> {
92
+ // The pass is pure DB reads/writes over cron_jobs; refuse to touch a
93
+ // partially-migrated schema (see assistant/CLAUDE.md, DB migration
94
+ // readiness gating). A skipped pass is retried by the periodic sweep.
95
+ if (!getDbMigrationReadiness().ready) {
96
+ return;
97
+ }
98
+
99
+ // The feature flag is a kill switch, not just a launch gate. While it is
100
+ // off nothing is parsed and nothing is emitted, and the desired set is
101
+ // empty, so the disarm branch below turns every declared row off on the next
102
+ // pass. Turning the flag back on re-arms them from their declarations.
103
+ const { desired, errors, manifestFailures }: CollectedDeclarations =
104
+ isPluginSchedulesEnabled()
105
+ ? await collectDesiredDeclarations()
106
+ : { desired: new Map(), errors: [], manifestFailures: [] };
107
+
108
+ const existingByKey = new Map<string, ScheduleJob>();
109
+ for (const row of listDeclaredSchedules()) {
110
+ if (row.sourceKey !== null) {
111
+ existingByKey.set(row.sourceKey, row);
112
+ }
113
+ }
114
+
115
+ for (const [sourceKey, { pluginName, declaration }] of desired) {
116
+ const row = existingByKey.get(sourceKey);
117
+ const definition = toDefinition(declaration);
118
+ const definitionChanged =
119
+ row !== undefined && row.definitionHash !== definition.definitionHash;
120
+ const changesArmedRow = definitionChanged && row.enabled;
121
+ // A definition change can also arm a row that was disarmed (e.g. an
122
+ // upgrade flipping the declared `enabled` from false to true). That
123
+ // transition gets the arrival notification below; a plain re-enable or
124
+ // reinstall re-link (hash unchanged) stays quiet.
125
+ const armsDisarmedRow = definitionChanged && !row.enabled;
126
+ // The change notice fires once, and by the next pass the new hash is
127
+ // already stored, so `changesArmedRow` can never bring it back. A pending
128
+ // entry means the emit never reached a pipeline verdict; it is retried
129
+ // while the row is still there and still armed for this definition.
130
+ const pendingChanged =
131
+ row !== undefined &&
132
+ row.enabled &&
133
+ isEmitPending("changed", sourceKey, definition.definitionHash);
134
+ let applied: ScheduleJob;
135
+ try {
136
+ applied = await upsertDeclaredSchedule(sourceKey, definition);
137
+ } catch (err) {
138
+ log.error(
139
+ { err, sourceKey },
140
+ "Failed to apply declared schedule, skipping",
141
+ );
142
+ continue;
143
+ }
144
+ if (changesArmedRow || pendingChanged) {
145
+ emitDefinitionChanged(pluginName, declaration);
146
+ }
147
+ // A row arming without consent must never be silent: a daemon-route
148
+ // install/upgrade has no interactive consent prompt, and even a CLI
149
+ // upgrade only confirms what it staged. The arrival notification is the
150
+ // consent surface for those paths; a CLI install that already prompted
151
+ // gets one redundant, hash-deduped notification. A re-linked row (its
152
+ // `source_key` already existed, e.g. reinstall or re-enable) is not new
153
+ // unless its definition change is what armed it. A pending entry means a
154
+ // prior emit for this exact definition never reached a pipeline verdict,
155
+ // so the notification is re-attempted even though the row already exists.
156
+ const pendingDeclared = isEmitPending(
157
+ "declared",
158
+ sourceKey,
159
+ definition.definitionHash,
160
+ );
161
+ if (
162
+ applied.enabled &&
163
+ (row === undefined || armsDisarmedRow || pendingDeclared)
164
+ ) {
165
+ emitScheduleDeclared(pluginName, declaration);
166
+ }
167
+ }
168
+
169
+ // A key with a declaration error keeps its last-good row untouched: fail
170
+ // closed means the broken declaration does not load, not that a previously
171
+ // healthy schedule is torn down by its own typo. An execute row can be held
172
+ // that way because it fires the message stored on the row, which is the one
173
+ // that last validated.
174
+ //
175
+ // A script row cannot: it fires its entrypoint by absolute path, so holding
176
+ // it armed through an invalid declaration runs whatever `index.sh` now
177
+ // holds, which is exactly the content that failed to validate. Those rows
178
+ // disarm through the same path as an absent declaration and re-arm on the
179
+ // pass after the declaration parses again. An `ended` recurrence is not a
180
+ // rewrite, so it keeps the last-good handling in both modes.
181
+ const erroredKeys = new Set<string>();
182
+ for (const error of errors) {
183
+ const row = existingByKey.get(error.sourceKey);
184
+ const disarmsScriptRow = error.kind === "invalid" && row?.mode === "script";
185
+ if (!disarmsScriptRow) {
186
+ erroredKeys.add(error.sourceKey);
187
+ }
188
+ }
189
+
190
+ // Disarm rows whose declaration is absent from the desired set (plugin
191
+ // uninstalled or disabled, or the schedule file removed), plus the script
192
+ // rows an invalid declaration left out of `erroredKeys` above. Teardown
193
+ // deliberately does not ride plugin shutdown hooks: uninstalling a disabled
194
+ // plugin skips them entirely (`cli/lib/uninstall-plugin.ts`), so
195
+ // directory-absence diffing here is the only reliable reap. The row and its
196
+ // runs are kept so a reinstall re-links by `source_key`.
197
+ //
198
+ // `pausedKeys` collects what this pass actually turned off, which is what
199
+ // the notification below reports. A row the user or the engine had already
200
+ // disabled is left alone, and a failed write leaves the row armed, so
201
+ // neither claims a pause that did not happen.
202
+ const pausedKeys = new Set<string>();
203
+ for (const [sourceKey, row] of existingByKey) {
204
+ if (desired.has(sourceKey) || erroredKeys.has(sourceKey)) {
205
+ continue;
206
+ }
207
+ try {
208
+ if (await disarmDeclaredSchedule(row.id)) {
209
+ pausedKeys.add(sourceKey);
210
+ }
211
+ } catch (err) {
212
+ log.error(
213
+ { err, sourceKey, scheduleId: row.id },
214
+ "Failed to disarm declared schedule, skipping",
215
+ );
216
+ }
217
+ }
218
+
219
+ // Manifest failures ride the same emit path as declaration errors but are
220
+ // kept out of `erroredKeys` above: an errored key preserves its last-good
221
+ // row, while a manifest failure must let the disarm loop pause the rows.
222
+ for (const error of [...errors, ...manifestFailures]) {
223
+ if (isCompletedRecurrence(error, existingByKey.get(error.sourceKey))) {
224
+ continue;
225
+ }
226
+ emitDefinitionError(error, pausedKeys.has(error.sourceKey));
227
+ }
228
+ }
229
+
230
+ /**
231
+ * True when an `ended` recurrence is the expected end of a bounded schedule
232
+ * rather than something to tell the user about. Three rows qualify: one the
233
+ * engine latched on its last occurrence, which zeroes `next_run_at` with a
234
+ * run behind it; one the user turned off; and one that shipped disabled and
235
+ * was never armed, which has `next_run_at` zeroed with no run behind it and
236
+ * no user choice recorded. None of them lost a firing the user expected.
237
+ *
238
+ * A row this reconciler disarmed does not qualify, and cannot be mistaken
239
+ * for the never-armed one because it keeps the stale non-zero `next_run_at`
240
+ * it was armed with. `enabled = false` there means the plugin is disabled or
241
+ * its declaration was briefly absent, and once the plugin comes back the
242
+ * ended recurrence is a dead schedule the user has to hear about. An ended
243
+ * declaration whose row is still armed loses firings, and one with no row at
244
+ * all arrives dead; both surface.
245
+ */
246
+ function isCompletedRecurrence(
247
+ error: DeclarationError,
248
+ row: ScheduleJob | undefined,
249
+ ): boolean {
250
+ if (error.kind !== "ended" || row === undefined) {
251
+ return false;
252
+ }
253
+ const engineLatched = row.nextRunAt === 0 && row.lastRunAt != null;
254
+ const insertedDisabled =
255
+ !row.enabled &&
256
+ row.nextRunAt === 0 &&
257
+ row.lastRunAt == null &&
258
+ row.userEnabled == null;
259
+ return engineLatched || row.userEnabled === false || insertedDisabled;
260
+ }
261
+
262
+ /**
263
+ * Enumerate installed plugins ({@link listInstalledPluginDirs}, the same walk
264
+ * as the plugin source collector) and gather their schedule declarations.
265
+ * Identity = the directory basename, mirroring `parsePluginManifest`.
266
+ *
267
+ * Disabled plugins and plugins without a `schedules/` directory are skipped
268
+ * entirely; in particular, only schedule-declaring plugins pay the manifest
269
+ * parse each pass. A schedule-declaring plugin whose manifest fails
270
+ * `parsePluginManifest` (unreadable or schema-invalid `package.json`) also
271
+ * contributes nothing to the desired set: the runtime loader refuses to
272
+ * bring such a plugin up, so its schedules must not stay armed either. Each
273
+ * of its declared schedules comes back in `manifestFailures` so the pause is
274
+ * surfaced with the same visibility as a bad declaration.
275
+ */
276
+ async function collectDesiredDeclarations(): Promise<CollectedDeclarations> {
277
+ const desired = new Map<string, DesiredEntry>();
278
+ const errors: DeclarationError[] = [];
279
+ const manifestFailures: DeclarationError[] = [];
280
+
281
+ for (const { name, dir } of listInstalledPluginDirs()) {
282
+ if (isPluginDisabled(name)) {
283
+ continue;
284
+ }
285
+ if (!existsSync(join(dir, "schedules"))) {
286
+ continue;
287
+ }
288
+ const parsed = parsePluginScheduleDeclarations(dir, name);
289
+ if ((await parsePluginManifest(dir, { quiet: true })) === undefined) {
290
+ // The reason states the fault only. Whether a row went off over it is
291
+ // the `paused` field's job, which is derived from the disarm outcome.
292
+ const reason = "the plugin's package.json could not be read or validated";
293
+ for (const declared of [
294
+ ...parsed.declarations.map((d) => ({
295
+ scheduleName: d.name,
296
+ sourceKey: d.sourceKey,
297
+ })),
298
+ ...parsed.errors,
299
+ ]) {
300
+ manifestFailures.push({
301
+ pluginName: name,
302
+ scheduleName: declared.scheduleName,
303
+ sourceKey: declared.sourceKey,
304
+ reason,
305
+ kind: "invalid",
306
+ });
307
+ }
308
+ continue;
309
+ }
310
+ for (const declaration of parsed.declarations) {
311
+ desired.set(declaration.sourceKey, { pluginName: name, declaration });
312
+ }
313
+ errors.push(...parsed.errors);
314
+ }
315
+
316
+ return { desired, errors, manifestFailures };
317
+ }
318
+
319
+ function toDefinition(
320
+ declaration: ScheduleDeclaration,
321
+ ): DeclaredScheduleDefinition {
322
+ const { config } = declaration;
323
+ return {
324
+ name: declaration.name,
325
+ description: config.description ?? undefined,
326
+ syntax: config.syntax,
327
+ expression: config.expression,
328
+ timezone: config.timezone,
329
+ // Script-mode runs ignore the message column (the engine executes
330
+ // `script`); it is non-null in the schema, so store an empty string.
331
+ message: declaration.message ?? "",
332
+ script: declaration.scriptInvocation,
333
+ mode: declaration.mode,
334
+ maxRetries: config.maxRetries ?? undefined,
335
+ retryBackoffMs: config.retryBackoffMs ?? undefined,
336
+ quiet: config.quiet ?? undefined,
337
+ inferenceProfile: config.inferenceProfile,
338
+ timeoutMs: config.timeoutMs,
339
+ enabled: config.enabled,
340
+ definitionHash: declaration.definitionHash,
341
+ };
342
+ }
343
+
344
+ // Same shape as the background-job failure notification: passive home-feed
345
+ // surfacing, no action required.
346
+ const DEFINITION_NOTIFICATION_HINTS: AttentionHints = {
347
+ requiresAction: false,
348
+ urgency: "medium",
349
+ isAsyncBackground: true,
350
+ visibleInSourceNow: false,
351
+ };
352
+
353
+ /**
354
+ * Emit one definition-lifecycle signal. Resolves `true` when the pipeline
355
+ * reached a verdict (dispatched, deduplicated, or suppressed) and `false`
356
+ * when it failed outright, so a caller latching a dedupe guard can retry a
357
+ * transient failure on a later pass.
358
+ */
359
+ async function emitDefinitionSignal(
360
+ sourceEventName: string,
361
+ sourceKey: string,
362
+ dedupeKey: string,
363
+ contextPayload: Record<string, unknown>,
364
+ ): Promise<boolean> {
365
+ try {
366
+ const result = await emitNotificationSignal({
367
+ sourceChannel: "scheduler",
368
+ sourceContextId: sourceKey,
369
+ sourceEventName,
370
+ dedupeKey,
371
+ contextPayload,
372
+ attentionHints: DEFINITION_NOTIFICATION_HINTS,
373
+ });
374
+ // emitNotificationSignal resolves (never rejects) and flags a pipeline
375
+ // error as `pipelineFailed`; every other outcome, including dedupe and
376
+ // suppression, is a verdict.
377
+ return !result.pipelineFailed;
378
+ } catch (err) {
379
+ log.warn(
380
+ { err, sourceKey, sourceEventName },
381
+ "Failed to emit schedule definition notification",
382
+ );
383
+ return false;
384
+ }
385
+ }
386
+
387
+ /**
388
+ * UTC day of the last completed definition-error emit per `sourceKey`. A
389
+ * persistently broken declaration re-surfaces on every pass (60s sweep); the
390
+ * downstream daily dedupe key would drop the repeats, but this guard skips
391
+ * the emit call entirely so steady-state passes do no notification work.
392
+ */
393
+ const definitionErrorEmittedDay = new Map<string, string>();
394
+
395
+ /**
396
+ * The definition-lifecycle events whose emit is retried until a pipeline
397
+ * verdict. `declared` is the consent surface for unattended arming
398
+ * (daemon-route install/upgrade) and `changed` is the only notice that an
399
+ * armed schedule's definition was rewritten; both are one-shot per definition
400
+ * hash. `error` is one-shot per UTC day and carries that day in place of a
401
+ * hash. Whatever triggered each is stored before the emit, so a transient
402
+ * pipeline failure leaves a marker the next pass acts on rather than losing
403
+ * the notification.
404
+ */
405
+ type TrackedEmitKind = "declared" | "changed" | "error";
406
+
407
+ function trackedEmitKey(kind: TrackedEmitKind, sourceKey: string): string {
408
+ return `${kind}:${sourceKey}`;
409
+ }
410
+
411
+ /**
412
+ * Token of a tracked emit that has not yet reached a pipeline verdict, keyed
413
+ * by {@link trackedEmitKey}: the definition hash the emit describes, or the
414
+ * UTC day for an `error`. Declared entries are kept across disarm so a
415
+ * reinstall re-delivers a never-delivered consent notification; the map is
416
+ * bounded by distinct declared schedules seen in this process. In-memory only:
417
+ * a daemon restart inside the failure window loses the retry, a residual
418
+ * accepted over persisting a marker.
419
+ */
420
+ const pendingEmits = new Map<string, string>();
421
+
422
+ /** Keys whose emit is awaiting its pipeline result. */
423
+ const inFlightEmits = new Set<string>();
424
+
425
+ function isEmitPending(
426
+ kind: TrackedEmitKind,
427
+ sourceKey: string,
428
+ definitionHash: string,
429
+ ): boolean {
430
+ return pendingEmits.get(trackedEmitKey(kind, sourceKey)) === definitionHash;
431
+ }
432
+
433
+ /**
434
+ * Emit one tracked event, holding a pending marker until the pipeline reaches
435
+ * a verdict. Emits for a key are serialized: while one is still evaluating, a
436
+ * later pass does not start a second. A duplicate would hit event-store dedupe
437
+ * and resolve as a verdict of its own, clearing the marker the original still
438
+ * needs if it goes on to fail and release the dedupe key. Skipping leaves the
439
+ * marker in place, so a pass after the in-flight attempt settles retries it.
440
+ */
441
+ function trackedEmit(
442
+ kind: TrackedEmitKind,
443
+ sourceKey: string,
444
+ token: string,
445
+ emit: () => Promise<boolean>,
446
+ ): void {
447
+ const key = trackedEmitKey(kind, sourceKey);
448
+ pendingEmits.set(key, token);
449
+ if (inFlightEmits.has(key)) {
450
+ return;
451
+ }
452
+ inFlightEmits.add(key);
453
+ void emit()
454
+ .then((verdict) => {
455
+ if (verdict && pendingEmits.get(key) === token) {
456
+ pendingEmits.delete(key);
457
+ }
458
+ })
459
+ .finally(() => {
460
+ inFlightEmits.delete(key);
461
+ });
462
+ }
463
+
464
+ /** Test-only: forget emit-guard state (error day-latch and tracked emits). */
465
+ export function resetDefinitionErrorEmitGuardForTests(): void {
466
+ definitionErrorEmittedDay.clear();
467
+ pendingEmits.clear();
468
+ inFlightEmits.clear();
469
+ }
470
+
471
+ /**
472
+ * Surface a malformed declaration (or a manifest failure pausing one), with
473
+ * `paused` set when this pass disarmed the row over it. Deduped per schedule
474
+ * per UTC day so a broken file doesn't spam on every pass. The day guard
475
+ * latches only after the emit resolves with a pipeline verdict; a transient
476
+ * pipeline failure is retried on the next pass instead of being silenced for
477
+ * the rest of the day. Attempts are serialized like the other tracked emits,
478
+ * so a second pass during an in-flight first one cannot latch the day on a
479
+ * deduped verdict while the original goes on to fail.
480
+ */
481
+ function emitDefinitionError(error: DeclarationError, paused: boolean): void {
482
+ const day = new Date().toISOString().slice(0, 10);
483
+ if (definitionErrorEmittedDay.get(error.sourceKey) === day) {
484
+ return;
485
+ }
486
+ trackedEmit("error", error.sourceKey, day, () =>
487
+ emitDefinitionSignal(
488
+ "schedule.definition_error",
489
+ error.sourceKey,
490
+ `schedule-definition-error:${error.sourceKey}:${day}`,
491
+ {
492
+ pluginName: error.pluginName,
493
+ scheduleName: error.scheduleName,
494
+ sourceKey: error.sourceKey,
495
+ reason: error.reason,
496
+ paused,
497
+ },
498
+ ).then((completed) => {
499
+ if (completed) {
500
+ definitionErrorEmittedDay.set(error.sourceKey, day);
501
+ }
502
+ return completed;
503
+ }),
504
+ );
505
+ }
506
+
507
+ /**
508
+ * Surface a plugin upgrade rewriting an armed schedule's definition, so the
509
+ * user learns the thing firing on their behalf changed. Deduped by the new
510
+ * hash so concurrent triggers emit once per change, and retried while the
511
+ * pipeline has not reached a verdict.
512
+ */
513
+ function emitDefinitionChanged(
514
+ pluginName: string,
515
+ declaration: ScheduleDeclaration,
516
+ ): void {
517
+ trackedEmit(
518
+ "changed",
519
+ declaration.sourceKey,
520
+ declaration.definitionHash,
521
+ () =>
522
+ emitDefinitionSignal(
523
+ "schedule.definition_changed",
524
+ declaration.sourceKey,
525
+ `schedule-definition-changed:${declaration.sourceKey}:${declaration.definitionHash}`,
526
+ {
527
+ pluginName,
528
+ scheduleName: declaration.name,
529
+ sourceKey: declaration.sourceKey,
530
+ },
531
+ ),
532
+ );
533
+ }
534
+
535
+ /**
536
+ * Surface a declared schedule arming (its row was created by this pass, or a
537
+ * definition change armed a disarmed row). Deduped by definition hash, so
538
+ * the CLI-install case (which already prompted for consent) collapses to a
539
+ * single notification and concurrent triggers emit once.
540
+ */
541
+ function emitScheduleDeclared(
542
+ pluginName: string,
543
+ declaration: ScheduleDeclaration,
544
+ ): void {
545
+ trackedEmit(
546
+ "declared",
547
+ declaration.sourceKey,
548
+ declaration.definitionHash,
549
+ () =>
550
+ emitDefinitionSignal(
551
+ "schedule.declared",
552
+ declaration.sourceKey,
553
+ `schedule-declared:${declaration.sourceKey}:${declaration.definitionHash}`,
554
+ {
555
+ pluginName,
556
+ scheduleName: declaration.name,
557
+ sourceKey: declaration.sourceKey,
558
+ cadence: declaration.config.expression,
559
+ },
560
+ ),
561
+ );
562
+ }
563
+
564
+ // ── Periodic backstop sweep ─────────────────────────────────────────────
565
+
566
+ /**
567
+ * Backstop interval. Enable/disable flows write the `.disabled` sentinel
568
+ * without poking the plugin source reconcile, so this bounds how long a
569
+ * toggle can go unreflected in the rows.
570
+ */
571
+ const SWEEP_INTERVAL_MS = 60_000;
572
+
573
+ let sweepTimer: ReturnType<typeof setInterval> | null = null;
574
+
575
+ /** Guard against a slow pass stacking further passes behind the latch. */
576
+ let sweepInProgress = false;
577
+
578
+ /**
579
+ * Start the periodic reconcile sweep. Idempotent: repeat calls reuse the
580
+ * timer. The DB-readiness guard lives inside {@link reconcilePluginSchedules},
581
+ * which no-ops while migrations are unready.
582
+ */
583
+ export function startPluginScheduleReconcileSweep(): void {
584
+ if (sweepTimer) {
585
+ return;
586
+ }
587
+ sweepTimer = setInterval(() => {
588
+ if (sweepInProgress) {
589
+ return;
590
+ }
591
+ sweepInProgress = true;
592
+ void reconcilePluginSchedules().finally(() => {
593
+ sweepInProgress = false;
594
+ });
595
+ }, SWEEP_INTERVAL_MS);
596
+ }
597
+
598
+ /** Stop the periodic reconcile sweep. Used in tests and shutdown. */
599
+ export function stopPluginScheduleReconcileSweep(): void {
600
+ if (sweepTimer) {
601
+ clearInterval(sweepTimer);
602
+ sweepTimer = null;
603
+ }
604
+ sweepInProgress = false;
605
+ }
@@ -0,0 +1,23 @@
1
+ import { isAssistantFeatureFlagEnabled } from "../config/assistant-feature-flags.js";
2
+ import type { AssistantConfig } from "../config/schema.js";
3
+
4
+ const PLUGIN_SCHEDULES_FLAG_KEY = "plugin-schedules" as const;
5
+
6
+ /**
7
+ * Whether plugins may declare schedules that register and run automatically.
8
+ *
9
+ * The flag is a kill switch, not just a launch gate. `reconcilePluginSchedules`
10
+ * treats an off flag as an empty desired set and still runs its disarm branch,
11
+ * so the next pass after turning the flag off disarms every declared row and
12
+ * the next pass after turning it back on re-arms them. The fire-time guard in
13
+ * `./scheduler.ts` and the run-now guard in
14
+ * `../runtime/routes/schedule-routes.ts` close the window between the toggle
15
+ * and that pass.
16
+ *
17
+ * The CLI's install-consent listing (`../cli/lib/plugin-surfaces.ts`) is not
18
+ * gated: it only reports what a plugin's files declare, and the reconciler is
19
+ * the point where a declaration becomes something that runs.
20
+ */
21
+ export function isPluginSchedulesEnabled(config?: AssistantConfig): boolean {
22
+ return isAssistantFeatureFlagEnabled(PLUGIN_SCHEDULES_FLAG_KEY, config);
23
+ }