@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
@@ -1,3 +1,5 @@
1
+ import { mkdirSync, rmSync, writeFileSync } from "node:fs";
2
+ import { join } from "node:path";
1
3
  import { beforeEach, describe, expect, test } from "bun:test";
2
4
  import { mock } from "bun:test";
3
5
 
@@ -14,6 +16,11 @@ mock.module("../background-wake/publisher.js", () => ({
14
16
 
15
17
  import type { AssistantEventEnvelope } from "../api/index.js";
16
18
  import { SYNC_TAGS } from "../daemon/message-types/sync.js";
19
+ import {
20
+ createConversation,
21
+ setConversationInferenceProfile,
22
+ setConversationInferenceProfileSession,
23
+ } from "../persistence/conversation-crud.js";
17
24
  import { getDb } from "../persistence/db-connection.js";
18
25
  import { initializeDb } from "../persistence/db-init.js";
19
26
  import { assistantEventHub } from "../runtime/assistant-event-hub.js";
@@ -22,6 +29,8 @@ import {
22
29
  LEGACY_DEFER_CREATED_BY,
23
30
  OWNER_DEFER_CREATED_BY,
24
31
  } from "../schedule/defer-provenance.js";
32
+ import { resolveDefaultScheduleInferenceProfile } from "../schedule/inference-profile.js";
33
+ import type { DeclaredScheduleDefinition } from "../schedule/schedule-store.js";
25
34
  import {
26
35
  cancelSchedule,
27
36
  claimDueSchedules,
@@ -32,12 +41,17 @@ import {
32
41
  createScheduleRun,
33
42
  deleteSchedule,
34
43
  describeCronExpression,
44
+ disarmDeclaredSchedule,
35
45
  failOneShot,
36
46
  getSchedule,
47
+ listDeclaredSchedules,
37
48
  listSchedules,
49
+ setUserEnabled,
38
50
  updateSchedule,
51
+ upsertDeclaredSchedule,
39
52
  } from "../schedule/schedule-store.js";
40
53
  import { UserError } from "../util/errors.js";
54
+ import { getWorkspacePluginsDir } from "../util/platform.js";
41
55
 
42
56
  await initializeDb();
43
57
 
@@ -1389,6 +1403,37 @@ describe("listSchedules new filters", () => {
1389
1403
  expect(conv123Only[0].name).toBe("Wake for conv-123");
1390
1404
  expect(conv123Only[0].wakeConversationId).toBe("conv-123");
1391
1405
  });
1406
+
1407
+ test("inferenceProfile filter returns only schedules pinned to that profile", async () => {
1408
+ const defaultProfile = resolveDefaultScheduleInferenceProfile();
1409
+ expect(defaultProfile).not.toBe("cost-optimized");
1410
+
1411
+ await createSchedule({
1412
+ name: "Cheap digest",
1413
+ message: "digest",
1414
+ cronExpression: "0 * * * *",
1415
+ inferenceProfile: "cost-optimized",
1416
+ });
1417
+ await createSchedule({
1418
+ name: "Second cheap digest",
1419
+ message: "digest",
1420
+ cronExpression: "30 * * * *",
1421
+ inferenceProfile: "cost-optimized",
1422
+ });
1423
+ await createSchedule({
1424
+ name: "Default profile schedule",
1425
+ message: "hi",
1426
+ cronExpression: "0 9 * * *",
1427
+ });
1428
+
1429
+ const cheapOnly = listSchedules({ inferenceProfile: "cost-optimized" });
1430
+ expect(cheapOnly.map((j) => j.name).sort()).toEqual([
1431
+ "Cheap digest",
1432
+ "Second cheap digest",
1433
+ ]);
1434
+ expect(listSchedules({ inferenceProfile: "no-such-profile" })).toEqual([]);
1435
+ expect(listSchedules().length).toBe(3);
1436
+ });
1392
1437
  });
1393
1438
 
1394
1439
  // ── describeCronExpression ──────────────────────────────────────────
@@ -1506,3 +1551,498 @@ describe("owner-defer provenance", () => {
1506
1551
  expect(hasOwnerDeferProvenance(updated!.createdBy)).toBe(false);
1507
1552
  });
1508
1553
  });
