@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
@@ -39,6 +39,7 @@ import { computeToolApprovalDigest } from "../security/tool-approval-digest.js";
39
39
  import { recordToolDenied, recordToolError } from "../telemetry/tool-audit.js";
40
40
  import { getLogger } from "../util/logger.js";
41
41
  import { resolveExecutionTarget } from "./execution-target.js";
42
+ import { safeTimeoutMs } from "./execution-timeout.js";
42
43
  import { channelCoordinatesFromToolContext } from "./policy-context.js";
43
44
  import { getAllTools, getTool, getToolOwner } from "./registry.js";
44
45
  import { isSideEffectTool } from "./side-effects.js";
@@ -127,9 +128,39 @@ export function buildInactiveToolMessage(args: {
127
128
 
128
129
  /** Default polling interval for inline grant wait (ms). */
129
130
  const TC_GRANT_WAIT_INTERVAL_MS = 500;
130
- /** Default maximum wait time for inline grant wait (ms). */
131
+ /**
132
+ * Fallback maximum wait for the inline grant wait (ms), used only when the
133
+ * deployed config cannot be read. The governing budget is
134
+ * `timeouts.permissionTimeoutSec` (see {@link resolveInlineGrantWaitMs}).
135
+ */
131
136
  export const TC_GRANT_WAIT_MAX_MS = 60_000;
132
137
 
138
+ /**
139
+ * Resolve the wait budget for an escalated tool grant.
140
+ *
141
+ * A guardian answering an escalated tool call is making the same decision as a
142
+ * local user answering a permission prompt, so both paths spend the same
143
+ * budget: `timeouts.permissionTimeoutSec` (read by `permissions/prompter.ts`).
144
+ * If anything, this path needs the larger share of it: the prompter's user
145
+ * already has the prompt on screen, while the guardian is notified
146
+ * out-of-band and has to context-switch before deciding.
147
+ *
148
+ * Falls back to {@link TC_GRANT_WAIT_MAX_MS} rather than the tool-execution
149
+ * default on a non-positive value, so a bad config can never collapse the
150
+ * window to zero and auto-deny every escalation.
151
+ *
152
+ * Exported because the grant resolver sizes its `inline_wait_active` staleness
153
+ * threshold off this same budget: if the two drift, an approval arriving while
154
+ * a waiter is still live gets misread as a dead waiter and the requester is
155
+ * told to retry a call that is about to resume on its own.
156
+ */
157
+ export function resolveInlineGrantWaitMs(): number {
158
+ return safeTimeoutMs(
159
+ getConfig().timeouts.permissionTimeoutSec,
160
+ TC_GRANT_WAIT_MAX_MS,
161
+ );
162
+ }
163
+
133
164
  /**
134
165
  * Inline wait result for trusted-contact grant polling.
135
166
  * - `granted`: a grant was minted and consumed within the wait window.
@@ -152,13 +183,16 @@ export type InlineGrantWaitOutcome =
152
183
  * and atomically consume the grant).
153
184
  *
154
185
  * Only called for trusted_contact actors with valid guardian bindings.
186
+ *
187
+ * `options.maxWaitMs` overrides the wait budget; omitting it spends the
188
+ * configured one from {@link resolveInlineGrantWaitMs}.
155
189
  */
156
190
  export async function waitForInlineGrant(
157
191
  escalationRequestId: string,
158
192
  consumeParams: Parameters<typeof consumeGrantForInvocation>[0],
159
193
  options?: { maxWaitMs?: number; intervalMs?: number; signal?: AbortSignal },
160
194
  ): Promise<InlineGrantWaitOutcome> {
161
- const maxWait = options?.maxWaitMs ?? TC_GRANT_WAIT_MAX_MS;
195
+ const maxWait = options?.maxWaitMs ?? resolveInlineGrantWaitMs();
162
196
  const interval = options?.intervalMs ?? TC_GRANT_WAIT_INTERVAL_MS;
163
197
  const signal = options?.signal;
164
198
  const deadline = Date.now() + maxWait;
@@ -592,9 +626,16 @@ export type PreExecutionGateResult =
592
626
  }
593
627
  | { allowed: false; result: ToolExecutionResult };
594
628
 
595
- /** Configuration for the inline grant wait behavior. */
629
+ /**
630
+ * Overrides for the inline grant wait behavior. Production leaves this empty
631
+ * so the wait spends the configured budget; tests inject short waits to keep
632
+ * escalation cases fast.
633
+ */
596
634
  export interface InlineGrantWaitConfig {
597
- /** Maximum time to wait for guardian approval (ms). Defaults to TC_GRANT_WAIT_MAX_MS. */
635
+ /**
636
+ * Maximum time to wait for guardian approval (ms). Defaults to the budget
637
+ * from {@link resolveInlineGrantWaitMs}.
638
+ */
598
639
  maxWaitMs?: number;
599
640
  /** Polling interval during the wait (ms). Defaults to TC_GRANT_WAIT_INTERVAL_MS. */
600
641
  intervalMs?: number;
@@ -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"