@vellumai/assistant 0.11.2 → 0.11.3-staging.1

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 (709) 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-queue.test.ts +265 -1
  72. package/src/__tests__/conversation-retry-route.test.ts +3 -1
  73. package/src/__tests__/conversation-store-ephemeral.test.ts +4 -0
  74. package/src/__tests__/conversation-store.test.ts +26 -10
  75. package/src/__tests__/conversation-summarize-route.test.ts +1 -1
  76. package/src/__tests__/conversation-surfaces-action-delivery.test.ts +15 -6
  77. package/src/__tests__/conversation-surfaces-activation-emit.test.ts +6 -5
  78. package/src/__tests__/conversation-surfaces-app-control.test.ts +12 -11
  79. package/src/__tests__/conversation-surfaces-app-open.test.ts +11 -5
  80. package/src/__tests__/conversation-surfaces-data-persist.test.ts +5 -4
  81. package/src/__tests__/conversation-surfaces-history-restored-completion.test.ts +967 -0
  82. package/src/__tests__/conversation-surfaces-persisted-info.test.ts +312 -0
  83. package/src/__tests__/conversation-surfaces-queued-emit.test.ts +6 -5
  84. package/src/__tests__/conversation-surfaces-standalone-payloads.test.ts +12 -8
  85. package/src/__tests__/conversation-surfaces-standalone.test.ts +21 -11
  86. package/src/__tests__/conversation-surfaces-state-update.test.ts +7 -11
  87. package/src/__tests__/conversation-surfaces-table-action.test.ts +7 -6
  88. package/src/__tests__/conversation-surfaces-task-progress.test.ts +11 -6
  89. package/src/__tests__/conversation-tool-setup-app-refresh.test.ts +9 -6
  90. package/src/__tests__/conversation-tool-setup-attribution.test.ts +70 -7
  91. package/src/__tests__/conversation-wait-for-idle.test.ts +6 -11
  92. package/src/__tests__/credential-prompt-route.test.ts +57 -7
  93. package/src/__tests__/credential-security-invariants.test.ts +1 -0
  94. package/src/__tests__/cu-unified-flow.test.ts +89 -28
  95. package/src/__tests__/custom-group-listing-visibility.test.ts +1 -1
  96. package/src/__tests__/db-schedule-syntax-migration.test.ts +4 -1
  97. package/src/__tests__/discord-access-request-privacy.test.ts +366 -0
  98. package/src/__tests__/discord-requester-notice-privacy.test.ts +87 -0
  99. package/src/__tests__/disk-pressure-guard.test.ts +55 -55
  100. package/src/__tests__/disk-pressure-routes.test.ts +10 -10
  101. package/src/__tests__/disk-pressure-tools.test.ts +2 -2
  102. package/src/__tests__/disk-usage.test.ts +35 -20
  103. package/src/__tests__/document-sync-tags.test.ts +379 -0
  104. package/src/__tests__/document-update-default-surface.test.ts +28 -0
  105. package/src/__tests__/dynamic-skill-background-guard.test.ts +2 -2
  106. package/src/__tests__/emit-signal-routing-intent.test.ts +3 -4
  107. package/src/__tests__/ensure-conversation-exists.test.ts +8 -5
  108. package/src/__tests__/events-dev-bypass-actor.test.ts +183 -1
  109. package/src/__tests__/evict-conversations-for-reload.test.ts +90 -0
  110. package/src/__tests__/file-list-tool.test.ts +12 -10
  111. package/src/__tests__/file-ops-service.test.ts +151 -44
  112. package/src/__tests__/file-read-container-boundary.test.ts +137 -0
  113. package/src/__tests__/file-read-tool.test.ts +21 -9
  114. package/src/__tests__/file-url-path-guard.test.ts +83 -0
  115. package/src/__tests__/filesystem-tools.test.ts +82 -66
  116. package/src/__tests__/guardian-outbound-http.test.ts +8 -8
  117. package/src/__tests__/helpers/gateway-verification-sessions-stub.ts +14 -1
  118. package/src/__tests__/helpers/hub-clients.ts +42 -0
  119. package/src/__tests__/helpers/mock-conversation.ts +46 -0
  120. package/src/__tests__/helpers/verification-sessions-ipc-sim.ts +61 -57
  121. package/src/__tests__/history-repair.test.ts +112 -12
  122. package/src/__tests__/host-bash-proxy.test.ts +10 -5
  123. package/src/__tests__/host-bash-routes.test.ts +67 -0
  124. package/src/__tests__/host-browser-proxy.test.ts +2 -2
  125. package/src/__tests__/host-cu-proxy.test.ts +3 -7
  126. package/src/__tests__/host-file-proxy.test.ts +3 -7
  127. package/src/__tests__/host-file-read-tool.test.ts +5 -1
  128. package/src/__tests__/host-transfer-proxy-targeted.test.ts +6 -4
  129. package/src/__tests__/http-conversation-lineage.test.ts +2 -5
  130. package/src/__tests__/http-user-message-parity.test.ts +44 -0
  131. package/src/__tests__/identity-routes.test.ts +13 -13
  132. package/src/__tests__/image-conversion.test.ts +29 -26
  133. package/src/__tests__/image-source-path-reinject.test.ts +2 -2
  134. package/src/__tests__/inference-profile-session-handler.test.ts +95 -0
  135. package/src/__tests__/inference-profile-session-ipc.test.ts +25 -0
  136. package/src/__tests__/introduction-card-resolver.test.ts +6 -1
  137. package/src/__tests__/list-messages-attachments.test.ts +56 -32
  138. package/src/__tests__/list-messages-background-tool-completion.test.ts +4 -4
  139. package/src/__tests__/list-messages-client-message-id.test.ts +2 -2
  140. package/src/__tests__/list-messages-hidden-metadata.test.ts +16 -16
  141. package/src/__tests__/list-messages-page-latest.test.ts +61 -61
  142. package/src/__tests__/list-messages-provider-error.test.ts +6 -6
  143. package/src/__tests__/list-messages-queued.test.ts +49 -6
  144. package/src/__tests__/list-messages-system-card.test.ts +4 -4
  145. package/src/__tests__/list-messages-tool-merge.test.ts +9 -9
  146. package/src/__tests__/llm-resolver-override-or-default.test.ts +141 -0
  147. package/src/__tests__/llm-usage-store.test.ts +55 -0
  148. package/src/__tests__/manual-token-reconciliation.test.ts +45 -0
  149. package/src/__tests__/mcp-tool-annotations-risk.test.ts +133 -0
  150. package/src/__tests__/media-stream-server-integration.test.ts +36 -0
  151. package/src/__tests__/migration-export-http.test.ts +1 -1
  152. package/src/__tests__/migration-import-commit-http.test.ts +1 -1
  153. package/src/__tests__/migration-import-preflight-http.test.ts +1 -1
  154. package/src/__tests__/migration-validate-http.test.ts +1 -1
  155. package/src/__tests__/mtime-cache.test.ts +65 -3
  156. package/src/__tests__/notification-platform-adapter.test.ts +104 -0
  157. package/src/__tests__/notification-schedule-notify-dedup.test.ts +1 -1
  158. package/src/__tests__/oauth-provider-profiles.test.ts +4 -2
  159. package/src/__tests__/oauth-provider-serializer.test.ts +17 -0
  160. package/src/__tests__/oauth-providers-routes.test.ts +1 -0
  161. package/src/__tests__/openai-responses-prompt-cache.test.ts +5 -2
  162. package/src/__tests__/path-policy.test.ts +164 -1
  163. package/src/__tests__/persist-media-references.test.ts +2 -2
  164. package/src/__tests__/persist-unsendable-image-downscale.test.ts +5 -5
  165. package/src/__tests__/persist-unsendable-image.test.ts +23 -23
  166. package/src/__tests__/persona-resolver.test.ts +154 -0
  167. package/src/__tests__/plugin-api-webhook-url.test.ts +170 -0
  168. package/src/__tests__/plugin-import-boundary-guard.test.ts +6 -0
  169. package/src/__tests__/processing-flag-persist-failure.test.ts +15 -10
  170. package/src/__tests__/profile-availability-incomplete.test.ts +113 -0
  171. package/src/__tests__/profiler-routes.test.ts +13 -13
  172. package/src/__tests__/prompt-cache-cross-turn-stability.test.ts +593 -0
  173. package/src/__tests__/proxy-approval-callback.test.ts +3 -3
  174. package/src/__tests__/queued-message-cancel-and-steer.test.ts +189 -0
  175. package/src/__tests__/queued-message-steer-actor-scoping.test.ts +164 -0
  176. package/src/__tests__/resolve-trust-class.test.ts +20 -0
  177. package/src/__tests__/run-conversation-turn-persistence.test.ts +1 -1
  178. package/src/__tests__/run-due-schedules.test.ts +174 -0
  179. package/src/__tests__/runtime-attachment-metadata.test.ts +15 -7
  180. package/src/__tests__/scaffold-managed-skill-tool.test.ts +166 -0
  181. package/src/__tests__/schedule-routes.test.ts +730 -8
  182. package/src/__tests__/schedule-store.test.ts +540 -0
  183. package/src/__tests__/schedule-tools.test.ts +103 -0
  184. package/src/__tests__/scheduler-wake.test.ts +5 -0
  185. package/src/__tests__/secret-routes-platform-proxy.test.ts +29 -1
  186. package/src/__tests__/secure-keys.test.ts +91 -0
  187. package/src/__tests__/server-history-render.test.ts +97 -7
  188. package/src/__tests__/size-guard.test.ts +8 -8
  189. package/src/__tests__/skill-docs-sync-guard.test.ts +3 -2
  190. package/src/__tests__/skill-tool-factory.test.ts +247 -0
  191. package/src/__tests__/skills-install-staging.test.ts +15 -6
  192. package/src/__tests__/slack-edit-ordering-characterization.test.ts +103 -0
  193. package/src/__tests__/slack-inbound-verification.test.ts +4 -1
  194. package/src/__tests__/slack-mention-binding-invariance.test.ts +103 -0
  195. package/src/__tests__/slack-mention-provider-leak-audit.test.ts +135 -0
  196. package/src/__tests__/starter-task-flow.test.ts +5 -4
  197. package/src/__tests__/steer-on-enqueue-question.test.ts +3 -2
  198. package/src/__tests__/stt-language-catalog-parity.test.ts +38 -0
  199. package/src/__tests__/subagent-allowlist-validation.test.ts +26 -4
  200. package/src/__tests__/subagent-cron-run-attribution.test.ts +301 -0
  201. package/src/__tests__/subagent-disposal.test.ts +10 -0
  202. package/src/__tests__/subagent-fork-notifications.test.ts +10 -0
  203. package/src/__tests__/subagent-fork-prompt-role.test.ts +85 -11
  204. package/src/__tests__/subagent-fork-spawn.test.ts +15 -7
  205. package/src/__tests__/subagent-manager-notify.test.ts +148 -0
  206. package/src/__tests__/subagent-notify-parent.test.ts +1 -1
  207. package/src/__tests__/subagent-persistence.test.ts +141 -0
  208. package/src/__tests__/subagent-role-registry.test.ts +172 -63
  209. package/src/__tests__/subagent-role-resolution.test.ts +88 -0
  210. package/src/__tests__/subagent-spawn-and-await.test.ts +48 -0
  211. package/src/__tests__/subagent-spawn-tool-fork.test.ts +111 -10
  212. package/src/__tests__/subagent-terminal-message.test.ts +88 -1
  213. package/src/__tests__/subagent-tool-gate-mode.test.ts +225 -10
  214. package/src/__tests__/subagent-tools.test.ts +1901 -108
  215. package/src/__tests__/surface-completion-compaction-boundary.test.ts +124 -0
  216. package/src/__tests__/surface-completion-in-flight-snapshot.test.ts +298 -0
  217. package/src/__tests__/system-prompt.test.ts +82 -7
  218. package/src/__tests__/tool-result-spool.test.ts +74 -18
  219. package/src/__tests__/tool-side-effects-documents.test.ts +174 -0
  220. package/src/__tests__/tool-side-effects-hook-failures.test.ts +129 -0
  221. package/src/__tests__/tools-audio-read.test.ts +10 -10
  222. package/src/__tests__/tools-get-route.test.ts +85 -12
  223. package/src/__tests__/tools-list-cli.test.ts +171 -0
  224. package/src/__tests__/top-level-renderer.test.ts +54 -1
  225. package/src/__tests__/trusted-contact-multichannel.test.ts +4 -1
  226. package/src/__tests__/turn-tail-chain.test.ts +135 -0
  227. package/src/__tests__/ui-choice-copy-surfaces.test.ts +6 -4
  228. package/src/__tests__/ui-visual-surface.test.ts +5 -4
  229. package/src/__tests__/ui-work-result-surface.test.ts +5 -4
  230. package/src/__tests__/unified-turn-context-visible-app.test.ts +7 -8
  231. package/src/__tests__/usage-routes.test.ts +62 -0
  232. package/src/__tests__/verification-outbound-delivery.test.ts +176 -0
  233. package/src/__tests__/visible-app-context.test.ts +20 -4
  234. package/src/__tests__/web-search-history.test.ts +96 -0
  235. package/src/__tests__/workspace-migration-139-clear-renamed-cost-profile-label.test.ts +6 -6
  236. package/src/__tests__/workspace-migration-140-repair-seed-pinned-memory-v3-live.test.ts +330 -0
  237. package/src/acp/failure-error.ts +3 -5
  238. package/src/agent/attachments.ts +26 -24
  239. package/src/agent/history-repair/history-repair.ts +27 -26
  240. package/src/agent/image-optimize.ts +6 -6
  241. package/src/agent/loop.ts +40 -22
  242. package/src/agent/message-types.ts +3 -3
  243. package/src/api/constants/document-tools.ts +55 -0
  244. package/src/api/events/question-answered.ts +53 -0
  245. package/src/api/events/tool-result.ts +7 -0
  246. package/src/api/index.ts +13 -0
  247. package/src/api/responses/conversation-message.ts +11 -0
  248. package/src/api/surfaces-oauth-connect.test.ts +96 -0
  249. package/src/api/surfaces.ts +36 -8
  250. package/src/approvals/guardian-channel-delivery.ts +83 -0
  251. package/src/approvals/guardian-expiry-notifier.ts +13 -11
  252. package/src/approvals/guardian-request-resolvers.ts +216 -132
  253. package/src/calls/__tests__/voice-session-bridge.test.ts +50 -0
  254. package/src/calls/media-stream-server.ts +9 -1
  255. package/src/calls/voice-session-bridge.ts +38 -0
  256. package/src/channels/__tests__/gateway-verification-sessions.test.ts +3 -3
  257. package/src/channels/config.ts +7 -2
  258. package/src/channels/gateway-verification-sessions.ts +7 -2
  259. package/src/channels/types.ts +7 -1
  260. package/src/cli/commands/__tests__/cache.test.ts +7 -4
  261. package/src/cli/commands/__tests__/cli-test-harness.ts +82 -0
  262. package/src/cli/commands/__tests__/conversations-wake.test.ts +51 -6
  263. package/src/cli/commands/__tests__/inference-models.test.ts +14 -0
  264. package/src/cli/commands/__tests__/inference-profiles.test.ts +44 -1
  265. package/src/cli/commands/__tests__/inference-providers.test.ts +7 -0
  266. package/src/cli/commands/__tests__/plugins.test.ts +692 -0
  267. package/src/cli/commands/__tests__/schedules.test.ts +136 -37
  268. package/src/cli/commands/conversations.ts +7 -3
  269. package/src/cli/commands/credentials.help.ts +7 -0
  270. package/src/cli/commands/inference-profiles.ts +9 -3
  271. package/src/cli/commands/inference.help.ts +16 -2
  272. package/src/cli/commands/mcp.help.ts +4 -2
  273. package/src/cli/commands/memory/memory-retrospective.ts +18 -0
  274. package/src/cli/commands/notifications.ts +3 -6
  275. package/src/cli/commands/oauth/connect-surface-guidance.test.ts +16 -0
  276. package/src/cli/commands/oauth/connect-surface-guidance.ts +25 -7
  277. package/src/cli/commands/oauth/connect.test.ts +26 -1
  278. package/src/cli/commands/oauth/connect.ts +4 -1
  279. package/src/cli/commands/oauth/index.help.ts +3 -1
  280. package/src/cli/commands/oauth/providers.ts +3 -0
  281. package/src/cli/commands/plugins.help.ts +25 -2
  282. package/src/cli/commands/plugins.ts +289 -10
  283. package/src/cli/commands/schedules.help.ts +14 -12
  284. package/src/cli/commands/schedules.ts +19 -5
  285. package/src/cli/commands/tools.help.ts +4 -2
  286. package/src/cli/commands/tools.ts +38 -0
  287. package/src/cli/lib/__tests__/inspect-plugin.test.ts +1 -0
  288. package/src/cli/lib/__tests__/install-from-github.test.ts +79 -0
  289. package/src/cli/lib/__tests__/install-from-platform.test.ts +47 -1
  290. package/src/cli/lib/__tests__/plugin-surfaces.test.ts +86 -7
  291. package/src/cli/lib/__tests__/upgrade-plugin.test.ts +122 -0
  292. package/src/cli/lib/install-from-github.ts +61 -0
  293. package/src/cli/lib/install-from-platform.ts +13 -2
  294. package/src/cli/lib/plugin-surfaces.ts +124 -4
  295. package/src/cli/lib/upgrade-plugin.ts +18 -0
  296. package/src/cli.ts +1 -1
  297. package/src/config/__tests__/deployment-context-defaults.test.ts +73 -0
  298. package/src/config/__tests__/memory-retrospective-schema.test.ts +40 -0
  299. package/src/config/__tests__/plugin-updates-schema.test.ts +43 -0
  300. package/src/config/__tests__/profile-tool-support.test.ts +131 -0
  301. package/src/config/bundled-skills/AGENTS.md +1 -1
  302. package/src/config/bundled-skills/app-builder/SKILL.md +1 -1
  303. package/src/config/bundled-skills/app-builder/references/RESPONSIVE.md +3 -3
  304. package/src/config/bundled-skills/schedule/SKILL.md +5 -1
  305. package/src/config/bundled-skills/schedule/TOOLS.json +2 -2
  306. package/src/config/bundled-skills/settings/TOOLS.json +2 -2
  307. package/src/config/bundled-skills/settings/tools/voice-config-update.ts +1 -0
  308. package/src/config/bundled-skills/subagent/SKILL.md +53 -22
  309. package/src/config/bundled-skills/subagent/TOOLS.json +14 -13
  310. package/src/config/bundled-skills/visualize/SKILL.md +1 -1
  311. package/src/config/default-profile-catalog.ts +6 -2
  312. package/src/config/feature-flag-registry.json +25 -17
  313. package/src/config/llm-resolver.ts +45 -15
  314. package/src/config/loader.ts +70 -1
  315. package/src/config/profile-tool-support.ts +110 -0
  316. package/src/config/sanitize-for-transfer.ts +11 -0
  317. package/src/config/schema.ts +4 -0
  318. package/src/config/schemas/__tests__/stt.test.ts +20 -5
  319. package/src/config/schemas/channels.ts +13 -0
  320. package/src/config/schemas/memory-retrospective.ts +25 -0
  321. package/src/config/schemas/plugin-updates.ts +55 -0
  322. package/src/config/schemas/services.ts +8 -4
  323. package/src/config/schemas/stt.ts +17 -5
  324. package/src/contacts/__tests__/guardian-delivery-reader.test.ts +16 -0
  325. package/src/contacts/guardian-delivery-reader.ts +29 -2
  326. package/src/context/compactor.ts +4 -4
  327. package/src/context/post-turn-tool-result-truncation.ts +32 -16
  328. package/src/context/strip-injections.ts +42 -0
  329. package/src/context/tool-result-spool.ts +32 -18
  330. package/src/credential-execution/ces-runtime.ts +3 -0
  331. package/src/credential-execution/client.ts +22 -1
  332. package/src/credential-execution/process-manager.test.ts +99 -0
  333. package/src/credential-execution/process-manager.ts +62 -16
  334. package/src/daemon/__tests__/abort-null-controller.test.ts +141 -5
  335. package/src/daemon/__tests__/conversation-surfaces-launch.test.ts +8 -12
  336. package/src/daemon/__tests__/install-assistant-command.test.ts +234 -0
  337. package/src/daemon/__tests__/turn-tail-assistant-reply-notify.test.ts +2 -4
  338. package/src/daemon/__tests__/turn-tail-deleted-conversation.test.ts +20 -29
  339. package/src/daemon/channel-ui-capability.ts +4 -4
  340. package/src/daemon/conversation-agent-loop-handlers.ts +117 -61
  341. package/src/daemon/conversation-agent-loop.ts +296 -145
  342. package/src/daemon/conversation-attachments.ts +1 -1
  343. package/src/daemon/conversation-error.ts +8 -1
  344. package/src/daemon/conversation-initial-prompt.ts +16 -38
  345. package/src/daemon/conversation-lifecycle.ts +154 -32
  346. package/src/daemon/conversation-messaging.ts +85 -42
  347. package/src/daemon/conversation-process.ts +76 -28
  348. package/src/daemon/conversation-runtime-assembly.ts +9 -4
  349. package/src/daemon/conversation-store.ts +52 -3
  350. package/src/daemon/conversation-surface-state.ts +9 -0
  351. package/src/daemon/conversation-surfaces.ts +403 -210
  352. package/src/daemon/conversation-tool-setup.ts +56 -7
  353. package/src/daemon/conversation-turn-finalize.ts +134 -89
  354. package/src/daemon/conversation.ts +105 -27
  355. package/src/daemon/disk-pressure-guard-lifecycle.ts +2 -2
  356. package/src/daemon/disk-pressure-guard.ts +3 -3
  357. package/src/daemon/doordash-steps.ts +5 -5
  358. package/src/daemon/external-plugins-bootstrap.ts +7 -1
  359. package/src/daemon/handlers/config-channels.ts +48 -5
  360. package/src/daemon/handlers/conversation-history.ts +1 -1
  361. package/src/daemon/handlers/conversations.ts +62 -17
  362. package/src/daemon/handlers/shared.ts +12 -0
  363. package/src/daemon/handlers/skills.ts +11 -1
  364. package/src/daemon/identity-helpers.ts +38 -10
  365. package/src/daemon/install-assistant-command.ts +322 -0
  366. package/src/daemon/lifecycle.ts +14 -3
  367. package/src/daemon/message-types/conversations.ts +6 -7
  368. package/src/daemon/message-types/sync.ts +1 -0
  369. package/src/daemon/persist-media-references.ts +23 -14
  370. package/src/daemon/process-message.ts +6 -6
  371. package/src/daemon/tool-setup-types.ts +19 -122
  372. package/src/daemon/tool-side-effects.ts +17 -94
  373. package/src/daemon/trust-context.ts +14 -6
  374. package/src/daemon/turn-tail-chain.ts +73 -0
  375. package/src/daemon/web-search-history.ts +47 -33
  376. package/src/heartbeat/heartbeat-service.ts +6 -6
  377. package/src/ipc/__tests__/socket-path.test.ts +21 -0
  378. package/src/ipc/assistant-server.ts +17 -10
  379. package/src/ipc/cli-client.ts +10 -1
  380. package/src/ipc/routes/__tests__/documents-sync-ipc-routes.test.ts +56 -0
  381. package/src/ipc/routes/documents-sync-ipc-routes.ts +42 -0
  382. package/src/ipc/socket-cleanup.ts +9 -7
  383. package/src/ipc/socket-path.ts +4 -96
  384. package/src/live-voice/__tests__/live-voice-archive.test.ts +16 -16
  385. package/src/live-voice/__tests__/live-voice-attach-image.test.ts +209 -0
  386. package/src/live-voice/__tests__/live-voice-connection.test.ts +20 -0
  387. package/src/live-voice/__tests__/live-voice-integration.test.ts +7 -5
  388. package/src/live-voice/__tests__/live-voice-session-telemetry.test.ts +265 -0
  389. package/src/live-voice/__tests__/live-voice-stt.test.ts +6 -1
  390. package/src/live-voice/__tests__/live-voice-vad.test.ts +35 -15
  391. package/src/live-voice/__tests__/protocol.test.ts +52 -0
  392. package/src/live-voice/__tests__/runtime-websocket-shell.test.ts +3 -0
  393. package/src/live-voice/live-voice-archive.ts +13 -13
  394. package/src/live-voice/live-voice-connection.ts +7 -1
  395. package/src/live-voice/live-voice-photo.ts +169 -0
  396. package/src/live-voice/live-voice-session.ts +108 -4
  397. package/src/live-voice/protocol.ts +89 -1
  398. package/src/mcp/client.ts +10 -0
  399. package/src/messaging/providers/__tests__/callback-routing.test.ts +5 -1
  400. package/src/messaging/providers/__tests__/transport-dispatch.test.ts +66 -4
  401. package/src/messaging/providers/callback-routing.ts +1 -0
  402. package/src/messaging/providers/discord/api.ts +312 -0
  403. package/src/messaging/providers/discord/dm-delivery.test.ts +204 -0
  404. package/src/messaging/providers/discord/render.test.ts +204 -0
  405. package/src/messaging/providers/discord/render.ts +195 -0
  406. package/src/messaging/providers/discord/send.test.ts +179 -0
  407. package/src/messaging/providers/discord/send.ts +212 -0
  408. package/src/messaging/providers/discord/transport.ts +81 -0
  409. package/src/messaging/providers/index.ts +3 -1
  410. package/src/messaging/providers/retry-policy.test.ts +247 -0
  411. package/src/messaging/providers/retry-policy.ts +206 -0
  412. package/src/messaging/providers/slack/mention-source.test.ts +285 -0
  413. package/src/messaging/providers/slack/mention-source.ts +287 -0
  414. package/src/messaging/providers/telegram-bot/api.ts +47 -130
  415. package/src/messaging/providers/whatsapp/api.ts +47 -136
  416. package/src/monitoring/__tests__/db-integrity-sample.test.ts +4 -1
  417. package/src/monitoring/__tests__/plugin-auto-update.test.ts +332 -0
  418. package/src/monitoring/db-integrity-sample.ts +6 -1
  419. package/src/monitoring/plugin-auto-update.ts +359 -0
  420. package/src/monitoring/resource-sampler.ts +21 -5
  421. package/src/monitoring/worker.ts +13 -0
  422. package/src/notifications/__tests__/assistant-reply-producer.test.ts +375 -2
  423. package/src/notifications/__tests__/broadcaster.test.ts +37 -1
  424. package/src/notifications/__tests__/copy-composer.test.ts +165 -0
  425. package/src/notifications/__tests__/emit-signal-pipeline-failure.test.ts +205 -0
  426. package/src/notifications/__tests__/notification-utils.test.ts +249 -0
  427. package/src/notifications/adapters/platform.ts +31 -2
  428. package/src/notifications/assistant-reply-producer.ts +79 -32
  429. package/src/notifications/broadcaster.ts +30 -10
  430. package/src/notifications/copy-composer.ts +69 -0
  431. package/src/notifications/emit-signal.ts +82 -15
  432. package/src/notifications/events-store.ts +10 -2
  433. package/src/notifications/home-feed-side-effect.ts +6 -3
  434. package/src/notifications/notification-utils.ts +173 -5
  435. package/src/notifications/signal.ts +12 -0
  436. package/src/oauth/byo-connection.ts +6 -1
  437. package/src/oauth/credential-token-resolver.ts +3 -10
  438. package/src/oauth/manual-token-connection.ts +29 -64
  439. package/src/oauth/manual-token-providers.ts +62 -0
  440. package/src/oauth/provider-serializer.ts +13 -0
  441. package/src/oauth/seed-providers.ts +56 -8
  442. package/src/onboarding/onboarding-events-store.ts +91 -1
  443. package/src/permissions/question-prompter.test.ts +4 -0
  444. package/src/permissions/question-prompter.ts +21 -4
  445. package/src/persistence/__tests__/conversation-lineage.test.ts +248 -0
  446. package/src/persistence/attachments-store.ts +15 -15
  447. package/src/persistence/conversation-attention-store.ts +30 -10
  448. package/src/persistence/conversation-bootstrap.ts +17 -0
  449. package/src/persistence/conversation-crud.ts +363 -97
  450. package/src/persistence/conversation-group-migration.ts +1 -1
  451. package/src/persistence/conversation-key-store.ts +38 -10
  452. package/src/persistence/conversation-lineage.ts +226 -0
  453. package/src/persistence/conversation-plugin-facade.ts +28 -3
  454. package/src/persistence/conversation-queries.ts +207 -33
  455. package/src/persistence/conversation-types.ts +107 -1
  456. package/src/persistence/jobs-store.ts +32 -8
  457. package/src/persistence/llm-usage-store.ts +22 -0
  458. package/src/persistence/migrations/361-normalize-managed-connection-rows.test.ts +168 -0
  459. package/src/persistence/migrations/361-normalize-managed-connection-rows.ts +76 -0
  460. package/src/persistence/migrations/362-add-conversation-subagent-kind.test.ts +187 -0
  461. package/src/persistence/migrations/362-add-conversation-subagent-kind.ts +93 -0
  462. package/src/persistence/migrations/363-backfill-schedule-inference-profile.test.ts +229 -0
  463. package/src/persistence/migrations/363-backfill-schedule-inference-profile.ts +127 -0
  464. package/src/persistence/migrations/364-add-schedule-source-key.test.ts +120 -0
  465. package/src/persistence/migrations/364-add-schedule-source-key.ts +43 -0
  466. package/src/persistence/migrations/365-add-conversation-fork-strategy.ts +27 -0
  467. package/src/persistence/schema/conversations.ts +32 -0
  468. package/src/persistence/schema/infrastructure.ts +3 -0
  469. package/src/persistence/steps.ts +30 -0
  470. package/src/persistence/subagent-store.ts +148 -6
  471. package/src/platform/managed-speech.test.ts +20 -0
  472. package/src/platform/managed-speech.ts +9 -0
  473. package/src/plugin-api/index.ts +8 -0
  474. package/src/plugin-api/webhook-url.ts +151 -0
  475. package/src/plugins/__tests__/installed-plugin-dirs.test.ts +82 -0
  476. package/src/plugins/__tests__/source-fingerprint.test.ts +5 -1
  477. package/src/plugins/collect-source-versions.ts +4 -27
  478. package/src/plugins/defaults/exploration-drift/hooks/post-tool-use.ts +5 -5
  479. package/src/plugins/defaults/exploration-drift/package.json +1 -1
  480. package/src/plugins/defaults/image-recovery/hooks/post-model-call.ts +4 -2
  481. package/src/plugins/defaults/image-recovery/recover.ts +67 -57
  482. package/src/plugins/defaults/index.ts +1 -1
  483. package/src/plugins/defaults/memory/__tests__/conversation-queries.test.ts +64 -18
  484. package/src/plugins/defaults/memory/__tests__/jobs-worker-outcome-contract.test.ts +242 -0
  485. package/src/plugins/defaults/memory/__tests__/jobs-worker-retrospective-sweep.test.ts +24 -1
  486. package/src/plugins/defaults/memory/__tests__/memory-retrospective-enqueue.test.ts +48 -3
  487. package/src/plugins/defaults/memory/__tests__/memory-retrospective-job.test.ts +511 -19
  488. package/src/plugins/defaults/memory/__tests__/memory-retrospective-prompt.test.ts +39 -3
  489. package/src/plugins/defaults/memory/__tests__/memory-retrospective-provider-path.test.ts +493 -0
  490. package/src/plugins/defaults/memory/__tests__/memory-retrospective-wake-chain.test.ts +5 -4
  491. package/src/plugins/defaults/memory/__tests__/memory-tier-boundary-guard.test.ts +5 -1
  492. package/src/plugins/defaults/memory/job-handlers.ts +87 -5
  493. package/src/plugins/defaults/memory/jobs-worker.ts +60 -6
  494. package/src/plugins/defaults/memory/memory-retrospective-constants.ts +11 -0
  495. package/src/plugins/defaults/memory/memory-retrospective-enqueue.ts +29 -0
  496. package/src/plugins/defaults/memory/memory-retrospective-job.ts +342 -60
  497. package/src/plugins/defaults/memory/memory-retrospective-prompt.ts +23 -3
  498. package/src/plugins/defaults/memory/substrate/consolidation-lock.ts +9 -4
  499. package/src/plugins/defaults/memory/substrate/sweep-job.ts +7 -0
  500. package/src/plugins/defaults/memory/v1/graph/injection.ts +1 -1
  501. package/src/plugins/defaults/memory/v2/router.ts +8 -0
  502. package/src/plugins/defaults/turn-context/unified-turn-context.ts +1 -2
  503. package/src/plugins/external-plugin-loader.ts +17 -8
  504. package/src/plugins/installed-plugin-dirs.ts +101 -0
  505. package/src/plugins/mtime-cache.ts +34 -22
  506. package/src/plugins/types.ts +69 -8
  507. package/src/prompts/persona-resolver.ts +6 -2
  508. package/src/prompts/system-prompt.ts +72 -17
  509. package/src/providers/__tests__/inference.test.ts +40 -0
  510. package/src/providers/__tests__/preflight-resolved-config.test.ts +58 -1
  511. package/src/providers/__tests__/retry-callsite.test.ts +67 -0
  512. package/src/providers/__tests__/vellum-connection-routing.test.ts +42 -11
  513. package/src/providers/anthropic/client.ts +112 -95
  514. package/src/providers/connection-resolution.ts +35 -1
  515. package/src/providers/gemini/client.ts +5 -5
  516. package/src/providers/inference/adapter-factory.ts +16 -10
  517. package/src/providers/inference/auth.ts +16 -0
  518. package/src/providers/inference/connection-availability.ts +164 -12
  519. package/src/providers/inference/connections.ts +25 -1
  520. package/src/providers/media-resolve.ts +21 -11
  521. package/src/providers/model-catalog.ts +15 -0
  522. package/src/providers/openai/chat-completions-provider.ts +4 -4
  523. package/src/providers/openai/responses-provider.ts +12 -25
  524. package/src/providers/registry.ts +9 -4
  525. package/src/providers/retry.ts +25 -0
  526. package/src/providers/server-tool-pairing.ts +134 -0
  527. package/src/providers/speech-to-text/__tests__/resolve.test.ts +187 -6
  528. package/src/providers/speech-to-text/deepgram.ts +1 -0
  529. package/src/providers/speech-to-text/resolve.ts +80 -10
  530. package/src/providers/speech-to-text/vellum-managed.ts +7 -1
  531. package/src/providers/speech-to-text/vellum-speech-relay-connection.ts +3 -1
  532. package/src/providers/types.ts +26 -6
  533. package/src/providers/vellum-model-routing.ts +6 -7
  534. package/src/runtime/AGENTS.md +2 -0
  535. package/src/runtime/__tests__/agent-wake.test.ts +16 -16
  536. package/src/runtime/__tests__/background-job-runner.test.ts +0 -144
  537. package/src/runtime/__tests__/desktop-presence.test.ts +162 -0
  538. package/src/runtime/agent-wake.ts +63 -24
  539. package/src/runtime/assistant-event-hub.ts +87 -12
  540. package/src/runtime/auth/__tests__/guard-tests.test.ts +2 -0
  541. package/src/runtime/auth/__tests__/scopes.test.ts +20 -0
  542. package/src/runtime/auth/same-actor.ts +56 -8
  543. package/src/runtime/auth/scopes.ts +12 -0
  544. package/src/runtime/auth/types.ts +2 -0
  545. package/src/runtime/background-job-runner.ts +5 -27
  546. package/src/runtime/channel-readiness-service.ts +59 -3
  547. package/src/runtime/channel-readiness-types.ts +18 -0
  548. package/src/runtime/desktop-presence.ts +69 -0
  549. package/src/runtime/http-server.ts +12 -0
  550. package/src/runtime/local-actor-identity.ts +11 -9
  551. package/src/runtime/migrations/__tests__/vbundle-symlink-streaming-importer.test.ts +0 -0
  552. package/src/runtime/routes/__tests__/channel-verification-routes.test.ts +1 -1
  553. package/src/runtime/routes/__tests__/client-routes.test.ts +186 -14
  554. package/src/runtime/routes/__tests__/conversation-list-routes.test.ts +547 -1
  555. package/src/runtime/routes/__tests__/conversation-management-routes.test.ts +162 -7
  556. package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +14 -10
  557. package/src/runtime/routes/__tests__/inference-profiles-routes.test.ts +340 -3
  558. package/src/runtime/routes/__tests__/llm-call-sites-routes.test.ts +28 -1
  559. package/src/runtime/routes/__tests__/plugins-routes.test.ts +173 -1
  560. package/src/runtime/routes/__tests__/question-routes.test.ts +64 -0
  561. package/src/runtime/routes/__tests__/retrospective-routes.test.ts +20 -0
  562. package/src/runtime/routes/__tests__/skill-history-route.test.ts +116 -0
  563. package/src/runtime/routes/__tests__/wake-conversation-routes.test.ts +60 -2
  564. package/src/runtime/routes/archive-utils.ts +16 -12
  565. package/src/runtime/routes/attachment-routes.ts +13 -6
  566. package/src/runtime/routes/canned-reply-release.ts +54 -0
  567. package/src/runtime/routes/channel-verification-routes.ts +2 -27
  568. package/src/runtime/routes/chatgpt-subscription-auth-routes.ts +5 -0
  569. package/src/runtime/routes/client-routes.ts +114 -2
  570. package/src/runtime/routes/conversation-cli-routes.ts +10 -12
  571. package/src/runtime/routes/conversation-list-routes.ts +59 -10
  572. package/src/runtime/routes/conversation-management-routes.ts +47 -18
  573. package/src/runtime/routes/conversation-query-routes.ts +32 -53
  574. package/src/runtime/routes/conversation-routes.ts +350 -293
  575. package/src/runtime/routes/credential-prompt-routes.ts +23 -2
  576. package/src/runtime/routes/debug-routes.ts +4 -1
  577. package/src/runtime/routes/default-provider-routes.ts +7 -11
  578. package/src/runtime/routes/documents-routes.ts +44 -1
  579. package/src/runtime/routes/events-routes.ts +28 -1
  580. package/src/runtime/routes/host-app-control-routes.ts +2 -0
  581. package/src/runtime/routes/host-bash-routes.ts +2 -0
  582. package/src/runtime/routes/host-browser-routes.ts +2 -0
  583. package/src/runtime/routes/host-cu-routes.ts +2 -0
  584. package/src/runtime/routes/host-file-routes.ts +2 -0
  585. package/src/runtime/routes/host-transfer-routes.ts +4 -0
  586. package/src/runtime/routes/identity-routes.ts +6 -6
  587. package/src/runtime/routes/inbound-message-handler.ts +5 -5
  588. package/src/runtime/routes/inbound-stages/acl-enforcement.ts +7 -3
  589. package/src/runtime/routes/inbound-stages/background-dispatch.ts +14 -0
  590. package/src/runtime/routes/inbound-stages/bootstrap-intercept.ts +2 -2
  591. package/src/runtime/routes/inbound-stages/guardian-activation-intercept.test.ts +14 -11
  592. package/src/runtime/routes/inbound-stages/guardian-activation-intercept.ts +14 -17
  593. package/src/runtime/routes/inference-profile-availability-guard.ts +171 -0
  594. package/src/runtime/routes/inference-profile-session-handler.ts +26 -2
  595. package/src/runtime/routes/inference-profile-session-routes.ts +2 -1
  596. package/src/runtime/routes/inference-profiles-routes.ts +152 -73
  597. package/src/runtime/routes/inference-provider-connection-routes.ts +0 -16
  598. package/src/runtime/routes/llm-call-sites-routes.ts +13 -0
  599. package/src/runtime/routes/log-export-routes.ts +1 -1
  600. package/src/runtime/routes/notification-routes.ts +0 -14
  601. package/src/runtime/routes/oauth-providers.ts +1 -0
  602. package/src/runtime/routes/plugins-routes.ts +106 -1
  603. package/src/runtime/routes/profiler-routes.ts +4 -2
  604. package/src/runtime/routes/question-routes.ts +28 -10
  605. package/src/runtime/routes/retrospective-routes.ts +9 -3
  606. package/src/runtime/routes/schedule-routes.ts +210 -12
  607. package/src/runtime/routes/secret-routes.ts +40 -23
  608. package/src/runtime/routes/settings-routes.ts +41 -13
  609. package/src/runtime/routes/skills-routes.ts +79 -0
  610. package/src/runtime/routes/ui-snapshot-routes.ts +1 -0
  611. package/src/runtime/routes/wake-conversation-routes.ts +23 -0
  612. package/src/runtime/services/__tests__/conversation-serializer.test.ts +1 -0
  613. package/src/runtime/services/conversation-serializer.ts +24 -9
  614. package/src/runtime/sync/documents-sidecar-publish.test.ts +101 -0
  615. package/src/runtime/sync/resource-sync-events.ts +79 -1
  616. package/src/runtime/sync/worker-daemon-notify.test.ts +34 -0
  617. package/src/runtime/sync/worker-daemon-notify.ts +44 -6
  618. package/src/runtime/verification-outbound-actions.ts +250 -222
  619. package/src/runtime/verification-templates.ts +41 -30
  620. package/src/schedule/__tests__/plugin-schedule-declarations.test.ts +614 -0
  621. package/src/schedule/__tests__/plugin-schedule-reconciler.test.ts +1066 -0
  622. package/src/schedule/inference-profile.ts +64 -3
  623. package/src/schedule/plugin-schedule-declarations.ts +518 -0
  624. package/src/schedule/plugin-schedule-reconciler.ts +605 -0
  625. package/src/schedule/plugin-schedules-gate.ts +23 -0
  626. package/src/schedule/schedule-store.ts +520 -26
  627. package/src/schedule/scheduler.ts +38 -0
  628. package/src/security/credential-backend.ts +51 -4
  629. package/src/security/encrypted-store.ts +64 -42
  630. package/src/security/secure-keys.ts +55 -7
  631. package/src/skills/catalog-install.ts +20 -7
  632. package/src/skills/inline-command-runner.ts +2 -7
  633. package/src/skills/skill-history.test.ts +269 -0
  634. package/src/skills/skill-history.ts +250 -0
  635. package/src/skills/skillssh-registry.ts +1 -1
  636. package/src/stt/daemon-batch-transcriber.ts +8 -6
  637. package/src/subagent/__tests__/consult-prompt.test.ts +25 -0
  638. package/src/subagent/consult-prompt.ts +3 -1
  639. package/src/subagent/consult-transcript.ts +1 -1
  640. package/src/subagent/manager.ts +295 -42
  641. package/src/subagent/progress-events.ts +35 -0
  642. package/src/subagent/role-resolution.ts +88 -0
  643. package/src/subagent/types.ts +302 -67
  644. package/src/subagent/validate-allowlists.ts +3 -4
  645. package/src/telegram/__tests__/webhook-health.test.ts +73 -1
  646. package/src/telegram/webhook-health.ts +101 -3
  647. package/src/telemetry/__tests__/telemetry-event-fixtures.ts +2 -0
  648. package/src/telemetry/activation-funnel.ts +6 -1
  649. package/src/telemetry/live-voice-funnel.ts +75 -0
  650. package/src/telemetry/telemetry-event-sources.ts +6 -0
  651. package/src/telemetry/telemetry-wire-source.json +1 -1
  652. package/src/telemetry/telemetry-wire.generated.ts +2 -0
  653. package/src/telemetry/types.ts +23 -4
  654. package/src/telemetry/usage-telemetry-reporter.test.ts +4 -0
  655. package/src/tools/__tests__/subagent-workflow-tool-input-schemas.test.ts +1 -1
  656. package/src/tools/ask-question/ask-question-tool.test.ts +118 -3
  657. package/src/tools/ask-question/ask-question-tool.ts +56 -2
  658. package/src/tools/browser/browser-execution.ts +1 -1
  659. package/src/tools/calls/call-start.ts +8 -5
  660. package/src/tools/conversation-groups/move_to_group.ts +10 -12
  661. package/src/tools/document/document-tool.ts +11 -4
  662. package/src/tools/executor.ts +7 -6
  663. package/src/tools/filesystem/edit.ts +1 -1
  664. package/src/tools/filesystem/list.ts +1 -1
  665. package/src/tools/filesystem/read.ts +7 -13
  666. package/src/tools/filesystem/search.ts +23 -21
  667. package/src/tools/filesystem/write.ts +4 -1
  668. package/src/tools/host-filesystem/edit.ts +7 -2
  669. package/src/tools/host-filesystem/read.ts +21 -8
  670. package/src/tools/host-filesystem/write.ts +10 -2
  671. package/src/tools/host-terminal/host-shell.ts +2 -2
  672. package/src/tools/mcp/mcp-tool-factory.ts +25 -1
  673. package/src/tools/permission-checker.ts +8 -3
  674. package/src/tools/schedule/delete.ts +13 -1
  675. package/src/tools/schedule/list.ts +14 -3
  676. package/src/tools/schedule/update.ts +56 -33
  677. package/src/tools/shared/filesystem/audio-read.ts +6 -4
  678. package/src/tools/shared/filesystem/file-ops-service.ts +176 -116
  679. package/src/tools/shared/filesystem/image-read.ts +12 -12
  680. package/src/tools/shared/filesystem/path-policy.ts +70 -5
  681. package/src/tools/shared/filesystem/size-guard.ts +6 -6
  682. package/src/tools/shared/input-misuse.ts +72 -0
  683. package/src/tools/skills/scaffold-managed.ts +82 -0
  684. package/src/tools/skills/skill-tool-factory.ts +14 -1
  685. package/src/tools/subagent/consult-deadline.ts +12 -7
  686. package/src/tools/subagent/message.ts +3 -1
  687. package/src/tools/subagent/read.ts +66 -4
  688. package/src/tools/subagent/spawn.ts +500 -78
  689. package/src/tools/types.ts +22 -3
  690. package/src/tools/ui-surface/surface-shape-docs.ts +1 -1
  691. package/src/tools/ui-surface/visual-validation.ts +2 -0
  692. package/src/usage/__tests__/subagent-attribution.test.ts +109 -0
  693. package/src/usage/subagent-attribution.ts +108 -0
  694. package/src/util/__tests__/sqlite-retry.test.ts +112 -0
  695. package/src/util/abort-reasons.ts +16 -0
  696. package/src/util/ansi.ts +44 -0
  697. package/src/util/disk-usage.ts +17 -13
  698. package/src/util/image-conversion.ts +20 -13
  699. package/src/util/schedule-source-key.ts +23 -0
  700. package/src/util/worker-process.ts +7 -1
  701. package/src/workspace/git-service.ts +33 -9
  702. package/src/workspace/migrations/140-repair-seed-pinned-memory-v3-live.ts +260 -0
  703. package/src/workspace/migrations/141-stt-english-default-to-multilingual.ts +111 -0
  704. package/src/workspace/migrations/__tests__/141-stt-english-default-to-multilingual.test.ts +126 -0
  705. package/src/workspace/migrations/registry.ts +4 -0
  706. package/src/workspace/top-level-renderer.ts +15 -2
  707. package/src/__tests__/tool-side-effects-slack-dm.test.ts +0 -357
  708. package/src/daemon/install-symlink.ts +0 -207
  709. package/src/notifications/deferred-emit.ts +0 -147