1554
+
1555
+ // ── Plugin-declared schedules ───────────────────────────────────────
1556
+
1557
+ describe("declared schedules", () => {
1558
+ const SOURCE_KEY = "plugin:example/daily";
1559
+ const examplePluginDir = () => join(getWorkspacePluginsDir(), "example");
1560
+
1561
+ // setUserEnabled's enable path probes the declaration's on-disk presence
1562
+ // and its plugin's manifest, so the suite keeps a declaration directory and
1563
+ // a valid package.json in place for SOURCE_KEY; ghost-row tests remove them.
1564
+ beforeEach(() => {
1565
+ getDb().run("DELETE FROM cron_runs");
1566
+ getDb().run("DELETE FROM cron_jobs");
1567
+ rmSync(examplePluginDir(), { recursive: true, force: true });
1568
+ const declarationDir = join(examplePluginDir(), "schedules", "daily");
1569
+ mkdirSync(declarationDir, { recursive: true });
1570
+ writeFileSync(
1571
+ join(declarationDir, "config.json"),
1572
+ JSON.stringify({ expression: "0 9 * * *" }),
1573
+ );
1574
+ writeFileSync(join(declarationDir, "index.md"), "produce the digest\n");
1575
+ writeFileSync(
1576
+ join(examplePluginDir(), "package.json"),
1577
+ JSON.stringify({ name: "example", version: "1.0.0" }),
1578
+ );
1579
+ });
1580
+
1581
+ function makeDefinition(
1582
+ overrides: Partial<DeclaredScheduleDefinition> = {},
1583
+ ): DeclaredScheduleDefinition {
1584
+ return {
1585
+ name: "Daily digest",
1586
+ description: "Summarize the day",
1587
+ syntax: "cron",
1588
+ expression: "0 9 * * *",
1589
+ message: "produce the digest",
1590
+ mode: "execute",
1591
+ enabled: true,
1592
+ definitionHash: "hash-1",
1593
+ ...overrides,
1594
+ };
1595
+ }
1596
+
1597
+ function rawJob(id: string): Record<string, unknown> {
1598
+ return getRawDb()
1599
+ .query("SELECT * FROM cron_jobs WHERE id = ?")
1600
+ .get(id) as Record<string, unknown>;
1601
+ }
1602
+
1603
+ test("upsert inserts a sourced row with declared enabled state", async () => {
1604
+ const job = await upsertDeclaredSchedule(SOURCE_KEY, makeDefinition());
1605
+
1606
+ expect(job.sourceKey).toBe(SOURCE_KEY);
1607
+ expect(job.definitionHash).toBe("hash-1");
1608
+ expect(job.userEnabled).toBeNull();
1609
+ expect(job.enabled).toBe(true);
1610
+ expect(job.expression).toBe("0 9 * * *");
1611
+ expect(job.nextRunAt).toBeGreaterThan(Date.now());
1612
+ });
1613
+
1614
+ test("listDeclaredSchedules returns only sourced rows", async () => {
1615
+ await upsertDeclaredSchedule(SOURCE_KEY, makeDefinition());
1616
+ const imperative = await createSchedule({
1617
+ name: "Mine",
1618
+ cronExpression: "0 8 * * *",
1619
+ message: "hello",
1620
+ syntax: "cron",
1621
+ });
1622
+
1623
+ const declared = listDeclaredSchedules();
1624
+ expect(declared).toHaveLength(1);
1625
+ expect(declared[0].sourceKey).toBe(SOURCE_KEY);
1626
+ expect(imperative.sourceKey).toBeNull();
1627
+ expect(imperative.definitionHash).toBeNull();
1628
+ expect(imperative.userEnabled).toBeNull();
1629
+ });
1630
+
1631
+ test("upsert with an unchanged hash is a no-op", async () => {
1632
+ const created = await upsertDeclaredSchedule(SOURCE_KEY, makeDefinition());
1633
+ const before = rawJob(created.id);
1634
+
1635
+ const again = await upsertDeclaredSchedule(SOURCE_KEY, makeDefinition());
1636
+
1637
+ expect(again.id).toBe(created.id);
1638
+ expect(rawJob(created.id)).toEqual(before);
1639
+ });
1640
+
1641
+ test("upsert with a changed hash updates definition columns without moving nextRunAt", async () => {
1642
+ const created = await upsertDeclaredSchedule(SOURCE_KEY, makeDefinition());
1643
+
1644
+ const updated = await upsertDeclaredSchedule(
1645
+ SOURCE_KEY,
1646
+ makeDefinition({
1647
+ message: "produce a longer digest",
1648
+ definitionHash: "hash-2",
1649
+ }),
1650
+ );
1651
+
1652
+ expect(updated.id).toBe(created.id);
1653
+ expect(updated.message).toBe("produce a longer digest");
1654
+ expect(updated.definitionHash).toBe("hash-2");
1655
+ expect(updated.nextRunAt).toBe(created.nextRunAt);
1656
+ });
1657
+
1658
+ test("upsert recomputes nextRunAt when the expression changes", async () => {
1659
+ const created = await upsertDeclaredSchedule(SOURCE_KEY, makeDefinition());
1660
+
1661
+ const updated = await upsertDeclaredSchedule(
1662
+ SOURCE_KEY,
1663
+ makeDefinition({ expression: "0 15 * * *", definitionHash: "hash-2" }),
1664
+ );
1665
+
1666
+ expect(updated.expression).toBe("0 15 * * *");
1667
+ expect(updated.nextRunAt).not.toBe(created.nextRunAt);
1668
+ expect(updated.nextRunAt).toBeGreaterThan(Date.now());
1669
+ });
1670
+
1671
+ test("upsert leaves engine-latched rows untouched", async () => {
1672
+ const created = await upsertDeclaredSchedule(SOURCE_KEY, makeDefinition());
1673
+ // Recurrence exhaustion latch: the claim path disables the row, zeroes
1674
+ // nextRunAt, and stamps lastRunAt in one write.
1675
+ getRawDb().run(
1676
+ "UPDATE cron_jobs SET enabled = 0, next_run_at = 0, last_run_at = ? WHERE id = ?",
1677
+ [Date.now(), created.id],
1678
+ );
1679
+ const latched = rawJob(created.id);
1680
+
1681
+ await upsertDeclaredSchedule(
1682
+ SOURCE_KEY,
1683
+ makeDefinition({ definitionHash: "hash-2" }),
1684
+ );
1685
+
1686
+ expect(rawJob(created.id)).toEqual(latched);
1687
+ });
1688
+
1689
+ test("a declaration shipped disabled inserts disarmed and is not treated as latched", async () => {
1690
+ const created = await upsertDeclaredSchedule(
1691
+ SOURCE_KEY,
1692
+ makeDefinition({ enabled: false }),
1693
+ );
1694
+ expect(created.enabled).toBe(false);
1695
+ expect(created.nextRunAt).toBe(0);
1696
+ expect(created.lastRunAt).toBeNull();
1697
+
1698
+ const armed = await upsertDeclaredSchedule(
1699
+ SOURCE_KEY,
1700
+ makeDefinition({ enabled: true, definitionHash: "hash-2" }),
1701
+ );
1702
+ expect(armed.enabled).toBe(true);
1703
+ expect(armed.nextRunAt).toBeGreaterThan(Date.now());
1704
+ });
1705
+
1706
+ test("disarm keeps the row and its runs; a later pass re-arms it", async () => {
1707
+ const created = await upsertDeclaredSchedule(SOURCE_KEY, makeDefinition());
1708
+ const runId = await createScheduleRun(created.id, "conv-123");
1709
+ await completeScheduleRun(runId, { status: "ok" });
1710
+
1711
+ await disarmDeclaredSchedule(created.id);
1712
+
1713
+ const disarmed = getSchedule(created.id);
1714
+ expect(disarmed!.enabled).toBe(false);
1715
+ const runs = getRawDb()
1716
+ .query("SELECT COUNT(*) AS n FROM cron_runs WHERE job_id = ?")
1717
+ .get(created.id) as { n: number };
1718
+ expect(runs.n).toBe(1);
1719
+
1720
+ // Same declaration back in the desired set (e.g. plugin re-enabled):
1721
+ // effective enabled is recomputed even though the hash matches.
1722
+ const rearmed = await upsertDeclaredSchedule(SOURCE_KEY, makeDefinition());
1723
+ expect(rearmed.enabled).toBe(true);
1724
+ expect(rearmed.nextRunAt).toBeGreaterThan(Date.now());
1725
+ });
1726
+
1727
+ test("disarm refuses imperative rows", async () => {
1728
+ const imperative = await createSchedule({
1729
+ name: "Mine",
1730
+ cronExpression: "0 8 * * *",
1731
+ message: "hello",
1732
+ syntax: "cron",
1733
+ });
1734
+
1735
+ await expect(disarmDeclaredSchedule(imperative.id)).rejects.toThrow(
1736
+ /plugin-sourced/,
1737
+ );
1738
+ expect(getSchedule(imperative.id)!.enabled).toBe(true);
1739
+ });
1740
+
1741
+ test("user override wins over the declared value in both directions", async () => {
1742
+ const created = await upsertDeclaredSchedule(SOURCE_KEY, makeDefinition());
1743
+
1744
+ const off = await setUserEnabled(created.id, false);
1745
+ expect(off!.userEnabled).toBe(false);
1746
+ expect(off!.enabled).toBe(false);
1747
+
1748
+ // A reconcile pass with the declaration still enabled must not undo the
1749
+ // user's override.
1750
+ const afterPass = await upsertDeclaredSchedule(
1751
+ SOURCE_KEY,
1752
+ makeDefinition(),
1753
+ );
1754
+ expect(afterPass.enabled).toBe(false);
1755
+ expect(afterPass.userEnabled).toBe(false);
1756
+
1757
+ const on = await setUserEnabled(created.id, true);
1758
+ expect(on!.userEnabled).toBe(true);
1759
+ expect(on!.enabled).toBe(true);
1760
+ expect(on!.nextRunAt).toBeGreaterThan(Date.now());
1761
+ });
1762
+
1763
+ test("setUserEnabled on an imperative row behaves like the existing toggle", async () => {
1764
+ const imperative = await createSchedule({
1765
+ name: "Mine",
1766
+ cronExpression: "0 8 * * *",
1767
+ message: "hello",
1768
+ syntax: "cron",
1769
+ });
1770
+
1771
+ const off = await setUserEnabled(imperative.id, false);
1772
+ expect(off!.enabled).toBe(false);
1773
+ expect(off!.userEnabled).toBeNull();
1774
+ expect(off!.nextRunAt).toBe(0);
1775
+
1776
+ const on = await setUserEnabled(imperative.id, true);
1777
+ expect(on!.enabled).toBe(true);
1778
+ expect(on!.userEnabled).toBeNull();
1779
+ expect(on!.nextRunAt).toBeGreaterThan(Date.now());
1780
+ });
1781
+
1782
+ test("upsert rewrites a moved script path without touching timing", async () => {
1783
+ const created = await upsertDeclaredSchedule(
1784
+ SOURCE_KEY,
1785
+ makeDefinition({
1786
+ mode: "script",
1787
+ message: "",
1788
+ script: "sh '/workspace-a/plugins/example/schedules/daily/index.sh'",
1789
+ }),
1790
+ );
1791
+
1792
+ // Same definition hash: relocation moves the absolute entrypoint path
1793
+ // while the schedules/-relative content is unchanged.
1794
+ const updated = await upsertDeclaredSchedule(
1795
+ SOURCE_KEY,
1796
+ makeDefinition({
1797
+ mode: "script",
1798
+ message: "",
1799
+ script: "sh '/workspace-b/plugins/example/schedules/daily/index.sh'",
1800
+ }),
1801
+ );
1802
+
1803
+ expect(updated.script).toBe(
1804
+ "sh '/workspace-b/plugins/example/schedules/daily/index.sh'",
1805
+ );
1806
+ expect(updated.definitionHash).toBe("hash-1");
1807
+ expect(updated.nextRunAt).toBe(created.nextRunAt);
1808
+ });
1809
+
1810
+ test("cancelSchedule refuses sourced rows", async () => {
1811
+ const created = await upsertDeclaredSchedule(SOURCE_KEY, makeDefinition());
1812
+ const before = rawJob(created.id);
1813
+
1814
+ await expect(cancelSchedule(created.id)).rejects.toThrow(UserError);
1815
+ await expect(cancelSchedule(created.id)).rejects.toThrow(
1816
+ /managed by a plugin/,
1817
+ );
1818
+ expect(rawJob(created.id)).toEqual(before);
1819
+ });
1820
+
1821
+ test("enabling a row whose declaration is gone records the override without re-arming", async () => {
1822
+ const created = await upsertDeclaredSchedule(SOURCE_KEY, makeDefinition());
1823
+ await setUserEnabled(created.id, false);
1824
+ rmSync(examplePluginDir(), { recursive: true, force: true });
1825
+
1826
+ const result = await setUserEnabled(created.id, true);
1827
+
1828
+ expect(result!.userEnabled).toBe(true);
1829
+ expect(result!.enabled).toBe(false);
1830
+ });
1831
+
1832
+ test("a flat markdown file does not satisfy the enable probe", async () => {
1833
+ const created = await upsertDeclaredSchedule(SOURCE_KEY, makeDefinition());
1834
+ await setUserEnabled(created.id, false);
1835
+ const schedulesDir = join(examplePluginDir(), "schedules");
1836
+ rmSync(join(schedulesDir, "daily"), { recursive: true, force: true });
1837
+ writeFileSync(join(schedulesDir, "daily.md"), "declaration");
1838
+
1839
+ const result = await setUserEnabled(created.id, true);
1840
+
1841
+ expect(result!.userEnabled).toBe(true);
1842
+ expect(result!.enabled).toBe(false);
1843
+ });
1844
+
1845
+ test("enabling a schedule of a disabled plugin records the override without re-arming", async () => {
1846
+ const created = await upsertDeclaredSchedule(SOURCE_KEY, makeDefinition());
1847
+ await setUserEnabled(created.id, false);
1848
+ // The declaration file is still on disk; the `.disabled` sentinel alone
1849
+ // must keep the enable probe from re-arming the row before the next
1850
+ // reconcile pass.
1851
+ writeFileSync(join(examplePluginDir(), ".disabled"), "");
1852
+
1853
+ const result = await setUserEnabled(created.id, true);
1854
+
1855
+ expect(result!.userEnabled).toBe(true);
1856
+ expect(result!.enabled).toBe(false);
1857
+ });
1858
+
1859
+ test("enabling a schedule whose plugin manifest is broken records the override without re-arming", async () => {
1860
+ const created = await upsertDeclaredSchedule(SOURCE_KEY, makeDefinition());
1861
+ await setUserEnabled(created.id, false);
1862
+ // The reconciler disarms the schedules of a plugin the loader refuses to
1863
+ // bring up, so a toggle must not re-arm one before the next sweep.
1864
+ writeFileSync(join(examplePluginDir(), "package.json"), "{not json");
1865
+
1866
+ const result = await setUserEnabled(created.id, true);
1867
+
1868
+ expect(result!.userEnabled).toBe(true);
1869
+ expect(result!.enabled).toBe(false);
1870
+ });
1871
+
1872
+ test("updateSchedule refuses sourced rows", async () => {
1873
+ const created = await upsertDeclaredSchedule(SOURCE_KEY, makeDefinition());
1874
+ const before = rawJob(created.id);
1875
+
1876
+ await expect(
1877
+ updateSchedule(created.id, { name: "renamed" }),
1878
+ ).rejects.toThrow(UserError);
1879
+ await expect(
1880
+ updateSchedule(created.id, { name: "renamed" }),
1881
+ ).rejects.toThrow(/managed by a plugin/);
1882
+ expect(rawJob(created.id)).toEqual(before);
1883
+ });
1884
+
1885
+ test("deleteSchedule refuses sourced rows and keeps runs", async () => {
1886
+ const created = await upsertDeclaredSchedule(SOURCE_KEY, makeDefinition());
1887
+ const runId = await createScheduleRun(created.id, "conv-123");
1888
+ await completeScheduleRun(runId, { status: "ok" });
1889
+
1890
+ await expect(deleteSchedule(created.id)).rejects.toThrow(UserError);
1891
+ await expect(deleteSchedule(created.id)).rejects.toThrow(
1892
+ /managed by a plugin/,
1893
+ );
1894
+
1895
+ expect(getSchedule(created.id)).not.toBeNull();
1896
+ const runs = getRawDb()
1897
+ .query("SELECT COUNT(*) AS n FROM cron_runs WHERE job_id = ?")
1898
+ .get(created.id) as { n: number };
1899
+ expect(runs.n).toBe(1);
1900
+ });
1901
+
1902
+ test("update and delete on imperative rows are unchanged", async () => {
1903
+ const imperative = await createSchedule({
1904
+ name: "Mine",
1905
+ cronExpression: "0 8 * * *",
1906
+ message: "hello",
1907
+ syntax: "cron",
1908
+ });
1909
+
1910
+ const renamed = await updateSchedule(imperative.id, { name: "Renamed" });
1911
+ expect(renamed!.name).toBe("Renamed");
1912
+
1913
+ expect(await deleteSchedule(imperative.id)).toBe(true);
1914
+ expect(getSchedule(imperative.id)).toBeNull();
1915
+ });
1916
+ });
1917
+
1918
+ // ── Inference-profile pinning ─────────────────────────────────────────────
1919
+
1920
+ /**
1921
+ * Every assertion below compares against the live resolution rather than a
1922
+ * literal key, so the tests describe the invariant (a schedule is pinned to
1923
+ * whatever the default resolves to at write time) instead of freezing this
1924
+ * workspace's shipped profile catalog.
1925
+ */
1926
+ describe("inference-profile pinning", () => {
1927
+ beforeEach(() => {
1928
+ getDb().run("DELETE FROM cron_runs");
1929
+ getDb().run("DELETE FROM cron_jobs");
1930
+ getDb().run("DELETE FROM conversations");
1931
+ });
1932
+
1933
+ test("creation with no profile pins the resolved default", async () => {
1934
+ const expected = resolveDefaultScheduleInferenceProfile();
1935
+ expect(expected).not.toBeNull();
1936
+
1937
+ const job = await createSchedule({
1938
+ name: "Unpinned",
1939
+ message: "hi",
1940
+ cronExpression: "0 9 * * *",
1941
+ });
1942
+
1943
+ expect(job.inferenceProfile).toBe(expected!);
1944
+ expect(getSchedule(job.id)!.inferenceProfile).toBe(expected!);
1945
+ });
1946
+
1947
+ test("creation with an explicit profile keeps it", async () => {
1948
+ expect(resolveDefaultScheduleInferenceProfile()).not.toBe("cost-optimized");
1949
+
1950
+ const job = await createSchedule({
1951
+ name: "Pinned",
1952
+ message: "hi",
1953
+ cronExpression: "0 9 * * *",
1954
+ inferenceProfile: "cost-optimized",
1955
+ });
1956
+
1957
+ expect(job.inferenceProfile).toBe("cost-optimized");
1958
+ });
1959
+
1960
+ test("updating to null re-snapshots the default instead of unpinning", async () => {
1961
+ const job = await createSchedule({
1962
+ name: "Repinned",
1963
+ message: "hi",
1964
+ cronExpression: "0 9 * * *",
1965
+ inferenceProfile: "cost-optimized",
1966
+ });
1967
+
1968
+ const updated = await updateSchedule(job.id, { inferenceProfile: null });
1969
+
1970
+ expect(updated!.inferenceProfile).toBe(
1971
+ resolveDefaultScheduleInferenceProfile()!,
1972
+ );
1973
+ });
1974
+
1975
+ test("a defer seeds the source conversation's pinned profile", async () => {
1976
+ const conversation = createConversation({ id: "conv-pinned" });
1977
+ setConversationInferenceProfile(conversation.id, "cost-optimized");
1978
+ expect(resolveDefaultScheduleInferenceProfile()).not.toBe("cost-optimized");
1979
+
1980
+ const job = await createOwnerDeferredWake({
1981
+ conversationId: conversation.id,
1982
+ hint: "check back",
1983
+ fireAt: Date.now() + 60_000,
1984
+ });
1985
+
1986
+ // The wake resumes this conversation and forces the row's pin as the
1987
+ // turn's override, so seeding from the global default would move a pinned
1988
+ // conversation's own follow-up onto a different model.
1989
+ expect(job.inferenceProfile).toBe("cost-optimized");
1990
+ });
1991
+
1992
+ test("a defer does not freeze the source conversation's profile session", async () => {
1993
+ const conversation = createConversation({ id: "conv-session" });
1994
+ setConversationInferenceProfileSession(
1995
+ conversation.id,
1996
+ "cost-optimized",
1997
+ "session-1",
1998
+ Date.now() + 10 * 60_000,
1999
+ );
2000
+
2001
+ const job = await createOwnerDeferredWake({
2002
+ conversationId: conversation.id,
2003
+ hint: "check back",
2004
+ fireAt: Date.now() + 7 * 24 * 60 * 60_000,
2005
+ });
2006
+
2007
+ // A profile session is a deliberately temporary choice. Snapshotting it
2008
+ // into the row would keep billing that model every time the wake fires,
2009
+ // long after the ten-minute session lapsed.
2010
+ expect(job.inferenceProfile).toBe(
2011
+ resolveDefaultScheduleInferenceProfile()!,
2012
+ );
2013
+ });
2014
+
2015
+ test("a defer from an unpinned conversation seeds the default", async () => {
2016
+ const conversation = createConversation({ id: "conv-unpinned" });
2017
+
2018
+ const job = await createOwnerDeferredWake({
2019
+ conversationId: conversation.id,
2020
+ hint: "check back",
2021
+ fireAt: Date.now() + 60_000,
2022
+ });
2023
+
2024
+ expect(job.inferenceProfile).toBe(
2025
+ resolveDefaultScheduleInferenceProfile()!,
2026
+ );
2027
+ });
2028
+
2029
+ test("any wake row seeds its target's pin, not just a defer", async () => {
2030
+ const conversation = createConversation({ id: "conv-wake-target" });
2031
+ setConversationInferenceProfile(conversation.id, "cost-optimized");
2032
+ expect(resolveDefaultScheduleInferenceProfile()).not.toBe("cost-optimized");
2033
+
2034
+ // Seeding is keyed on `mode: "wake"` at the insert chokepoint rather than
2035
+ // on defer provenance: every wake row forces its pin as the woken turn's
2036
+ // override, so a wake minted any other way must not flatten its target's
2037
+ // choice onto the global default either.
2038
+ const job = await createSchedule({
2039
+ name: "Wake pinned conversation",
2040
+ message: "resume",
2041
+ nextRunAt: Date.now() + 60_000,
2042
+ mode: "wake",
2043
+ wakeConversationId: conversation.id,
2044
+ });
2045
+
2046
+ expect(job.inferenceProfile).toBe("cost-optimized");
2047
+ });
2048
+ });
@@ -9,6 +9,7 @@ import { createGroup, deleteGroup } from "../persistence/group-crud.js";
9
9
  import {
10
10
  getSchedule,
11
11
  resolveScheduleConversationGroupId,
12
+ upsertDeclaredSchedule,
12
13
  } from "../schedule/schedule-store.js";
13
14
  import {
14
15
  __clearRegistryForTesting,
@@ -1888,6 +1889,108 @@ describe("schedule_delete tool", () => {
1888
1889
  });
1889
1890
  });
1890
1891
 
1892
+ // ── plugin-sourced schedules ────────────────────────────────────────
1893
+
1894
+ describe("schedule tools on plugin-sourced schedules", () => {
1895
+ beforeEach(() => {
1896
+ getRawDb().run("DELETE FROM cron_runs");
1897
+ getRawDb().run("DELETE FROM cron_jobs");
1898
+ });
1899
+
1900
+ function createSourcedSchedule() {
1901
+ return upsertDeclaredSchedule("plugin:example/daily", {
1902
+ name: "daily",
1903
+ syntax: "cron",
1904
+ expression: "0 9 * * *",
1905
+ message: "Do the daily thing.",
1906
+ mode: "execute",
1907
+ enabled: true,
1908
+ definitionHash: "hash-1",
1909
+ });
1910
+ }
1911
+
1912
+ test("schedule_update toggles enabled via the user_enabled override", async () => {
1913
+ const job = await createSourcedSchedule();
1914
+
1915
+ const result = await executeScheduleUpdate(
1916
+ { job_id: job.id, enabled: false },
1917
+ ctx,
1918
+ );
1919
+
1920
+ expect(result.isError).toBe(false);
1921
+ expect(result.content).toContain("Schedule updated successfully");
1922
+ expect(result.content).toContain("Enabled: false");
1923
+
1924
+ const row = getRawDb()
1925
+ .query("SELECT enabled, user_enabled FROM cron_jobs WHERE id = ?")
1926
+ .get(job.id) as { enabled: number; user_enabled: number };
1927
+ expect(row.enabled).toBe(0);
1928
+ expect(row.user_enabled).toBe(0);
1929
+ });
1930
+
1931
+ test("schedule_update refuses definition fields with a message naming the enabled toggle", async () => {
1932
+ const job = await createSourcedSchedule();
1933
+
1934
+ const result = await executeScheduleUpdate(
1935
+ { job_id: job.id, name: "renamed" },
1936
+ ctx,
1937
+ );
1938
+
1939
+ expect(result.isError).toBe(true);
1940
+ expect(result.content).toContain("managed by a plugin");
1941
+ expect(result.content).toContain("only enabled can be changed");
1942
+ expect(getSchedule(job.id)?.name).toBe("daily");
1943
+ });
1944
+
1945
+ test("schedule_update refuses enabled mixed with definition fields", async () => {
1946
+ const job = await createSourcedSchedule();
1947
+
1948
+ const result = await executeScheduleUpdate(
1949
+ { job_id: job.id, enabled: false, message: "rewritten" },
1950
+ ctx,
1951
+ );
1952
+
1953
+ expect(result.isError).toBe(true);
1954
+ expect(result.content).toContain("managed by a plugin");
1955
+ expect(getSchedule(job.id)?.enabled).toBe(true);
1956
+ });
1957
+
1958
+ test("schedule_delete surfaces the store refusal as a normal tool error", async () => {
1959
+ const job = await createSourcedSchedule();
1960
+
1961
+ const result = await executeScheduleDelete({ job_id: job.id }, ctx);
1962
+
1963
+ expect(result.isError).toBe(true);
1964
+ expect(result.content).toContain("managed by a plugin");
1965
+ expect(getSchedule(job.id)).not.toBeNull();
1966
+ });
1967
+
1968
+ test("schedule_list marks plugin-managed rows in list and detail modes", async () => {
1969
+ const job = await createSourcedSchedule();
1970
+
1971
+ const list = await executeScheduleList({}, ctx);
1972
+ expect(list.isError).toBe(false);
1973
+ expect(list.content).toContain("(managed by plugin example)");
1974
+
1975
+ const detail = await executeScheduleList({ job_id: job.id }, ctx);
1976
+ expect(detail.isError).toBe(false);
1977
+ expect(detail.content).toContain("Managed by plugin: example");
1978
+ expect(detail.content).toContain("only enabled can be changed");
1979
+ });
1980
+
1981
+ test("schedule_list carries no plugin marker on imperative rows", async () => {
1982
+ const created = await executeScheduleCreate(
1983
+ { name: "imperative", expression: "0 9 * * *", message: "Do the thing." },
1984
+ ctx,
1985
+ );
1986
+ expect(created.isError).toBe(false);
1987
+
1988
+ const list = await executeScheduleList({}, ctx);
1989
+ expect(list.isError).toBe(false);
1990
+ expect(list.content).not.toContain("managed by plugin");
1991
+ });
1992
+ });
1993
+
1891
1994
  // ── model-input schema validation (LUM-2853) ────────────────────────
1892
1995
 
1893
1996
  describe("schedule tools — model-input schema validation", () => {