@@ -1,5 +1,6 @@
1
1
  import { z } from "zod";
2
2
 
3
+ import type { AnsweredQuestion } from "../api/events/question-answered.js";
3
4
  import type { InterfaceId } from "../channels/types.js";
4
5
  import type { LLMCallSite } from "../config/schemas/llm.js";
5
6
  import type { ToolActivityMetadata } from "../daemon/message-types/web-activity.js";
@@ -133,6 +134,14 @@ export interface ToolExecutionResult {
133
134
  /** Structured activity metadata for client rendering (web search, web fetch, etc).
134
135
  * Populated by daemon-internal tools; plugins must not set this. */
135
136
  activityMetadata?: ToolActivityMetadata;
137
+ /**
138
+ * Typed side channel from the `ask_question` executor to the agent loop: the
139
+ * questions asked and the answers the user gave. The loop forwards it on the
140
+ * `tool_result` event and persists it on the tool_use block, so the answered
141
+ * card renders live and survives a history reopen instead of the decision
142
+ * disappearing with the interactive prompt. Set only by `ask_question`.
143
+ */
144
+ answeredQuestion?: AnsweredQuestion;
136
145
  }
137
146
 
138
147
  export type ProxyToolResolver = (
@@ -407,6 +416,14 @@ export interface ToolContext {
407
416
  * @legacy
408
417
  */
409
418
  overrideProfile?: string;
419
+ /**
420
+ * The firing's `cron_runs.id` when a schedule triggered this turn, `null`
421
+ * otherwise. Tools that spawn further LLM work (`subagent_spawn`,
422
+ * `subagent_message`) forward it so the delegated usage rows carry the same
423
+ * stamp and attribute to the firing rather than dropping out of schedule
424
+ * cost reporting.
425
+ */
426
+ cronRunId?: string | null;
410
427
  /**
411
428
  * The LLM call site of the turn currently executing this tool (`mainAgent`,
412
429
  * `heartbeatAgent`, scheduled work, etc.). `subagent_spawn` reads it to
@@ -418,10 +435,12 @@ export interface ToolContext {
418
435
  invokingCallSite?: LLMCallSite;
419
436
  /**
420
437
  * Canonical principal ID of the actor on whose behalf this tool invocation
421
- * is running. Sourced from `conversation.trustContext.guardianPrincipalId`.
438
+ * is running — the turn's actor principal, falling back to the trust
439
+ * context's `guardianPrincipalId` (see `resolveTurnActorPrincipalId`).
422
440
  * Used by host proxies to bind cross-client targeted execution to the same
423
- * authenticated user identity. May be undefined for legacy/internal flows
424
- * with no resolved actor identity.
441
+ * authenticated user identity, so it must resolve to the SAME principal a
442
+ * desktop client registers with on its SSE stream. May be undefined for
443
+ * legacy/internal flows with no resolved actor identity.
425
444
  * @legacy
426
445
  */
427
446
  sourceActorPrincipalId?: string;
@@ -132,7 +132,7 @@ export const SURFACE_SHAPE_DOCS: Record<string, SurfaceShapeDoc> = {
132
132
  oauth_connect: {
133
133
  purpose: "managed OAuth connection button for an integration account",
134
134
  shape:
135
- "{ providerKey, displayName?, description?, logoUrl? } — managed OAuth connection CTA; use when the task needs a managed integration account (Google, Linear, GitHub, ...) instead of settings or shell OAuth. Do not include OAuth scopes; managed providers use the platform's configured scopes",
135
+ "{ providerKey, requestedScopes?, displayName?, description?, logoUrl? }: managed OAuth connection CTA; use when the task needs a managed integration account (Google, Linear, GitHub, ...) instead of settings or shell OAuth. requestedScopes is an optional FULL replacement set of OAuth scopes: when set, the connection is granted exactly these scopes instead of the platform defaults, so include every scope the connection should keep; omit it to use the platform's default scopes",
136
136
  missingContent: (data) =>
137
137
  isNonEmptyString(data.providerKey)
138
138
  ? null
@@ -79,8 +79,10 @@ export const WIDGET_TOKEN_PROPERTIES: readonly string[] = [
79
79
  // System / status
80
80
  "--system-positive-strong",
81
81
  "--system-positive-weak",
82
+ "--system-positive-on-weak",
82
83
  "--system-negative-strong",
83
84
  "--system-negative-weak",
85
+ "--system-negative-on-weak",
84
86
  "--system-negative-hover",
85
87
  "--system-mid-strong",
86
88
  "--system-mid-weak",
@@ -0,0 +1,109 @@
1
+ import { beforeEach, describe, expect, test } from "bun:test";
2
+
3
+ import { getDb } from "../../persistence/db-connection.js";
4
+ import { initializeDb } from "../../persistence/db-init.js";
5
+ import {
6
+ resetSubagentAttributionCacheForTests,
7
+ resolveSubagentAttribution,
8
+ } from "../subagent-attribution.js";
9
+
10
+ await initializeDb();
11
+
12
+ let counter = 0;
13
+
14
+ function insertConversation(opts: {
15
+ subagentRole?: string;
16
+ subagentSpawnMode?: string;
17
+ }): string {
18
+ counter += 1;
19
+ const id = `conv-attr-${counter}`;
20
+ const db = getDb();
21
+ db.run(
22
+ `INSERT INTO conversations (id, conversation_type, created_at, updated_at, subagent_role, subagent_spawn_mode)
23
+ VALUES ('${id}', 'background', 1000, 1000, ${
24
+ opts.subagentRole === undefined ? "NULL" : `'${opts.subagentRole}'`
25
+ }, ${
26
+ opts.subagentSpawnMode === undefined
27
+ ? "NULL"
28
+ : `'${opts.subagentSpawnMode}'`
29
+ })`,
30
+ );
31
+ return id;
32
+ }
33
+
34
+ describe("resolveSubagentAttribution", () => {
35
+ beforeEach(() => {
36
+ resetSubagentAttributionCacheForTests();
37
+ });
38
+
39
+ test("returns the stamped role and spawn mode for a subagent conversation", () => {
40
+ const id = insertConversation({
41
+ subagentRole: "advisor",
42
+ subagentSpawnMode: "advisor_consult",
43
+ });
44
+
45
+ expect(resolveSubagentAttribution(id)).toEqual({
46
+ subagentRole: "advisor",
47
+ subagentSpawnMode: "advisor_consult",
48
+ });
49
+ });
50
+
51
+ test("returns nulls for an ordinary conversation", () => {
52
+ const id = insertConversation({});
53
+
54
+ expect(resolveSubagentAttribution(id)).toEqual({
55
+ subagentRole: null,
56
+ subagentSpawnMode: null,
57
+ });
58
+ });
59
+
60
+ test("returns nulls without a conversation id", () => {
61
+ expect(resolveSubagentAttribution(undefined)).toEqual({
62
+ subagentRole: null,
63
+ subagentSpawnMode: null,
64
+ });
65
+ expect(resolveSubagentAttribution("")).toEqual({
66
+ subagentRole: null,
67
+ subagentSpawnMode: null,
68
+ });
69
+ });
70
+
71
+ test("returns nulls for an unknown conversation and does not cache the miss", () => {
72
+ // The columns are stamped at conversation creation, so a miss means the
73
+ // row has not landed yet, and caching it would pin an empty result.
74
+ expect(resolveSubagentAttribution("conv-not-yet")).toEqual({
75
+ subagentRole: null,
76
+ subagentSpawnMode: null,
77
+ });
78
+
79
+ const db = getDb();
80
+ db.run(
81
+ `INSERT INTO conversations (id, conversation_type, created_at, updated_at, subagent_role, subagent_spawn_mode)
82
+ VALUES ('conv-not-yet', 'background', 1000, 1000, 'coder', 'regular')`,
83
+ );
84
+
85
+ expect(resolveSubagentAttribution("conv-not-yet")).toEqual({
86
+ subagentRole: "coder",
87
+ subagentSpawnMode: "regular",
88
+ });
89
+ });
90
+
91
+ test("memoizes a resolved conversation", () => {
92
+ const id = insertConversation({
93
+ subagentRole: "general",
94
+ subagentSpawnMode: "fork",
95
+ });
96
+ expect(resolveSubagentAttribution(id).subagentSpawnMode).toBe("fork");
97
+
98
+ // The columns are immutable after creation, so a later write must not be
99
+ // observable, proving the second call did not hit the database.
100
+ const db = getDb();
101
+ db.run(
102
+ `UPDATE conversations SET subagent_spawn_mode = 'regular' WHERE id = '${id}'`,
103
+ );
104
+
105
+ expect(resolveSubagentAttribution(id).subagentSpawnMode).toBe("fork");
106
+ resetSubagentAttributionCacheForTests();
107
+ expect(resolveSubagentAttribution(id).subagentSpawnMode).toBe("regular");
108
+ });
109
+ });
@@ -0,0 +1,108 @@
1
+ import { rawGet } from "../persistence/raw-query.js";
2
+ import { getLogger } from "../util/logger.js";
3
+
4
+ const log = getLogger("subagent-attribution");
5
+
6
+ /**
7
+ * The two delegated-work attribution dimensions carried on a conversation:
8
+ * the subagent role it was spawned with, and how it was spawned. Both null
9
+ * for a conversation that is not a subagent.
10
+ */
11
+ export interface SubagentAttribution {
12
+ subagentRole: string | null;
13
+ subagentSpawnMode: string | null;
14
+ }
15
+
16
+ const NOT_A_SUBAGENT: SubagentAttribution = {
17
+ subagentRole: null,
18
+ subagentSpawnMode: null,
19
+ };
20
+
21
+ /**
22
+ * Bound on the memo below. Both columns are stamped once at conversation
23
+ * creation and never updated, so a hit is always correct and the only cost of
24
+ * a bounded cache is an occasional re-read. Sized to comfortably cover the
25
+ * conversations resident in one process without becoming a memory concern.
26
+ */
27
+ const MAX_CACHED_CONVERSATIONS = 2048;
28
+
29
+ /**
30
+ * Insertion-ordered memo. `Map` iteration order is insertion order, so
31
+ * dropping the first key evicts the oldest entry, sufficient for a lookup
32
+ * whose values are immutable and whose miss cost is a single primary-key read.
33
+ */
34
+ const cache = new Map<string, SubagentAttribution>();
35
+
36
+ /**
37
+ * Resolve the delegated-work attribution for a conversation, for forwarding on
38
+ * the runtime proxy's `X-Vellum-Subagent-*` headers.
39
+ *
40
+ * Reads the columns stamped at spawn time (migration 362) rather than the
41
+ * `subagents` table: `subagents` rows are deleted on dispose, and the billing
42
+ * path must not depend on a row that may already be gone.
43
+ *
44
+ * Never throws. This runs on the provider dispatch hot path and attribution
45
+ * must never be able to take a model request down. Any failure degrades to
46
+ * "no attribution", which is exactly how the platform treats a missing header.
47
+ */
48
+ export function resolveSubagentAttribution(
49
+ conversationId: string | undefined,
50
+ ): SubagentAttribution {
51
+ if (conversationId === undefined || conversationId.length === 0) {
52
+ return NOT_A_SUBAGENT;
53
+ }
54
+ const cached = cache.get(conversationId);
55
+ if (cached !== undefined) {
56
+ return cached;
57
+ }
58
+
59
+ const row = readRow(conversationId);
60
+ if (!row) {
61
+ // Either no row yet, or the read failed. Deliberately NOT cached: caching
62
+ // a miss would pin an empty result for a conversation whose row lands
63
+ // moments later, or for the whole process after one transient DB error.
64
+ return NOT_A_SUBAGENT;
65
+ }
66
+
67
+ const attribution: SubagentAttribution = {
68
+ subagentRole: row.subagent_role,
69
+ subagentSpawnMode: row.subagent_spawn_mode,
70
+ };
71
+ if (cache.size >= MAX_CACHED_CONVERSATIONS) {
72
+ const oldest = cache.keys().next();
73
+ if (!oldest.done) {
74
+ cache.delete(oldest.value);
75
+ }
76
+ }
77
+ cache.set(conversationId, attribution);
78
+ return attribution;
79
+ }
80
+
81
+ interface ConversationSubagentRow {
82
+ subagent_role: string | null;
83
+ subagent_spawn_mode: string | null;
84
+ }
85
+
86
+ /**
87
+ * Read the two columns, or `null` when the conversation has no row or the
88
+ * read fails. Failure covers the pre-migration case (columns absent) and any
89
+ * DB-unavailable context, such as a unit test exercising the provider stack
90
+ * alone. Attribution must never be able to take a model request down.
91
+ */
92
+ function readRow(conversationId: string): ConversationSubagentRow | null {
93
+ try {
94
+ return rawGet<ConversationSubagentRow>(
95
+ "usage:subagentAttribution",
96
+ `SELECT subagent_role, subagent_spawn_mode FROM conversations WHERE id = ?`,
97
+ conversationId,
98
+ );
99
+ } catch (err) {
100
+ log.debug({ err, conversationId }, "Subagent attribution lookup failed");
101
+ return null;
102
+ }
103
+ }
104
+
105
+ /** Test seam: drops every memoized entry. */
106
+ export function resetSubagentAttributionCacheForTests(): void {
107
+ cache.clear();
108
+ }
@@ -0,0 +1,112 @@
1
+ /**
2
+ * Tests for `withSqliteRetry()`, the shared retry wrapper for transient
3
+ * SQLite write contention (`SQLITE_BUSY*` / `SQLITE_IOERR*`).
4
+ */
5
+
6
+ import { describe, expect, test } from "bun:test";
7
+
8
+ import { isRetryableSqliteError, withSqliteRetry } from "../sqlite-retry.js";
9
+
10
+ function sqliteError(code: string): Error {
11
+ const err = new Error(code);
12
+ (err as Error & { code: string }).code = code;
13
+ return err;
14
+ }
15
+
16
+ describe("isRetryableSqliteError", () => {
17
+ test("matches the SQLITE_BUSY and SQLITE_IOERR families", () => {
18
+ expect(isRetryableSqliteError(sqliteError("SQLITE_BUSY"))).toBe(true);
19
+ expect(isRetryableSqliteError(sqliteError("SQLITE_BUSY_SNAPSHOT"))).toBe(
20
+ true,
21
+ );
22
+ expect(isRetryableSqliteError(sqliteError("SQLITE_IOERR"))).toBe(true);
23
+ expect(isRetryableSqliteError(sqliteError("SQLITE_IOERR_SHORT_READ"))).toBe(
24
+ true,
25
+ );
26
+ });
27
+
28
+ test("rejects non-transient errors", () => {
29
+ expect(isRetryableSqliteError(sqliteError("SQLITE_CONSTRAINT"))).toBe(
30
+ false,
31
+ );
32
+ expect(isRetryableSqliteError(new Error("database is locked"))).toBe(false);
33
+ expect(isRetryableSqliteError(null)).toBe(false);
34
+ expect(isRetryableSqliteError(undefined)).toBe(false);
35
+ });
36
+ });
37
+
38
+ describe("withSqliteRetry", () => {
39
+ test("returns the wrapped value on first success", async () => {
40
+ let calls = 0;
41
+ const result = await withSqliteRetry(
42
+ () => {
43
+ calls += 1;
44
+ return "ok";
45
+ },
46
+ { op: "test-op" },
47
+ );
48
+ expect(result).toBe("ok");
49
+ expect(calls).toBe(1);
50
+ });
51
+
52
+ test("retries transient contention and returns the eventual success", async () => {
53
+ let calls = 0;
54
+ const result = await withSqliteRetry(
55
+ () => {
56
+ calls += 1;
57
+ if (calls <= 2) {
58
+ throw sqliteError("SQLITE_BUSY");
59
+ }
60
+ return "recovered";
61
+ },
62
+ { op: "test-op", baseDelayMs: 1 },
63
+ );
64
+ expect(result).toBe("recovered");
65
+ expect(calls).toBe(3);
66
+ });
67
+
68
+ test("awaits an async wrapped function", async () => {
69
+ let calls = 0;
70
+ const result = await withSqliteRetry(
71
+ async () => {
72
+ calls += 1;
73
+ if (calls === 1) {
74
+ throw sqliteError("SQLITE_IOERR");
75
+ }
76
+ return 42;
77
+ },
78
+ { op: "test-op", baseDelayMs: 1 },
79
+ );
80
+ expect(result).toBe(42);
81
+ expect(calls).toBe(2);
82
+ });
83
+
84
+ test("throws immediately on a non-retryable error", async () => {
85
+ let calls = 0;
86
+ await expect(
87
+ withSqliteRetry(
88
+ () => {
89
+ calls += 1;
90
+ throw sqliteError("SQLITE_CONSTRAINT_PRIMARYKEY");
91
+ },
92
+ { op: "test-op", baseDelayMs: 1 },
93
+ ),
94
+ ).rejects.toThrow("SQLITE_CONSTRAINT_PRIMARYKEY");
95
+ expect(calls).toBe(1);
96
+ });
97
+
98
+ test("rethrows after exhausting maxRetries", async () => {
99
+ let calls = 0;
100
+ await expect(
101
+ withSqliteRetry(
102
+ () => {
103
+ calls += 1;
104
+ throw sqliteError("SQLITE_BUSY");
105
+ },
106
+ { op: "test-op", maxRetries: 2, baseDelayMs: 1 },
107
+ ),
108
+ ).rejects.toThrow("SQLITE_BUSY");
109
+ // Initial attempt + 2 retries.
110
+ expect(calls).toBe(3);
111
+ });
112
+ });
@@ -46,6 +46,22 @@ export function createAbortReason(
46
46
  };
47
47
  }
48
48
 
49
+ /**
50
+ * True when the abort came from a person stopping the turn they are watching
51
+ * (the Stop button, Esc, or the CLI's cancel signal file) rather than from the
52
+ * conversation going away or another subsystem seizing it.
53
+ *
54
+ * The distinction matters for the message queue. A user interrupt ends the
55
+ * turn in flight; it says nothing about the messages the same user queued
56
+ * behind it, so those survive the abort and run on the interrupted turn's
57
+ * drain. Every other kind (dispose, eviction, voice supersession, subagent
58
+ * teardown) is the conversation or its owner disappearing, where a queued
59
+ * message has nothing left to run on and is discarded.
60
+ */
61
+ export function isUserInterruptAbort(reason: AbortReason | undefined): boolean {
62
+ return reason?.kind === "user_cancel" || reason?.kind === "signal_cancel";
63
+ }
64
+
49
65
  export function isAbortReason(value: unknown): value is AbortReason {
50
66
  if (typeof value !== "object" || value === null) {
51
67
  return false;
@@ -0,0 +1,44 @@
1
+ /**
2
+ * Stripping of terminal escape sequences and control characters from
3
+ * untrusted strings before they reach a terminal or notification copy.
4
+ *
5
+ * One pattern serves every call site so coverage cannot drift between
6
+ * hand-rolled copies. CSI sequences carry the full parameter and
7
+ * intermediate byte ranges; OSC sequences match through their BEL/ST
8
+ * terminator, with the terminator optional so an unterminated sequence
9
+ * cannot smuggle its body past the strip.
10
+ */
11
+
12
+ /**
13
+ * Well-formed ANSI escape sequences: CSI (`ESC [ params intermediates final`,
14
+ * covering colors, cursor moves, erase) and OSC (`ESC ] payload BEL|ST`,
15
+ * covering window-title writes and hyperlinks).
16
+ */
17
+ const ANSI_SEQUENCE_RE =
18
+ /\u001b\[[0-?]*[ -/]*[@-~]|\u001b\][^\u0007\u001b]*(?:\u0007|\u001b\\)?/g;
19
+
20
+ /**
21
+ * Runs of C0/C1 control characters, including DEL and any bare ESC left by a
22
+ * malformed sequence. `\r` and `\n` are C0, so this also flattens newlines.
23
+ */
24
+ const CONTROL_CHAR_RUN_RE = /[\u0000-\u001f\u007f-\u009f]+/g;
25
+
26
+ /**
27
+ * Remove ANSI CSI/OSC escape sequences, leaving other control characters
28
+ * (newlines, tabs) intact. For multi-line text whose line structure matters.
29
+ */
30
+ export function stripAnsiSequences(value: string): string {
31
+ return value.replace(ANSI_SEQUENCE_RE, "");
32
+ }
33
+
34
+ /**
35
+ * Remove ANSI CSI/OSC escape sequences, then replace every remaining run of
36
+ * C0/C1 control characters with `replacement` (removed by default). For
37
+ * single-line rendering where no control character may survive.
38
+ */
39
+ export function stripAnsiAndControlChars(
40
+ value: string,
41
+ replacement = "",
42
+ ): string {
43
+ return stripAnsiSequences(value).replace(CONTROL_CHAR_RUN_RE, replacement);
44
+ }
@@ -1,5 +1,6 @@
1
- import { spawnSync } from "node:child_process";
1
+ import { execFile } from "node:child_process";
2
2
  import { existsSync, statfsSync } from "node:fs";
3
+ import { promisify } from "node:util";
3
4
 
4
5
  import {
5
6
  getIsContainerized,
@@ -8,6 +9,8 @@ import {
8
9
  } from "../config/env-registry.js";
9
10
  import { getWorkspaceDir } from "./platform.js";
10
11
 
12
+ const execFileAsync = promisify(execFile);
13
+
11
14
  export interface DiskUsageInfo {
12
15
  path: string;
13
16
  totalMb: number;
@@ -19,21 +22,17 @@ export interface DiskUsageInfo {
19
22
  * Measure the on-disk usage of one or more directory paths using `du -sb`.
20
23
  * Returns the sum of all paths in bytes, or null on failure.
21
24
  */
22
- function getDirectorySizeBytes(paths: string[]): number | null {
25
+ async function getDirectorySizeBytes(paths: string[]): Promise<number | null> {
23
26
  try {
24
27
  const existing = paths.filter((p) => existsSync(p));
25
28
  if (existing.length === 0) {
26
29
  return null;
27
30
  }
28
- const result = spawnSync("du", ["-sb", ...existing], {
29
- encoding: "utf-8",
31
+ const { stdout } = await execFileAsync("du", ["-sb", ...existing], {
30
32
  timeout: 30_000,
31
33
  });
32
- if (result.status !== 0) {
33
- return null;
34
- }
35
34
  let total = 0;
36
- for (const line of result.stdout.trim().split("\n")) {
35
+ for (const line of stdout.trim().split("\n")) {
37
36
  const size = parseInt(line.split("\t")[0], 10);
38
37
  if (!isNaN(size) && size > 0) {
39
38
  total += size;
@@ -46,14 +45,19 @@ function getDirectorySizeBytes(paths: string[]): number | null {
46
45
  }
47
46
 
48
47
  const DU_CACHE_TTL_MS = 60_000;
49
- let duCacheValue: number | null = null;
48
+ // Caches the in-flight promise so concurrent callers share one `du` run.
49
+ let duCacheValue: Promise<number | null> | null = null;
50
50
  let duCacheTime = 0;
51
51
  let duCachePaths: string | null = null;
52
52
 
53
- function getCachedDirectorySizeBytes(paths: string[]): number | null {
53
+ function getCachedDirectorySizeBytes(paths: string[]): Promise<number | null> {
54
54
  const key = paths.join("\0");
55
55
  const now = Date.now();
56
- if (duCachePaths === key && now - duCacheTime < DU_CACHE_TTL_MS) {
56
+ if (
57
+ duCacheValue !== null &&
58
+ duCachePaths === key &&
59
+ now - duCacheTime < DU_CACHE_TTL_MS
60
+ ) {
57
61
  return duCacheValue;
58
62
  }
59
63
  duCacheValue = getDirectorySizeBytes(paths);
@@ -119,7 +123,7 @@ function classifyWorkspaceVolume(
119
123
  return { kind: "none" };
120
124
  }
121
125
 
122
- export function getDiskUsageInfo(): DiskUsageInfo | null {
126
+ export async function getDiskUsageInfo(): Promise<DiskUsageInfo | null> {
123
127
  try {
124
128
  const wsDir = getWorkspaceDir();
125
129
  const diskPath = existsSync(wsDir) ? wsDir : "/";
@@ -135,7 +139,7 @@ export function getDiskUsageInfo(): DiskUsageInfo | null {
135
139
  if (diskPath !== "/data" && existsSync("/data")) {
136
140
  volumePaths.push("/data");
137
141
  }
138
- const usedBytes = getCachedDirectorySizeBytes(volumePaths);
142
+ const usedBytes = await getCachedDirectorySizeBytes(volumePaths);
139
143
  if (usedBytes !== null) {
140
144
  const totalBytes =
141
145
  reporting.kind === "fixed-cap"
@@ -13,7 +13,6 @@
13
13
  * conversions of the same image (or daemon restarts) skip the sips call.
14
14
  */
15
15
 
16
- import { execFileSync } from "node:child_process";
17
16
  import { createHash } from "node:crypto";
18
17
  import {
19
18
  closeSync,
@@ -101,10 +100,10 @@ export interface ConvertToJpegOptions {
101
100
  quality?: number;
102
101
  }
103
102
 
104
- function runSips(
103
+ async function runSips(
105
104
  inputBytes: Uint8Array,
106
105
  options: ConvertToJpegOptions,
107
- ): Buffer | null {
106
+ ): Promise<Buffer | null> {
108
107
  const stamp = `${Date.now()}-${uuid().slice(0, 8)}`;
109
108
  const srcPath = join(tmpdir(), `vellum-img-opt-${stamp}-src`);
110
109
  const outPath = join(tmpdir(), `vellum-img-opt-${stamp}-out.jpg`);
@@ -133,7 +132,15 @@ function runSips(
133
132
  "--out",
134
133
  outPath,
135
134
  );
136
- execFileSync("sips", args, { stdio: "pipe", timeout: 15_000 });
135
+ const proc = Bun.spawn(["sips", ...args], {
136
+ stdout: "ignore",
137
+ stderr: "ignore",
138
+ timeout: 15_000,
139
+ });
140
+ await proc.exited;
141
+ if (proc.exitCode !== 0) {
142
+ return null;
143
+ }
137
144
  return readFileSync(outPath) as Buffer;
138
145
  } catch {
139
146
  return null;
@@ -155,10 +162,10 @@ function runSips(
155
162
  * Convert an image to JPEG, optionally downscaling. Returns null when
156
163
  * conversion is unavailable (non-macOS) or fails; callers keep the original.
157
164
  */
158
- export function convertImageToJpeg(
165
+ export async function convertImageToJpeg(
159
166
  bytes: Uint8Array,
160
167
  options: ConvertToJpegOptions = {},
161
- ): Buffer | null {
168
+ ): Promise<Buffer | null> {
162
169
  const hash = createHash("sha256").update(bytes).digest("hex").slice(0, 16);
163
170
  // Options qualify the key so full-resolution storage conversions and
164
171
  // resized transport conversions of the same source never collide.
@@ -173,7 +180,7 @@ export function convertImageToJpeg(
173
180
  return cached;
174
181
  }
175
182
 
176
- const converted = runSips(bytes, options);
183
+ const converted = await runSips(bytes, options);
177
184
  if (!converted) {
178
185
  return null;
179
186
  }
@@ -346,12 +353,12 @@ export interface NormalizedImageBytes {
346
353
  * {@link jpegFilenameFor} when `converted` is true — a MIME-only correction
347
354
  * sets `converted: false` and keeps the filename.
348
355
  */
349
- export function normalizeImageBytes(
356
+ export async function normalizeImageBytes(
350
357
  mimeType: string,
351
358
  bytes: Uint8Array,
352
- ): NormalizedImageBytes {
359
+ ): Promise<NormalizedImageBytes> {
353
360
  if (isHeifImage(bytes)) {
354
- const converted = convertImageToJpeg(bytes, {
361
+ const converted = await convertImageToJpeg(bytes, {
355
362
  quality: STORAGE_JPEG_QUALITY,
356
363
  });
357
364
  if (!converted) {
@@ -376,14 +383,14 @@ export interface NormalizedImageBase64 {
376
383
  * Base64 variant of {@link normalizeImageBytes}. Sniffs the decoded head
377
384
  * first so non-HEIF payloads skip the full decode.
378
385
  */
379
- export function normalizeImageBase64(
386
+ export async function normalizeImageBase64(
380
387
  mimeType: string,
381
388
  dataBase64: string,
382
- ): NormalizedImageBase64 {
389
+ ): Promise<NormalizedImageBase64> {
383
390
  // 16 base64 chars decode to the 12 bytes the ftyp/signature sniffs need.
384
391
  const head = Buffer.from(dataBase64.slice(0, 16), "base64");
385
392
  if (isHeifImage(head)) {
386
- const normalized = normalizeImageBytes(
393
+ const normalized = await normalizeImageBytes(
387
394
  mimeType,
388
395
  Buffer.from(dataBase64, "base64"),
389
396
  );
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Plugin-declared schedules record their provenance in the `source_key`
3
+ * column as `plugin:<pluginName>/<scheduleName>`. The format is minted by
4
+ * `pluginScheduleSourceKey` in schedule/plugin-schedule-declarations.ts;
5
+ * this leaf parser lets display surfaces (CLI tables, tool output) derive
6
+ * the owning plugin without importing the daemon's schedule modules.
7
+ */
8
+
9
+ const PLUGIN_SOURCE_KEY_RE = /^plugin:([^/]+)\//;
10
+
11
+ /**
12
+ * Display attribution for a schedule row: the owning plugin's name, the raw
13
+ * key when it does not match the format so provenance is never silently
14
+ * dropped, or null when the row is imperative.
15
+ */
16
+ export function describeScheduleSource(
17
+ sourceKey: string | null | undefined,
18
+ ): string | null {
19
+ if (!sourceKey) {
20
+ return null;
21
+ }
22
+ return PLUGIN_SOURCE_KEY_RE.exec(sourceKey)?.[1] ?? sourceKey;
23
+ }