@vellumai/assistant 0.11.2 → 0.11.3-staging.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (720) hide show
  1. package/ARCHITECTURE.md +58 -3
  2. package/docs/trusted-contact-access.md +9 -5
  3. package/docs/vellum-doctor.md +195 -0
  4. package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/channels.ts +39 -0
  5. package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/ingress.ts +10 -0
  6. package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/remote-web-pairing.ts +6 -0
  7. package/node_modules/@vellumai/ces-client/src/index.ts +1 -0
  8. package/node_modules/@vellumai/ces-client/src/rpc-client.ts +19 -2
  9. package/node_modules/@vellumai/environments/package.json +2 -1
  10. package/node_modules/@vellumai/environments/src/__tests__/cloud-assistant-hub-url.test.ts +38 -0
  11. package/node_modules/@vellumai/environments/src/__tests__/install-layout.test.ts +98 -0
  12. package/node_modules/@vellumai/environments/src/__tests__/package-boundary.test.ts +5 -5
  13. package/node_modules/@vellumai/environments/src/index.ts +17 -6
  14. package/node_modules/@vellumai/environments/src/install-layout.ts +49 -0
  15. package/node_modules/@vellumai/environments/src/seeds.ts +29 -0
  16. package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/channels.ts +39 -0
  17. package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/ingress.ts +10 -0
  18. package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/remote-web-pairing.ts +6 -0
  19. package/node_modules/@vellumai/gateway-client/src/index.ts +4 -0
  20. package/node_modules/@vellumai/gateway-client/src/verification-session-contract.ts +91 -6
  21. package/node_modules/@vellumai/ipc-server-utils/src/endpoint.test.ts +36 -0
  22. package/node_modules/@vellumai/ipc-server-utils/src/endpoint.ts +142 -0
  23. package/node_modules/@vellumai/ipc-server-utils/src/index.ts +2 -0
  24. package/node_modules/@vellumai/ipc-server-utils/src/listen-options.ts +3 -0
  25. package/node_modules/@vellumai/ipc-server-utils/src/socket-watchdog.test.ts +15 -1
  26. package/node_modules/@vellumai/ipc-server-utils/src/socket-watchdog.ts +17 -2
  27. package/node_modules/@vellumai/service-contracts/src/channels.ts +39 -0
  28. package/node_modules/@vellumai/service-contracts/src/ingress.ts +10 -0
  29. package/node_modules/@vellumai/service-contracts/src/remote-web-pairing.ts +6 -0
  30. package/node_modules/@vellumai/slack-text/src/index.ts +16 -2
  31. package/openapi.yaml +589 -30
  32. package/package.json +1 -1
  33. package/src/__tests__/agent-image-optimize.test.ts +4 -4
  34. package/src/__tests__/agent-loop-mutable-latest-user-message.test.ts +113 -66
  35. package/src/__tests__/agent-loop-override-profile.test.ts +38 -48
  36. package/src/__tests__/agent-loop-subagent-billing-headers.test.ts +166 -0
  37. package/src/__tests__/agent-wake-disk-pressure-callsite.test.ts +1 -0
  38. package/src/__tests__/agent-wake-override-profile.test.ts +1 -0
  39. package/src/__tests__/answered-question-persistence.test.ts +240 -0
  40. package/src/__tests__/anthropic-provider.test.ts +165 -30
  41. package/src/__tests__/app-control-flow.test.ts +9 -8
  42. package/src/__tests__/assistant-event-hub.test.ts +48 -0
  43. package/src/__tests__/attachments-store-heic-normalize.test.ts +16 -12
  44. package/src/__tests__/attachments-store.test.ts +130 -68
  45. package/src/__tests__/attachments.test.ts +36 -36
  46. package/src/__tests__/background-workers-disk-pressure.test.ts +1 -1
  47. package/src/__tests__/call-start-guardian-guard.test.ts +18 -4
  48. package/src/__tests__/canned-reply-release.test.ts +86 -0
  49. package/src/__tests__/channel-guardian.test.ts +59 -29
  50. package/src/__tests__/channel-readiness-telegram-remote.test.ts +154 -0
  51. package/src/__tests__/channel-setup-panel-ack.test.ts +6 -5
  52. package/src/__tests__/client-os-metadata-persistence.test.ts +103 -4
  53. package/src/__tests__/compactor-image-manifest-trust.test.ts +1 -1
  54. package/src/__tests__/consult-deadline.test.ts +65 -0
  55. package/src/__tests__/conversation-agent-loop.test.ts +438 -2
  56. package/src/__tests__/conversation-disk-view-integration.test.ts +5 -1
  57. package/src/__tests__/conversation-disk-view.test.ts +4 -4
  58. package/src/__tests__/conversation-error.test.ts +22 -3
  59. package/src/__tests__/conversation-fork-crud.test.ts +5 -1
  60. package/src/__tests__/conversation-fork-referential.test.ts +352 -0
  61. package/src/__tests__/conversation-fork-retrospective.test.ts +5 -1
  62. package/src/__tests__/conversation-group-tools.test.ts +32 -2
  63. package/src/__tests__/conversation-inference-profile-route.test.ts +27 -1
  64. package/src/__tests__/conversation-initial-prompt.test.ts +51 -22
  65. package/src/__tests__/conversation-key-store-bootstrap-cleanup.test.ts +119 -0
  66. package/src/__tests__/conversation-key-store-origin.test.ts +105 -0
  67. package/src/__tests__/conversation-list-recency-ordering.test.ts +104 -0
  68. package/src/__tests__/conversation-origin-at-creation.test.ts +153 -0
  69. package/src/__tests__/conversation-origin-channel-filter.test.ts +95 -0
  70. package/src/__tests__/conversation-placement-promotion.test.ts +172 -0
  71. package/src/__tests__/conversation-process-app-control-preactivation.test.ts +123 -0
  72. package/src/__tests__/conversation-queue.test.ts +433 -1
  73. package/src/__tests__/conversation-retry-route.test.ts +3 -1
  74. package/src/__tests__/conversation-routes-hidden-queue.test.ts +64 -1
  75. package/src/__tests__/conversation-store-ephemeral.test.ts +4 -0
  76. package/src/__tests__/conversation-store.test.ts +26 -10
  77. package/src/__tests__/conversation-summarize-route.test.ts +1 -1
  78. package/src/__tests__/conversation-surfaces-action-delivery.test.ts +15 -6
  79. package/src/__tests__/conversation-surfaces-activation-emit.test.ts +6 -5
  80. package/src/__tests__/conversation-surfaces-app-control.test.ts +12 -11
  81. package/src/__tests__/conversation-surfaces-app-open.test.ts +11 -5
  82. package/src/__tests__/conversation-surfaces-data-persist.test.ts +5 -4
  83. package/src/__tests__/conversation-surfaces-history-restored-completion.test.ts +967 -0
  84. package/src/__tests__/conversation-surfaces-persisted-info.test.ts +312 -0
  85. package/src/__tests__/conversation-surfaces-queued-emit.test.ts +6 -5
  86. package/src/__tests__/conversation-surfaces-standalone-payloads.test.ts +12 -8
  87. package/src/__tests__/conversation-surfaces-standalone.test.ts +21 -11
  88. package/src/__tests__/conversation-surfaces-state-update.test.ts +7 -11
  89. package/src/__tests__/conversation-surfaces-table-action.test.ts +7 -6
  90. package/src/__tests__/conversation-surfaces-task-progress.test.ts +11 -6
  91. package/src/__tests__/conversation-tool-setup-app-refresh.test.ts +9 -6
  92. package/src/__tests__/conversation-tool-setup-attribution.test.ts +70 -7
  93. package/src/__tests__/conversation-wait-for-idle.test.ts +6 -11
  94. package/src/__tests__/credential-prompt-route.test.ts +57 -7
  95. package/src/__tests__/credential-security-invariants.test.ts +1 -0
  96. package/src/__tests__/cu-unified-flow.test.ts +89 -28
  97. package/src/__tests__/custom-group-listing-visibility.test.ts +1 -1
  98. package/src/__tests__/db-schedule-syntax-migration.test.ts +4 -1
  99. package/src/__tests__/discord-access-request-privacy.test.ts +366 -0
  100. package/src/__tests__/discord-requester-notice-privacy.test.ts +87 -0
  101. package/src/__tests__/disk-pressure-guard.test.ts +55 -55
  102. package/src/__tests__/disk-pressure-routes.test.ts +10 -10
  103. package/src/__tests__/disk-pressure-tools.test.ts +2 -2
  104. package/src/__tests__/disk-usage.test.ts +35 -20
  105. package/src/__tests__/document-sync-tags.test.ts +379 -0
  106. package/src/__tests__/document-update-default-surface.test.ts +28 -0
  107. package/src/__tests__/dynamic-skill-background-guard.test.ts +2 -2
  108. package/src/__tests__/emit-signal-routing-intent.test.ts +3 -4
  109. package/src/__tests__/ensure-conversation-exists.test.ts +8 -5
  110. package/src/__tests__/events-dev-bypass-actor.test.ts +183 -1
  111. package/src/__tests__/evict-conversations-for-reload.test.ts +90 -0
  112. package/src/__tests__/file-list-tool.test.ts +12 -10
  113. package/src/__tests__/file-ops-service.test.ts +151 -44
  114. package/src/__tests__/file-read-container-boundary.test.ts +137 -0
  115. package/src/__tests__/file-read-tool.test.ts +21 -9
  116. package/src/__tests__/file-url-path-guard.test.ts +83 -0
  117. package/src/__tests__/filesystem-tools.test.ts +82 -66
  118. package/src/__tests__/guardian-outbound-http.test.ts +8 -8
  119. package/src/__tests__/helpers/gateway-verification-sessions-stub.ts +14 -1
  120. package/src/__tests__/helpers/hub-clients.ts +42 -0
  121. package/src/__tests__/helpers/mock-conversation.ts +46 -0
  122. package/src/__tests__/helpers/verification-sessions-ipc-sim.ts +61 -57
  123. package/src/__tests__/history-repair.test.ts +112 -12
  124. package/src/__tests__/host-bash-proxy.test.ts +10 -5
  125. package/src/__tests__/host-bash-routes.test.ts +67 -0
  126. package/src/__tests__/host-browser-proxy.test.ts +2 -2
  127. package/src/__tests__/host-cu-proxy.test.ts +3 -7
  128. package/src/__tests__/host-file-proxy.test.ts +3 -7
  129. package/src/__tests__/host-file-read-tool.test.ts +5 -1
  130. package/src/__tests__/host-transfer-proxy-targeted.test.ts +6 -4
  131. package/src/__tests__/http-conversation-lineage.test.ts +2 -5
  132. package/src/__tests__/http-user-message-parity.test.ts +44 -0
  133. package/src/__tests__/identity-routes.test.ts +13 -13
  134. package/src/__tests__/image-conversion.test.ts +29 -26
  135. package/src/__tests__/image-source-path-reinject.test.ts +2 -2
  136. package/src/__tests__/inference-profile-session-handler.test.ts +95 -0
  137. package/src/__tests__/inference-profile-session-ipc.test.ts +25 -0
  138. package/src/__tests__/introduction-card-resolver.test.ts +6 -1
  139. package/src/__tests__/list-messages-attachments.test.ts +56 -32
  140. package/src/__tests__/list-messages-background-tool-completion.test.ts +4 -4
  141. package/src/__tests__/list-messages-client-message-id.test.ts +2 -2
  142. package/src/__tests__/list-messages-hidden-metadata.test.ts +16 -16
  143. package/src/__tests__/list-messages-page-latest.test.ts +61 -61
  144. package/src/__tests__/list-messages-provider-error.test.ts +6 -6
  145. package/src/__tests__/list-messages-queued.test.ts +49 -6
  146. package/src/__tests__/list-messages-system-card.test.ts +4 -4
  147. package/src/__tests__/list-messages-tool-merge.test.ts +9 -9
  148. package/src/__tests__/llm-resolver-override-or-default.test.ts +141 -0
  149. package/src/__tests__/llm-usage-store.test.ts +55 -0
  150. package/src/__tests__/manual-token-reconciliation.test.ts +45 -0
  151. package/src/__tests__/mcp-tool-annotations-risk.test.ts +133 -0
  152. package/src/__tests__/media-stream-server-integration.test.ts +36 -0
  153. package/src/__tests__/migration-export-http.test.ts +1 -1
  154. package/src/__tests__/migration-import-commit-http.test.ts +1 -1
  155. package/src/__tests__/migration-import-preflight-http.test.ts +1 -1
  156. package/src/__tests__/migration-validate-http.test.ts +1 -1
  157. package/src/__tests__/mtime-cache.test.ts +65 -3
  158. package/src/__tests__/notification-platform-adapter.test.ts +104 -0
  159. package/src/__tests__/notification-schedule-notify-dedup.test.ts +1 -1
  160. package/src/__tests__/oauth-provider-profiles.test.ts +4 -2
  161. package/src/__tests__/oauth-provider-seed-logos.test.ts +65 -0
  162. package/src/__tests__/oauth-provider-serializer.test.ts +17 -0
  163. package/src/__tests__/oauth-providers-routes.test.ts +1 -0
  164. package/src/__tests__/openai-responses-prompt-cache.test.ts +5 -2
  165. package/src/__tests__/path-policy.test.ts +164 -1
  166. package/src/__tests__/persist-media-references.test.ts +2 -2
  167. package/src/__tests__/persist-unsendable-image-downscale.test.ts +5 -5
  168. package/src/__tests__/persist-unsendable-image.test.ts +23 -23
  169. package/src/__tests__/persona-resolver.test.ts +154 -0
  170. package/src/__tests__/plugin-api-webhook-url.test.ts +170 -0
  171. package/src/__tests__/plugin-import-boundary-guard.test.ts +6 -0
  172. package/src/__tests__/processing-flag-persist-failure.test.ts +15 -10
  173. package/src/__tests__/profile-availability-incomplete.test.ts +113 -0
  174. package/src/__tests__/profiler-routes.test.ts +13 -13
  175. package/src/__tests__/prompt-cache-cross-turn-stability.test.ts +593 -0
  176. package/src/__tests__/proxy-approval-callback.test.ts +3 -3
  177. package/src/__tests__/queued-message-cancel-and-steer.test.ts +189 -0
  178. package/src/__tests__/queued-message-steer-actor-scoping.test.ts +164 -0
  179. package/src/__tests__/resolve-trust-class.test.ts +20 -0
  180. package/src/__tests__/run-conversation-turn-persistence.test.ts +1 -1
  181. package/src/__tests__/run-due-schedules.test.ts +174 -0
  182. package/src/__tests__/runtime-attachment-metadata.test.ts +15 -7
  183. package/src/__tests__/scaffold-managed-skill-tool.test.ts +166 -0
  184. package/src/__tests__/schedule-routes.test.ts +730 -8
  185. package/src/__tests__/schedule-store.test.ts +540 -0
  186. package/src/__tests__/schedule-tools.test.ts +103 -0
  187. package/src/__tests__/scheduler-wake.test.ts +5 -0
  188. package/src/__tests__/secret-routes-platform-proxy.test.ts +29 -1
  189. package/src/__tests__/secure-keys.test.ts +91 -0
  190. package/src/__tests__/server-history-render.test.ts +97 -7
  191. package/src/__tests__/size-guard.test.ts +8 -8
  192. package/src/__tests__/skill-docs-sync-guard.test.ts +3 -2
  193. package/src/__tests__/skill-tool-factory.test.ts +247 -0
  194. package/src/__tests__/skills-install-staging.test.ts +15 -6
  195. package/src/__tests__/slack-edit-ordering-characterization.test.ts +103 -0
  196. package/src/__tests__/slack-inbound-verification.test.ts +4 -1
  197. package/src/__tests__/slack-mention-binding-invariance.test.ts +103 -0
  198. package/src/__tests__/slack-mention-provider-leak-audit.test.ts +135 -0
  199. package/src/__tests__/starter-task-flow.test.ts +5 -4
  200. package/src/__tests__/steer-on-enqueue-question.test.ts +3 -2
  201. package/src/__tests__/stt-language-catalog-parity.test.ts +38 -0
  202. package/src/__tests__/subagent-allowlist-validation.test.ts +26 -4
  203. package/src/__tests__/subagent-cron-run-attribution.test.ts +301 -0
  204. package/src/__tests__/subagent-disposal.test.ts +10 -0
  205. package/src/__tests__/subagent-fork-notifications.test.ts +10 -0
  206. package/src/__tests__/subagent-fork-prompt-role.test.ts +85 -11
  207. package/src/__tests__/subagent-fork-spawn.test.ts +15 -7
  208. package/src/__tests__/subagent-manager-notify.test.ts +148 -0
  209. package/src/__tests__/subagent-notify-parent.test.ts +1 -1
  210. package/src/__tests__/subagent-persistence.test.ts +141 -0
  211. package/src/__tests__/subagent-role-registry.test.ts +172 -63
  212. package/src/__tests__/subagent-role-resolution.test.ts +88 -0
  213. package/src/__tests__/subagent-spawn-and-await.test.ts +48 -0
  214. package/src/__tests__/subagent-spawn-tool-fork.test.ts +111 -10
  215. package/src/__tests__/subagent-terminal-message.test.ts +88 -1
  216. package/src/__tests__/subagent-tool-gate-mode.test.ts +225 -10
  217. package/src/__tests__/subagent-tools.test.ts +1901 -108
  218. package/src/__tests__/surface-completion-compaction-boundary.test.ts +124 -0
  219. package/src/__tests__/surface-completion-in-flight-snapshot.test.ts +298 -0
  220. package/src/__tests__/system-prompt.test.ts +82 -7
  221. package/src/__tests__/tool-grant-request-escalation.test.ts +40 -1
  222. package/src/__tests__/tool-result-spool.test.ts +74 -18
  223. package/src/__tests__/tool-side-effects-documents.test.ts +174 -0
  224. package/src/__tests__/tool-side-effects-hook-failures.test.ts +129 -0
  225. package/src/__tests__/tools-audio-read.test.ts +10 -10
  226. package/src/__tests__/tools-get-route.test.ts +85 -12
  227. package/src/__tests__/tools-list-cli.test.ts +171 -0
  228. package/src/__tests__/top-level-renderer.test.ts +54 -1
  229. package/src/__tests__/trusted-contact-inline-approval-integration.test.ts +68 -1
  230. package/src/__tests__/trusted-contact-multichannel.test.ts +4 -1
  231. package/src/__tests__/turn-tail-chain.test.ts +135 -0
  232. package/src/__tests__/ui-choice-copy-surfaces.test.ts +6 -4
  233. package/src/__tests__/ui-visual-surface.test.ts +5 -4
  234. package/src/__tests__/ui-work-result-surface.test.ts +5 -4
  235. package/src/__tests__/unified-turn-context-visible-app.test.ts +7 -8
  236. package/src/__tests__/usage-routes.test.ts +62 -0
  237. package/src/__tests__/verification-outbound-delivery.test.ts +176 -0
  238. package/src/__tests__/visible-app-context.test.ts +20 -4
  239. package/src/__tests__/web-search-history.test.ts +96 -0
  240. package/src/__tests__/workspace-migration-139-clear-renamed-cost-profile-label.test.ts +6 -6
  241. package/src/__tests__/workspace-migration-140-repair-seed-pinned-memory-v3-live.test.ts +330 -0
  242. package/src/acp/failure-error.ts +3 -5
  243. package/src/agent/attachments.ts +26 -24
  244. package/src/agent/history-repair/history-repair.ts +27 -26
  245. package/src/agent/image-optimize.ts +6 -6
  246. package/src/agent/loop.ts +40 -22
  247. package/src/agent/message-types.ts +3 -3
  248. package/src/api/constants/document-tools.ts +55 -0
  249. package/src/api/events/question-answered.ts +53 -0
  250. package/src/api/events/tool-result.ts +7 -0
  251. package/src/api/index.ts +13 -0
  252. package/src/api/responses/conversation-message.ts +11 -0
  253. package/src/api/surfaces-oauth-connect.test.ts +96 -0
  254. package/src/api/surfaces.ts +36 -8
  255. package/src/approvals/guardian-channel-delivery.ts +83 -0
  256. package/src/approvals/guardian-expiry-notifier.ts +13 -11
  257. package/src/approvals/guardian-request-resolvers.ts +222 -135
  258. package/src/calls/__tests__/voice-session-bridge.test.ts +50 -0
  259. package/src/calls/media-stream-server.ts +9 -1
  260. package/src/calls/voice-session-bridge.ts +38 -0
  261. package/src/channels/__tests__/gateway-verification-sessions.test.ts +3 -3
  262. package/src/channels/config.ts +7 -2
  263. package/src/channels/gateway-verification-sessions.ts +7 -2
  264. package/src/channels/types.ts +7 -1
  265. package/src/cli/commands/__tests__/cache.test.ts +7 -4
  266. package/src/cli/commands/__tests__/cli-test-harness.ts +82 -0
  267. package/src/cli/commands/__tests__/conversations-wake.test.ts +51 -6
  268. package/src/cli/commands/__tests__/inference-models.test.ts +14 -0
  269. package/src/cli/commands/__tests__/inference-profiles.test.ts +44 -1
  270. package/src/cli/commands/__tests__/inference-providers.test.ts +7 -0
  271. package/src/cli/commands/__tests__/plugins.test.ts +692 -0
  272. package/src/cli/commands/__tests__/schedules.test.ts +136 -37
  273. package/src/cli/commands/conversations.ts +7 -3
  274. package/src/cli/commands/credentials.help.ts +7 -0
  275. package/src/cli/commands/inference-profiles.ts +9 -3
  276. package/src/cli/commands/inference.help.ts +16 -2
  277. package/src/cli/commands/mcp.help.ts +4 -2
  278. package/src/cli/commands/memory/memory-retrospective.ts +18 -0
  279. package/src/cli/commands/notifications.ts +3 -6
  280. package/src/cli/commands/oauth/connect-surface-guidance.test.ts +16 -0
  281. package/src/cli/commands/oauth/connect-surface-guidance.ts +25 -7
  282. package/src/cli/commands/oauth/connect.test.ts +26 -1
  283. package/src/cli/commands/oauth/connect.ts +4 -1
  284. package/src/cli/commands/oauth/index.help.ts +3 -1
  285. package/src/cli/commands/oauth/providers.ts +3 -0
  286. package/src/cli/commands/plugins.help.ts +25 -2
  287. package/src/cli/commands/plugins.ts +289 -10
  288. package/src/cli/commands/schedules.help.ts +14 -12
  289. package/src/cli/commands/schedules.ts +19 -5
  290. package/src/cli/commands/tools.help.ts +4 -2
  291. package/src/cli/commands/tools.ts +38 -0
  292. package/src/cli/lib/__tests__/inspect-plugin.test.ts +1 -0
  293. package/src/cli/lib/__tests__/install-from-github.test.ts +79 -0
  294. package/src/cli/lib/__tests__/install-from-platform.test.ts +47 -1
  295. package/src/cli/lib/__tests__/plugin-surfaces.test.ts +86 -7
  296. package/src/cli/lib/__tests__/upgrade-plugin.test.ts +122 -0
  297. package/src/cli/lib/install-from-github.ts +61 -0
  298. package/src/cli/lib/install-from-platform.ts +13 -2
  299. package/src/cli/lib/plugin-surfaces.ts +124 -4
  300. package/src/cli/lib/upgrade-plugin.ts +18 -0
  301. package/src/cli.ts +1 -1
  302. package/src/config/__tests__/deployment-context-defaults.test.ts +73 -0
  303. package/src/config/__tests__/memory-retrospective-schema.test.ts +40 -0
  304. package/src/config/__tests__/plugin-updates-schema.test.ts +43 -0
  305. package/src/config/__tests__/profile-tool-support.test.ts +131 -0
  306. package/src/config/bundled-skills/AGENTS.md +1 -1
  307. package/src/config/bundled-skills/app-builder/SKILL.md +1 -1
  308. package/src/config/bundled-skills/app-builder/references/RESPONSIVE.md +3 -3
  309. package/src/config/bundled-skills/schedule/SKILL.md +5 -1
  310. package/src/config/bundled-skills/schedule/TOOLS.json +2 -2
  311. package/src/config/bundled-skills/settings/TOOLS.json +2 -2
  312. package/src/config/bundled-skills/settings/tools/voice-config-update.ts +1 -0
  313. package/src/config/bundled-skills/subagent/SKILL.md +53 -22
  314. package/src/config/bundled-skills/subagent/TOOLS.json +14 -13
  315. package/src/config/bundled-skills/visualize/SKILL.md +1 -1
  316. package/src/config/default-profile-catalog.ts +6 -2
  317. package/src/config/feature-flag-registry.json +25 -17
  318. package/src/config/llm-resolver.ts +45 -15
  319. package/src/config/loader.ts +70 -1
  320. package/src/config/profile-tool-support.ts +110 -0
  321. package/src/config/sanitize-for-transfer.ts +11 -0
  322. package/src/config/schema.ts +4 -0
  323. package/src/config/schemas/__tests__/stt.test.ts +20 -5
  324. package/src/config/schemas/channels.ts +13 -0
  325. package/src/config/schemas/memory-retrospective.ts +25 -0
  326. package/src/config/schemas/plugin-updates.ts +55 -0
  327. package/src/config/schemas/services.ts +8 -4
  328. package/src/config/schemas/stt.ts +17 -5
  329. package/src/config/schemas/timeouts.ts +1 -1
  330. package/src/contacts/__tests__/guardian-delivery-reader.test.ts +16 -0
  331. package/src/contacts/guardian-delivery-reader.ts +29 -2
  332. package/src/context/compactor.ts +4 -4
  333. package/src/context/post-turn-tool-result-truncation.ts +32 -16
  334. package/src/context/strip-injections.ts +42 -0
  335. package/src/context/tool-result-spool.ts +32 -18
  336. package/src/credential-execution/ces-runtime.ts +3 -0
  337. package/src/credential-execution/client.ts +22 -1
  338. package/src/credential-execution/process-manager.test.ts +99 -0
  339. package/src/credential-execution/process-manager.ts +62 -16
  340. package/src/daemon/__tests__/abort-null-controller.test.ts +141 -5
  341. package/src/daemon/__tests__/conversation-surfaces-launch.test.ts +8 -12
  342. package/src/daemon/__tests__/install-assistant-command.test.ts +234 -0
  343. package/src/daemon/__tests__/turn-tail-assistant-reply-notify.test.ts +2 -4
  344. package/src/daemon/__tests__/turn-tail-deleted-conversation.test.ts +20 -29
  345. package/src/daemon/channel-ui-capability.ts +4 -4
  346. package/src/daemon/conversation-agent-loop-handlers.ts +117 -61
  347. package/src/daemon/conversation-agent-loop.ts +318 -148
  348. package/src/daemon/conversation-attachments.ts +1 -1
  349. package/src/daemon/conversation-error.ts +8 -1
  350. package/src/daemon/conversation-initial-prompt.ts +16 -38
  351. package/src/daemon/conversation-lifecycle.ts +154 -32
  352. package/src/daemon/conversation-messaging.ts +106 -43
  353. package/src/daemon/conversation-process.ts +131 -33
  354. package/src/daemon/conversation-queue-manager.ts +10 -0
  355. package/src/daemon/conversation-runtime-assembly.ts +9 -4
  356. package/src/daemon/conversation-store.ts +52 -3
  357. package/src/daemon/conversation-surface-state.ts +9 -0
  358. package/src/daemon/conversation-surfaces.ts +403 -210
  359. package/src/daemon/conversation-tool-setup.ts +56 -7
  360. package/src/daemon/conversation-turn-finalize.ts +134 -89
  361. package/src/daemon/conversation.ts +111 -27
  362. package/src/daemon/disk-pressure-guard-lifecycle.ts +2 -2
  363. package/src/daemon/disk-pressure-guard.ts +3 -3
  364. package/src/daemon/doordash-steps.ts +5 -5
  365. package/src/daemon/external-plugins-bootstrap.ts +7 -1
  366. package/src/daemon/handlers/config-channels.ts +48 -5
  367. package/src/daemon/handlers/conversation-history.ts +1 -1
  368. package/src/daemon/handlers/conversations.ts +62 -17
  369. package/src/daemon/handlers/shared.ts +12 -0
  370. package/src/daemon/handlers/skills.ts +11 -1
  371. package/src/daemon/identity-helpers.ts +38 -10
  372. package/src/daemon/install-assistant-command.ts +322 -0
  373. package/src/daemon/lifecycle.ts +14 -3
  374. package/src/daemon/message-types/conversations.ts +6 -7
  375. package/src/daemon/message-types/sync.ts +1 -0
  376. package/src/daemon/persist-media-references.ts +23 -14
  377. package/src/daemon/process-message.ts +6 -6
  378. package/src/daemon/tool-setup-types.ts +19 -122
  379. package/src/daemon/tool-side-effects.ts +17 -94
  380. package/src/daemon/trust-context-types.ts +39 -0
  381. package/src/daemon/trust-context.ts +14 -6
  382. package/src/daemon/turn-tail-chain.ts +73 -0
  383. package/src/daemon/web-search-history.ts +47 -33
  384. package/src/heartbeat/heartbeat-service.ts +6 -6
  385. package/src/ipc/__tests__/socket-path.test.ts +21 -0
  386. package/src/ipc/assistant-server.ts +17 -10
  387. package/src/ipc/cli-client.ts +10 -1
  388. package/src/ipc/routes/__tests__/documents-sync-ipc-routes.test.ts +56 -0
  389. package/src/ipc/routes/documents-sync-ipc-routes.ts +42 -0
  390. package/src/ipc/socket-cleanup.ts +9 -7
  391. package/src/ipc/socket-path.ts +4 -96
  392. package/src/live-voice/__tests__/live-voice-archive.test.ts +16 -16
  393. package/src/live-voice/__tests__/live-voice-attach-image.test.ts +209 -0
  394. package/src/live-voice/__tests__/live-voice-connection.test.ts +20 -0
  395. package/src/live-voice/__tests__/live-voice-integration.test.ts +7 -5
  396. package/src/live-voice/__tests__/live-voice-session-telemetry.test.ts +265 -0
  397. package/src/live-voice/__tests__/live-voice-stt.test.ts +6 -1
  398. package/src/live-voice/__tests__/live-voice-vad.test.ts +35 -15
  399. package/src/live-voice/__tests__/protocol.test.ts +52 -0
  400. package/src/live-voice/__tests__/runtime-websocket-shell.test.ts +3 -0
  401. package/src/live-voice/live-voice-archive.ts +13 -13
  402. package/src/live-voice/live-voice-connection.ts +7 -1
  403. package/src/live-voice/live-voice-photo.ts +169 -0
  404. package/src/live-voice/live-voice-session.ts +108 -4
  405. package/src/live-voice/protocol.ts +89 -1
  406. package/src/mcp/client.ts +10 -0
  407. package/src/messaging/providers/__tests__/callback-routing.test.ts +5 -1
  408. package/src/messaging/providers/__tests__/transport-dispatch.test.ts +66 -4
  409. package/src/messaging/providers/callback-routing.ts +1 -0
  410. package/src/messaging/providers/discord/api.ts +312 -0
  411. package/src/messaging/providers/discord/dm-delivery.test.ts +204 -0
  412. package/src/messaging/providers/discord/render.test.ts +204 -0
  413. package/src/messaging/providers/discord/render.ts +195 -0
  414. package/src/messaging/providers/discord/send.test.ts +179 -0
  415. package/src/messaging/providers/discord/send.ts +212 -0
  416. package/src/messaging/providers/discord/transport.ts +81 -0
  417. package/src/messaging/providers/index.ts +3 -1
  418. package/src/messaging/providers/retry-policy.test.ts +247 -0
  419. package/src/messaging/providers/retry-policy.ts +206 -0
  420. package/src/messaging/providers/slack/mention-source.test.ts +285 -0
  421. package/src/messaging/providers/slack/mention-source.ts +287 -0
  422. package/src/messaging/providers/telegram-bot/api.ts +47 -130
  423. package/src/messaging/providers/whatsapp/api.ts +47 -136
  424. package/src/monitoring/__tests__/db-integrity-sample.test.ts +4 -1
  425. package/src/monitoring/__tests__/plugin-auto-update.test.ts +332 -0
  426. package/src/monitoring/db-integrity-sample.ts +6 -1
  427. package/src/monitoring/plugin-auto-update.ts +359 -0
  428. package/src/monitoring/resource-sampler.ts +21 -5
  429. package/src/monitoring/worker.ts +13 -0
  430. package/src/notifications/__tests__/assistant-reply-producer.test.ts +375 -2
  431. package/src/notifications/__tests__/broadcaster.test.ts +37 -1
  432. package/src/notifications/__tests__/copy-composer.test.ts +165 -0
  433. package/src/notifications/__tests__/emit-signal-pipeline-failure.test.ts +205 -0
  434. package/src/notifications/__tests__/notification-utils.test.ts +249 -0
  435. package/src/notifications/adapters/platform.ts +31 -2
  436. package/src/notifications/assistant-reply-producer.ts +79 -32
  437. package/src/notifications/broadcaster.ts +30 -10
  438. package/src/notifications/copy-composer.ts +69 -0
  439. package/src/notifications/emit-signal.ts +82 -15
  440. package/src/notifications/events-store.ts +10 -2
  441. package/src/notifications/home-feed-side-effect.ts +6 -3
  442. package/src/notifications/notification-utils.ts +173 -5
  443. package/src/notifications/signal.ts +12 -0
  444. package/src/oauth/AGENTS.md +4 -2
  445. package/src/oauth/byo-connection.ts +6 -1
  446. package/src/oauth/credential-token-resolver.ts +3 -10
  447. package/src/oauth/manual-token-connection.ts +29 -64
  448. package/src/oauth/manual-token-providers.ts +62 -0
  449. package/src/oauth/provider-serializer.ts +13 -0
  450. package/src/oauth/seed-providers.ts +62 -11
  451. package/src/onboarding/onboarding-events-store.ts +91 -1
  452. package/src/permissions/question-prompter.test.ts +4 -0
  453. package/src/permissions/question-prompter.ts +21 -4
  454. package/src/persistence/__tests__/conversation-lineage.test.ts +248 -0
  455. package/src/persistence/attachments-store.ts +15 -15
  456. package/src/persistence/conversation-attention-store.ts +30 -10
  457. package/src/persistence/conversation-bootstrap.ts +17 -0
  458. package/src/persistence/conversation-crud.ts +363 -97
  459. package/src/persistence/conversation-group-migration.ts +1 -1
  460. package/src/persistence/conversation-key-store.ts +38 -10
  461. package/src/persistence/conversation-lineage.ts +226 -0
  462. package/src/persistence/conversation-plugin-facade.ts +28 -3
  463. package/src/persistence/conversation-queries.ts +207 -33
  464. package/src/persistence/conversation-types.ts +107 -1
  465. package/src/persistence/jobs-store.ts +32 -8
  466. package/src/persistence/llm-usage-store.ts +22 -0
  467. package/src/persistence/migrations/361-normalize-managed-connection-rows.test.ts +168 -0
  468. package/src/persistence/migrations/361-normalize-managed-connection-rows.ts +76 -0
  469. package/src/persistence/migrations/362-add-conversation-subagent-kind.test.ts +187 -0
  470. package/src/persistence/migrations/362-add-conversation-subagent-kind.ts +93 -0
  471. package/src/persistence/migrations/363-backfill-schedule-inference-profile.test.ts +229 -0
  472. package/src/persistence/migrations/363-backfill-schedule-inference-profile.ts +127 -0
  473. package/src/persistence/migrations/364-add-schedule-source-key.test.ts +120 -0
  474. package/src/persistence/migrations/364-add-schedule-source-key.ts +43 -0
  475. package/src/persistence/migrations/365-add-conversation-fork-strategy.ts +27 -0
  476. package/src/persistence/schema/conversations.ts +32 -0
  477. package/src/persistence/schema/infrastructure.ts +3 -0
  478. package/src/persistence/steps.ts +30 -0
  479. package/src/persistence/subagent-store.ts +148 -6
  480. package/src/platform/managed-speech.test.ts +20 -0
  481. package/src/platform/managed-speech.ts +9 -0
  482. package/src/plugin-api/index.ts +8 -0
  483. package/src/plugin-api/webhook-url.ts +151 -0
  484. package/src/plugins/__tests__/installed-plugin-dirs.test.ts +82 -0
  485. package/src/plugins/__tests__/source-fingerprint.test.ts +5 -1
  486. package/src/plugins/collect-source-versions.ts +4 -27
  487. package/src/plugins/defaults/exploration-drift/hooks/post-tool-use.ts +5 -5
  488. package/src/plugins/defaults/exploration-drift/package.json +1 -1
  489. package/src/plugins/defaults/image-recovery/hooks/post-model-call.ts +4 -2
  490. package/src/plugins/defaults/image-recovery/recover.ts +67 -57
  491. package/src/plugins/defaults/index.ts +1 -1
  492. package/src/plugins/defaults/memory/__tests__/conversation-queries.test.ts +64 -18
  493. package/src/plugins/defaults/memory/__tests__/jobs-worker-outcome-contract.test.ts +242 -0
  494. package/src/plugins/defaults/memory/__tests__/jobs-worker-retrospective-sweep.test.ts +24 -1
  495. package/src/plugins/defaults/memory/__tests__/memory-retrospective-enqueue.test.ts +48 -3
  496. package/src/plugins/defaults/memory/__tests__/memory-retrospective-job.test.ts +511 -19
  497. package/src/plugins/defaults/memory/__tests__/memory-retrospective-prompt.test.ts +39 -3
  498. package/src/plugins/defaults/memory/__tests__/memory-retrospective-provider-path.test.ts +493 -0
  499. package/src/plugins/defaults/memory/__tests__/memory-retrospective-wake-chain.test.ts +5 -4
  500. package/src/plugins/defaults/memory/__tests__/memory-tier-boundary-guard.test.ts +5 -1
  501. package/src/plugins/defaults/memory/job-handlers.ts +87 -5
  502. package/src/plugins/defaults/memory/jobs-worker.ts +60 -6
  503. package/src/plugins/defaults/memory/memory-retrospective-constants.ts +11 -0
  504. package/src/plugins/defaults/memory/memory-retrospective-enqueue.ts +29 -0
  505. package/src/plugins/defaults/memory/memory-retrospective-job.ts +342 -60
  506. package/src/plugins/defaults/memory/memory-retrospective-prompt.ts +23 -3
  507. package/src/plugins/defaults/memory/substrate/consolidation-lock.ts +9 -4
  508. package/src/plugins/defaults/memory/substrate/sweep-job.ts +7 -0
  509. package/src/plugins/defaults/memory/v1/graph/injection.ts +1 -1
  510. package/src/plugins/defaults/memory/v2/router.ts +8 -0
  511. package/src/plugins/defaults/turn-context/unified-turn-context.ts +1 -2
  512. package/src/plugins/external-plugin-loader.ts +17 -8
  513. package/src/plugins/installed-plugin-dirs.ts +101 -0
  514. package/src/plugins/mtime-cache.ts +34 -22
  515. package/src/plugins/types.ts +69 -8
  516. package/src/prompts/persona-resolver.ts +6 -2
  517. package/src/prompts/system-prompt.ts +72 -17
  518. package/src/providers/__tests__/inference.test.ts +40 -0
  519. package/src/providers/__tests__/preflight-resolved-config.test.ts +58 -1
  520. package/src/providers/__tests__/retry-callsite.test.ts +67 -0
  521. package/src/providers/__tests__/vellum-connection-routing.test.ts +42 -11
  522. package/src/providers/anthropic/client.ts +112 -95
  523. package/src/providers/connection-resolution.ts +35 -1
  524. package/src/providers/gemini/client.ts +5 -5
  525. package/src/providers/inference/adapter-factory.ts +16 -10
  526. package/src/providers/inference/auth.ts +16 -0
  527. package/src/providers/inference/connection-availability.ts +164 -12
  528. package/src/providers/inference/connections.ts +25 -1
  529. package/src/providers/media-resolve.ts +21 -11
  530. package/src/providers/model-catalog.ts +15 -0
  531. package/src/providers/openai/chat-completions-provider.ts +4 -4
  532. package/src/providers/openai/responses-provider.ts +12 -25
  533. package/src/providers/registry.ts +9 -4
  534. package/src/providers/retry.ts +25 -0
  535. package/src/providers/server-tool-pairing.ts +134 -0
  536. package/src/providers/speech-to-text/__tests__/resolve.test.ts +187 -6
  537. package/src/providers/speech-to-text/deepgram.ts +1 -0
  538. package/src/providers/speech-to-text/resolve.ts +80 -10
  539. package/src/providers/speech-to-text/vellum-managed.ts +7 -1
  540. package/src/providers/speech-to-text/vellum-speech-relay-connection.ts +3 -1
  541. package/src/providers/types.ts +26 -6
  542. package/src/providers/vellum-model-routing.ts +6 -7
  543. package/src/runtime/AGENTS.md +2 -0
  544. package/src/runtime/__tests__/agent-wake.test.ts +16 -16
  545. package/src/runtime/__tests__/background-job-runner.test.ts +0 -144
  546. package/src/runtime/__tests__/desktop-presence.test.ts +162 -0
  547. package/src/runtime/agent-wake.ts +63 -24
  548. package/src/runtime/assistant-event-hub.ts +87 -12
  549. package/src/runtime/auth/__tests__/guard-tests.test.ts +2 -0
  550. package/src/runtime/auth/__tests__/scopes.test.ts +20 -0
  551. package/src/runtime/auth/same-actor.ts +56 -8
  552. package/src/runtime/auth/scopes.ts +12 -0
  553. package/src/runtime/auth/types.ts +2 -0
  554. package/src/runtime/background-job-runner.ts +5 -27
  555. package/src/runtime/channel-readiness-service.ts +59 -3
  556. package/src/runtime/channel-readiness-types.ts +18 -0
  557. package/src/runtime/desktop-presence.ts +69 -0
  558. package/src/runtime/http-server.ts +12 -0
  559. package/src/runtime/local-actor-identity.ts +11 -9
  560. package/src/runtime/migrations/__tests__/vbundle-symlink-streaming-importer.test.ts +0 -0
  561. package/src/runtime/routes/__tests__/channel-verification-routes.test.ts +1 -1
  562. package/src/runtime/routes/__tests__/client-routes.test.ts +186 -14
  563. package/src/runtime/routes/__tests__/conversation-list-routes.test.ts +547 -1
  564. package/src/runtime/routes/__tests__/conversation-management-routes.test.ts +162 -7
  565. package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +14 -10
  566. package/src/runtime/routes/__tests__/inference-profiles-routes.test.ts +340 -3
  567. package/src/runtime/routes/__tests__/llm-call-sites-routes.test.ts +28 -1
  568. package/src/runtime/routes/__tests__/plugins-routes.test.ts +173 -1
  569. package/src/runtime/routes/__tests__/question-routes.test.ts +64 -0
  570. package/src/runtime/routes/__tests__/retrospective-routes.test.ts +20 -0
  571. package/src/runtime/routes/__tests__/skill-history-route.test.ts +116 -0
  572. package/src/runtime/routes/__tests__/wake-conversation-routes.test.ts +60 -2
  573. package/src/runtime/routes/archive-utils.ts +16 -12
  574. package/src/runtime/routes/attachment-routes.ts +13 -6
  575. package/src/runtime/routes/canned-reply-release.ts +54 -0
  576. package/src/runtime/routes/channel-verification-routes.ts +2 -27
  577. package/src/runtime/routes/chatgpt-subscription-auth-routes.ts +5 -0
  578. package/src/runtime/routes/client-routes.ts +114 -2
  579. package/src/runtime/routes/conversation-cli-routes.ts +10 -12
  580. package/src/runtime/routes/conversation-list-routes.ts +59 -10
  581. package/src/runtime/routes/conversation-management-routes.ts +47 -18
  582. package/src/runtime/routes/conversation-query-routes.ts +32 -53
  583. package/src/runtime/routes/conversation-routes.ts +360 -293
  584. package/src/runtime/routes/credential-prompt-routes.ts +23 -2
  585. package/src/runtime/routes/debug-routes.ts +4 -1
  586. package/src/runtime/routes/default-provider-routes.ts +7 -11
  587. package/src/runtime/routes/documents-routes.ts +44 -1
  588. package/src/runtime/routes/events-routes.ts +28 -1
  589. package/src/runtime/routes/host-app-control-routes.ts +2 -0
  590. package/src/runtime/routes/host-bash-routes.ts +2 -0
  591. package/src/runtime/routes/host-browser-routes.ts +2 -0
  592. package/src/runtime/routes/host-cu-routes.ts +2 -0
  593. package/src/runtime/routes/host-file-routes.ts +2 -0
  594. package/src/runtime/routes/host-transfer-routes.ts +4 -0
  595. package/src/runtime/routes/identity-routes.ts +6 -6
  596. package/src/runtime/routes/inbound-message-handler.ts +5 -5
  597. package/src/runtime/routes/inbound-stages/acl-enforcement.ts +7 -3
  598. package/src/runtime/routes/inbound-stages/background-dispatch.ts +14 -0
  599. package/src/runtime/routes/inbound-stages/bootstrap-intercept.ts +2 -2
  600. package/src/runtime/routes/inbound-stages/guardian-activation-intercept.test.ts +14 -11
  601. package/src/runtime/routes/inbound-stages/guardian-activation-intercept.ts +14 -17
  602. package/src/runtime/routes/inference-profile-availability-guard.ts +171 -0
  603. package/src/runtime/routes/inference-profile-session-handler.ts +26 -2
  604. package/src/runtime/routes/inference-profile-session-routes.ts +2 -1
  605. package/src/runtime/routes/inference-profiles-routes.ts +152 -73
  606. package/src/runtime/routes/inference-provider-connection-routes.ts +0 -16
  607. package/src/runtime/routes/llm-call-sites-routes.ts +13 -0
  608. package/src/runtime/routes/log-export-routes.ts +1 -1
  609. package/src/runtime/routes/notification-routes.ts +0 -14
  610. package/src/runtime/routes/oauth-providers.ts +1 -0
  611. package/src/runtime/routes/plugins-routes.ts +106 -1
  612. package/src/runtime/routes/profiler-routes.ts +4 -2
  613. package/src/runtime/routes/question-routes.ts +28 -10
  614. package/src/runtime/routes/retrospective-routes.ts +9 -3
  615. package/src/runtime/routes/schedule-routes.ts +210 -12
  616. package/src/runtime/routes/secret-routes.ts +40 -23
  617. package/src/runtime/routes/settings-routes.ts +41 -13
  618. package/src/runtime/routes/skills-routes.ts +79 -0
  619. package/src/runtime/routes/ui-snapshot-routes.ts +1 -0
  620. package/src/runtime/routes/wake-conversation-routes.ts +23 -0
  621. package/src/runtime/services/__tests__/conversation-serializer.test.ts +1 -0
  622. package/src/runtime/services/conversation-serializer.ts +24 -9
  623. package/src/runtime/sync/documents-sidecar-publish.test.ts +101 -0
  624. package/src/runtime/sync/resource-sync-events.ts +79 -1
  625. package/src/runtime/sync/worker-daemon-notify.test.ts +34 -0
  626. package/src/runtime/sync/worker-daemon-notify.ts +44 -6
  627. package/src/runtime/verification-outbound-actions.ts +250 -222
  628. package/src/runtime/verification-templates.ts +41 -30
  629. package/src/schedule/__tests__/plugin-schedule-declarations.test.ts +614 -0
  630. package/src/schedule/__tests__/plugin-schedule-reconciler.test.ts +1066 -0
  631. package/src/schedule/inference-profile.ts +64 -3
  632. package/src/schedule/plugin-schedule-declarations.ts +518 -0
  633. package/src/schedule/plugin-schedule-reconciler.ts +605 -0
  634. package/src/schedule/plugin-schedules-gate.ts +23 -0
  635. package/src/schedule/schedule-store.ts +520 -26
  636. package/src/schedule/scheduler.ts +38 -0
  637. package/src/security/credential-backend.ts +51 -4
  638. package/src/security/encrypted-store.ts +64 -42
  639. package/src/security/secure-keys.ts +55 -7
  640. package/src/skills/catalog-install.ts +20 -7
  641. package/src/skills/inline-command-runner.ts +2 -7
  642. package/src/skills/skill-history.test.ts +269 -0
  643. package/src/skills/skill-history.ts +250 -0
  644. package/src/skills/skillssh-registry.ts +1 -1
  645. package/src/stt/daemon-batch-transcriber.ts +8 -6
  646. package/src/subagent/__tests__/consult-prompt.test.ts +25 -0
  647. package/src/subagent/consult-prompt.ts +3 -1
  648. package/src/subagent/consult-transcript.ts +1 -1
  649. package/src/subagent/manager.ts +304 -44
  650. package/src/subagent/progress-events.ts +35 -0
  651. package/src/subagent/role-resolution.ts +88 -0
  652. package/src/subagent/types.ts +302 -67
  653. package/src/subagent/validate-allowlists.ts +3 -4
  654. package/src/telegram/__tests__/webhook-health.test.ts +73 -1
  655. package/src/telegram/webhook-health.ts +101 -3
  656. package/src/telemetry/__tests__/telemetry-event-fixtures.ts +2 -0
  657. package/src/telemetry/activation-funnel.ts +6 -1
  658. package/src/telemetry/live-voice-funnel.ts +75 -0
  659. package/src/telemetry/telemetry-event-sources.ts +6 -0
  660. package/src/telemetry/telemetry-wire-source.json +1 -1
  661. package/src/telemetry/telemetry-wire.generated.ts +2 -0
  662. package/src/telemetry/types.ts +23 -4
  663. package/src/telemetry/usage-telemetry-reporter.test.ts +4 -0
  664. package/src/tools/__tests__/subagent-workflow-tool-input-schemas.test.ts +1 -1
  665. package/src/tools/ask-question/ask-question-tool.test.ts +118 -3
  666. package/src/tools/ask-question/ask-question-tool.ts +56 -2
  667. package/src/tools/browser/browser-execution.ts +1 -1
  668. package/src/tools/calls/call-start.ts +8 -5
  669. package/src/tools/conversation-groups/move_to_group.ts +10 -12
  670. package/src/tools/document/document-tool.ts +11 -4
  671. package/src/tools/execution-timeout.ts +9 -2
  672. package/src/tools/executor.ts +7 -6
  673. package/src/tools/filesystem/edit.ts +1 -1
  674. package/src/tools/filesystem/list.ts +1 -1
  675. package/src/tools/filesystem/read.ts +7 -13
  676. package/src/tools/filesystem/search.ts +23 -21
  677. package/src/tools/filesystem/write.ts +4 -1
  678. package/src/tools/host-filesystem/edit.ts +7 -2
  679. package/src/tools/host-filesystem/read.ts +21 -8
  680. package/src/tools/host-filesystem/write.ts +10 -2
  681. package/src/tools/host-terminal/host-shell.ts +2 -2
  682. package/src/tools/mcp/mcp-tool-factory.ts +25 -1
  683. package/src/tools/permission-checker.ts +8 -3
  684. package/src/tools/schedule/delete.ts +13 -1
  685. package/src/tools/schedule/list.ts +14 -3
  686. package/src/tools/schedule/update.ts +56 -33
  687. package/src/tools/shared/filesystem/audio-read.ts +6 -4
  688. package/src/tools/shared/filesystem/file-ops-service.ts +176 -116
  689. package/src/tools/shared/filesystem/image-read.ts +12 -12
  690. package/src/tools/shared/filesystem/path-policy.ts +70 -5
  691. package/src/tools/shared/filesystem/size-guard.ts +6 -6
  692. package/src/tools/shared/input-misuse.ts +72 -0
  693. package/src/tools/skills/scaffold-managed.ts +82 -0
  694. package/src/tools/skills/skill-tool-factory.ts +14 -1
  695. package/src/tools/subagent/consult-deadline.ts +12 -7
  696. package/src/tools/subagent/message.ts +3 -1
  697. package/src/tools/subagent/read.ts +66 -4
  698. package/src/tools/subagent/spawn.ts +500 -78
  699. package/src/tools/tool-approval-handler.ts +45 -4
  700. package/src/tools/types.ts +22 -3
  701. package/src/tools/ui-surface/surface-shape-docs.ts +1 -1
  702. package/src/tools/ui-surface/visual-validation.ts +2 -0
  703. package/src/usage/__tests__/subagent-attribution.test.ts +109 -0
  704. package/src/usage/subagent-attribution.ts +108 -0
  705. package/src/util/__tests__/sqlite-retry.test.ts +112 -0
  706. package/src/util/abort-reasons.ts +16 -0
  707. package/src/util/ansi.ts +44 -0
  708. package/src/util/disk-usage.ts +17 -13
  709. package/src/util/image-conversion.ts +20 -13
  710. package/src/util/schedule-source-key.ts +23 -0
  711. package/src/util/worker-process.ts +7 -1
  712. package/src/workspace/git-service.ts +33 -9
  713. package/src/workspace/migrations/140-repair-seed-pinned-memory-v3-live.ts +260 -0
  714. package/src/workspace/migrations/141-stt-english-default-to-multilingual.ts +111 -0
  715. package/src/workspace/migrations/__tests__/141-stt-english-default-to-multilingual.test.ts +126 -0
  716. package/src/workspace/migrations/registry.ts +4 -0
  717. package/src/workspace/top-level-renderer.ts +15 -2
  718. package/src/__tests__/tool-side-effects-slack-dm.test.ts +0 -357
  719. package/src/daemon/install-symlink.ts +0 -207
  720. package/src/notifications/deferred-emit.ts +0 -147
@@ -1,7 +1,10 @@
1
1
  import { z } from "zod";
2
2
 
3
3
  import { resolveCapabilities } from "../../runtime/capabilities.js";
4
- import { validateScheduleInferenceProfile } from "../../schedule/inference-profile.js";
4
+ import {
5
+ formatScheduleInferenceProfile,
6
+ validateScheduleInferenceProfile,
7
+ } from "../../schedule/inference-profile.js";
5
8
  import { validateRruleSetLines } from "../../schedule/recurrence-engine.js";
6
9
  import {
7
10
  detectScheduleSyntax,
@@ -17,6 +20,7 @@ import {
17
20
  describeCronExpression,
18
21
  formatLocalDate,
19
22
  getSchedule,
23
+ setUserEnabled,
20
24
  updateSchedule,
21
25
  } from "../../schedule/schedule-store.js";
22
26
  import { resolveGroupReference } from "../conversation-groups/group_shared.js";
@@ -45,8 +49,9 @@ const VALID_ROUTING_INTENTS: RoutingIntent[] = [
45
49
  * preprocessing that would silently turn an explicit update into a no-op.
46
50
  * - `timezone` and `script` are nullable at runtime though advertised as
47
51
  * plain strings: `updateSchedule` persists null as "clear this field".
48
- * - `timeout_ms` / `inference_profile` / `group` advertise null (it reverts
49
- * to the default); the executor's bespoke handling stays.
52
+ * - `timeout_ms` / `group` advertise null (it reverts to the default), and
53
+ * `inference_profile` advertises null (it re-pins to the current default);
54
+ * the executor's bespoke handling stays.
50
55
  * - `mode`, `routing_intent`, `workflow_name`, and `workflow_args` are
51
56
  * deliberately UNDECLARED (loose passthrough): the first two keep bespoke
52
57
  * `VALID_*` errors; `workflow_name` has bespoke coercion semantics in the
@@ -94,10 +99,11 @@ export async function executeScheduleUpdate(
94
99
  return { content: "Error: job_id is required", isError: true };
95
100
  }
96
101
 
102
+ const existing = getSchedule(jobId);
103
+
97
104
  // Prevent changing a one-shot to recurring or vice versa. (`fire_at` is
98
105
  // not an advertised update field, so it stays a raw-input read.)
99
106
  if (parsed.expression !== undefined || input.fire_at !== undefined) {
100
- const existing = getSchedule(jobId);
101
107
  if (!existing) {
102
108
  return { content: `Error: Schedule not found: ${jobId}`, isError: true };
103
109
  }
@@ -203,8 +209,8 @@ export async function executeScheduleUpdate(
203
209
  updates.retryBackoffMs = parsed.retry_backoff_ms;
204
210
  }
205
211
 
206
- // Inference profile override (null clears it, reverting to the default
207
- // main-agent model selection)
212
+ // Inference profile (null re-pins the schedule to the currently resolved
213
+ // default rather than leaving it to follow whatever the default becomes)
208
214
  if (parsed.inference_profile !== undefined) {
209
215
  if (parsed.inference_profile === null) {
210
216
  updates.inferenceProfile = null;
@@ -278,6 +284,22 @@ export async function executeScheduleUpdate(
278
284
  };
279
285
  }
280
286
 
287
+ // A plugin-sourced schedule accepts only the enabled toggle, recorded as
288
+ // the user_enabled override exactly like the HTTP toggle route. Every other
289
+ // field is owned by the plugin's schedule file, and the reconciler would
290
+ // overwrite a direct edit on its next pass.
291
+ const isPluginSourced = existing?.sourceKey != null;
292
+ if (
293
+ isPluginSourced &&
294
+ Object.keys(updates).some((field) => field !== "enabled")
295
+ ) {
296
+ return {
297
+ content:
298
+ "Error: This schedule is managed by a plugin, so only enabled can be changed here. To change anything else, edit the plugin's schedule file.",
299
+ isError: true,
300
+ };
301
+ }
302
+
281
303
  // Mirror the HTTP route: a schedule whose RESULTING mode is `workflow` must
282
304
  // carry a non-empty workflowName. Compute the post-update
283
305
  // state (the update's value if present, else the persisted one) so both
@@ -285,7 +307,6 @@ export async function executeScheduleUpdate(
285
307
  // schedule" are rejected — otherwise the scheduler hits the `!job.workflowName`
286
308
  // skip branch and a one-shot firing job wedges.
287
309
  if (updates.mode !== undefined || updates.workflowName !== undefined) {
288
- const existing = getSchedule(jobId);
289
310
  if (existing) {
290
311
  const resultingMode =
291
312
  updates.mode !== undefined ? (updates.mode as string) : existing.mode;
@@ -325,31 +346,33 @@ export async function executeScheduleUpdate(
325
346
  }
326
347
 
327
348
  try {
328
- const job = await updateSchedule(
329
- jobId,
330
- updates as {
331
- name?: string;
332
- description?: string;
333
- cronExpression?: string;
334
- timezone?: string | null;
335
- message?: string;
336
- script?: string | null;
337
- enabled?: boolean;
338
- syntax?: ScheduleSyntax;
339
- expression?: string;
340
- mode?: ScheduleMode;
341
- routingIntent?: RoutingIntent;
342
- routingHints?: Record<string, unknown>;
343
- quiet?: boolean;
344
- reuseConversation?: boolean;
345
- maxRetries?: number;
346
- retryBackoffMs?: number;
347
- timeoutMs?: number | null;
348
- workflowName?: string | null;
349
- workflowArgs?: unknown;
350
- inferenceProfile?: string | null;
351
- },
352
- );
349
+ const job = isPluginSourced
350
+ ? await setUserEnabled(jobId, updates.enabled as boolean)
351
+ : await updateSchedule(
352
+ jobId,
353
+ updates as {
354
+ name?: string;
355
+ description?: string;
356
+ cronExpression?: string;
357
+ timezone?: string | null;
358
+ message?: string;
359
+ script?: string | null;
360
+ enabled?: boolean;
361
+ syntax?: ScheduleSyntax;
362
+ expression?: string;
363
+ mode?: ScheduleMode;
364
+ routingIntent?: RoutingIntent;
365
+ routingHints?: Record<string, unknown>;
366
+ quiet?: boolean;
367
+ reuseConversation?: boolean;
368
+ maxRetries?: number;
369
+ retryBackoffMs?: number;
370
+ timeoutMs?: number | null;
371
+ workflowName?: string | null;
372
+ workflowArgs?: unknown;
373
+ inferenceProfile?: string | null;
374
+ },
375
+ );
353
376
 
354
377
  if (!job) {
355
378
  return { content: `Error: Schedule not found: ${jobId}`, isError: true };
@@ -369,7 +392,7 @@ export async function executeScheduleUpdate(
369
392
  ` Description: ${job.description}`,
370
393
  ` Syntax: ${job.syntax}`,
371
394
  ` Mode: ${job.mode}`,
372
- ` Inference profile: ${job.inferenceProfile ?? "default (mainAgent)"}`,
395
+ ` Inference profile: ${formatScheduleInferenceProfile(job.inferenceProfile)}`,
373
396
  ` Schedule: ${scheduleDescription}${job.timezone ? ` (${job.timezone})` : ""}`,
374
397
  ` Enabled: ${job.enabled}`,
375
398
  ` Next run: ${job.enabled ? formatLocalDate(job.nextRunAt) : "n/a (disabled)"}`,
@@ -1,4 +1,4 @@
1
- import { readFileSync, statSync } from "node:fs";
1
+ import { readFile, stat as fsStat } from "node:fs/promises";
2
2
  import { basename, extname } from "node:path";
3
3
 
4
4
  import { GEMINI_MAX_INLINE_AUDIO_BYTES } from "../../../providers/gemini/inline-media.js";
@@ -67,7 +67,9 @@ function buildAudioToolResult(
67
67
  * The caller is responsible for path resolution and sandbox enforcement —
68
68
  * `resolvedPath` must be an already-validated absolute path.
69
69
  */
70
- export function readAudioFile(resolvedPath: string): ToolExecutionResult {
70
+ export async function readAudioFile(
71
+ resolvedPath: string,
72
+ ): Promise<ToolExecutionResult> {
71
73
  const mimeType = EXTENSION_MIME[extname(resolvedPath).toLowerCase()];
72
74
  if (!mimeType) {
73
75
  // Defensive: callers gate on AUDIO_EXTENSIONS, so this is unreachable.
@@ -79,7 +81,7 @@ export function readAudioFile(resolvedPath: string): ToolExecutionResult {
79
81
 
80
82
  let stat;
81
83
  try {
82
- stat = statSync(resolvedPath);
84
+ stat = await fsStat(resolvedPath);
83
85
  } catch {
84
86
  return { content: `Error: file not found: ${resolvedPath}`, isError: true };
85
87
  }
@@ -96,7 +98,7 @@ export function readAudioFile(resolvedPath: string): ToolExecutionResult {
96
98
 
97
99
  let buffer: Buffer;
98
100
  try {
99
- buffer = readFileSync(resolvedPath) as Buffer;
101
+ buffer = (await readFile(resolvedPath)) as Buffer;
100
102
  } catch (err) {
101
103
  const msg = err instanceof Error ? err.message : String(err);
102
104
  return { content: `Error reading file: ${msg}`, isError: true };
@@ -1,10 +1,4 @@
1
- import {
2
- lstatSync,
3
- readdirSync,
4
- readFileSync,
5
- statSync,
6
- writeFileSync,
7
- } from "node:fs";
1
+ import { lstat, readdir, readFile, stat, writeFile } from "node:fs/promises";
8
2
  import { dirname, join } from "node:path";
9
3
 
10
4
  import { minimatch } from "minimatch";
@@ -42,6 +36,32 @@ export type PathPolicy = (
42
36
  // Service
43
37
  // ---------------------------------------------------------------------------
44
38
 
39
+ // Serialize mutations per resolved path. The agent loop executes sibling
40
+ // tool calls in parallel, and the async read -> apply -> write window would
41
+ // otherwise let two edits of the same file both read the same old content and
42
+ // have the later write silently drop the earlier edit (the previous
43
+ // synchronous service made each mutation atomic within the event loop).
44
+ const fileWriteLocks = new Map<string, Promise<void>>();
45
+
46
+ async function withFileWriteLock<T>(
47
+ filePath: string,
48
+ fn: () => Promise<T>,
49
+ ): Promise<T> {
50
+ const prev = fileWriteLocks.get(filePath) ?? Promise.resolve();
51
+ const run = prev.then(fn, fn);
52
+ const tail = run.then(
53
+ () => undefined,
54
+ () => undefined,
55
+ );
56
+ fileWriteLocks.set(filePath, tail);
57
+ void tail.then(() => {
58
+ if (fileWriteLocks.get(filePath) === tail) {
59
+ fileWriteLocks.delete(filePath);
60
+ }
61
+ });
62
+ return run;
63
+ }
64
+
45
65
  function pathError(
46
66
  path: string,
47
67
  reason: PathFailureReason,
@@ -57,6 +77,29 @@ function pathError(
57
77
  }
58
78
  }
59
79
 
80
+ /**
81
+ * Lines returned by a read that names no `limit`. Without a default the read
82
+ * returns the whole file, and a file-read result is honored in full for the
83
+ * rest of the turn (see `isSpoolEligible` in `context/tool-result-spool.ts`),
84
+ * so one unbounded read of a large file rides every subsequent LLM call in
85
+ * that turn. The cap bounds that; `offset`/`limit` page past it, and
86
+ * {@link truncationNotice} tells the model when it is looking at a window
87
+ * rather than the whole file.
88
+ */
89
+ export const DEFAULT_READ_LINE_LIMIT = 2000;
90
+
91
+ /**
92
+ * Trailing marker appended when a read stops short of the last line. Silent
93
+ * truncation is the failure mode worth avoiding: a model that cannot tell a
94
+ * window from a whole file reasons about code it never saw.
95
+ */
96
+ function truncationNotice(
97
+ lastLineReturned: number,
98
+ totalLines: number,
99
+ ): string {
100
+ return `\n\n[Truncated: showing through line ${lastLineReturned} of ${totalLines}. Read on with offset=${lastLineReturned + 1}, or pass an explicit limit.]`;
101
+ }
102
+
60
103
  export class FileSystemOps {
61
104
  private policy: PathPolicy;
62
105
  private sizeLimit: number | undefined;
@@ -70,7 +113,7 @@ export class FileSystemOps {
70
113
  // Read
71
114
  // -------------------------------------------------------------------------
72
115
 
73
- readFileSafe(input: ReadInput): ReadResult {
116
+ async readFileSafe(input: ReadInput): Promise<ReadResult> {
74
117
  const pathCheck = this.policy(input.path, { mustExist: true });
75
118
  if (!pathCheck.ok) {
76
119
  return {
@@ -84,23 +127,24 @@ export class FileSystemOps {
84
127
  return { ok: false, error: Err.notFound(filePath) };
85
128
  }
86
129
 
87
- const stat = statSync(filePath);
88
- if (!stat.isFile()) {
130
+ const pathStat = await stat(filePath);
131
+ if (!pathStat.isFile()) {
89
132
  return { ok: false, error: Err.notAFile(filePath) };
90
133
  }
91
134
 
92
- const sizeErr = checkFileSizeOnDisk(filePath, this.sizeLimit);
135
+ const sizeErr = await checkFileSizeOnDisk(filePath, this.sizeLimit);
93
136
  if (sizeErr) {
94
137
  return { ok: false, error: Err.sizeLimitExceeded(filePath, sizeErr) };
95
138
  }
96
139
 
97
140
  try {
98
- const raw = readFileSync(filePath, "utf-8");
141
+ const raw = await readFile(filePath, "utf-8");
99
142
  const lines = raw.split("\n");
100
143
 
101
144
  const offset = (input.offset ?? 1) - 1;
102
- const limit = input.limit ?? lines.length;
103
- const selected = lines.slice(Math.max(0, offset), offset + limit);
145
+ const start = Math.max(0, offset);
146
+ const limit = input.limit ?? DEFAULT_READ_LINE_LIMIT;
147
+ const selected = lines.slice(start, offset + limit);
104
148
 
105
149
  const numbered = selected
106
150
  .map((line, i) => {
@@ -109,7 +153,16 @@ export class FileSystemOps {
109
153
  })
110
154
  .join("\n");
111
155
 
112
- return { ok: true, value: { content: numbered } };
156
+ // Only when the window stops before the last line. An empty window means
157
+ // the caller paged past the end or asked for nothing, which is not a
158
+ // truncated read.
159
+ const lastLineReturned = start + selected.length;
160
+ const content =
161
+ selected.length > 0 && lastLineReturned < lines.length
162
+ ? numbered + truncationNotice(lastLineReturned, lines.length)
163
+ : numbered;
164
+
165
+ return { ok: true, value: { content } };
113
166
  } catch (err) {
114
167
  const msg = err instanceof Error ? err.message : String(err);
115
168
  return { ok: false, error: Err.ioError(filePath, msg) };
@@ -120,7 +173,7 @@ export class FileSystemOps {
120
173
  // Write
121
174
  // -------------------------------------------------------------------------
122
175
 
123
- writeFileSafe(input: WriteInput): WriteResult {
176
+ async writeFileSafe(input: WriteInput): Promise<WriteResult> {
124
177
  const pathCheck = this.policy(input.path, { mustExist: false });
125
178
  if (!pathCheck.ok) {
126
179
  return {
@@ -135,41 +188,43 @@ export class FileSystemOps {
135
188
  return { ok: false, error: Err.sizeLimitExceeded(filePath, sizeErr) };
136
189
  }
137
190
 
138
- try {
139
- ensureDir(dirname(filePath));
140
-
141
- let oldContent = "";
142
- const isNewFile = !pathExists(filePath);
143
- if (!isNewFile) {
144
- try {
145
- oldContent = readFileSync(filePath, "utf-8");
146
- } catch {
147
- // Unreadable existing file - keep oldContent as empty string.
191
+ return withFileWriteLock(filePath, async (): Promise<WriteResult> => {
192
+ try {
193
+ ensureDir(dirname(filePath));
194
+
195
+ let oldContent = "";
196
+ const isNewFile = !pathExists(filePath);
197
+ if (!isNewFile) {
198
+ try {
199
+ oldContent = await readFile(filePath, "utf-8");
200
+ } catch {
201
+ // Unreadable existing file - keep oldContent as empty string.
202
+ }
148
203
  }
149
- }
150
-
151
- writeFileSync(filePath, input.content);
152
204
 
153
- return {
154
- ok: true,
155
- value: {
156
- filePath,
157
- isNewFile,
158
- oldContent,
159
- newContent: input.content,
160
- },
161
- };
162
- } catch (err) {
163
- const msg = err instanceof Error ? err.message : String(err);
164
- return { ok: false, error: Err.ioError(filePath, msg) };
165
- }
205
+ await writeFile(filePath, input.content);
206
+
207
+ return {
208
+ ok: true,
209
+ value: {
210
+ filePath,
211
+ isNewFile,
212
+ oldContent,
213
+ newContent: input.content,
214
+ },
215
+ };
216
+ } catch (err) {
217
+ const msg = err instanceof Error ? err.message : String(err);
218
+ return { ok: false, error: Err.ioError(filePath, msg) };
219
+ }
220
+ });
166
221
  }
167
222
 
168
223
  // -------------------------------------------------------------------------
169
224
  // Edit
170
225
  // -------------------------------------------------------------------------
171
226
 
172
- editFileSafe(input: EditInput): EditResult {
227
+ async editFileSafe(input: EditInput): Promise<EditResult> {
173
228
  const pathCheck = this.policy(input.path, { mustExist: true });
174
229
  if (!pathCheck.ok) {
175
230
  return {
@@ -179,82 +234,85 @@ export class FileSystemOps {
179
234
  }
180
235
  const filePath = pathCheck.resolved;
181
236
 
182
- // Size-check the file on disk (swallow ENOENT - readFileSync gives a clearer error)
183
- try {
184
- const sizeErr = checkFileSizeOnDisk(filePath, this.sizeLimit);
185
- if (sizeErr) {
186
- return { ok: false, error: Err.sizeLimitExceeded(filePath, sizeErr) };
237
+ return withFileWriteLock(filePath, async (): Promise<EditResult> => {
238
+ // Size-check the file on disk (swallow ENOENT - the read below gives a
239
+ // clearer error)
240
+ try {
241
+ const sizeErr = await checkFileSizeOnDisk(filePath, this.sizeLimit);
242
+ if (sizeErr) {
243
+ return { ok: false, error: Err.sizeLimitExceeded(filePath, sizeErr) };
244
+ }
245
+ } catch {
246
+ // Fall through - the read below will surface NOT_FOUND.
187
247
  }
188
- } catch {
189
- // Fall through - the readFileSync below will surface NOT_FOUND.
190
- }
191
248
 
192
- let content: string;
193
- try {
194
- content = readFileSync(filePath, "utf-8");
195
- } catch (err) {
196
- const code =
197
- err instanceof Error && "code" in err
198
- ? (err as NodeJS.ErrnoException).code
199
- : undefined;
200
- if (code === "EISDIR") {
201
- return { ok: false, error: Err.notAFile(filePath) };
249
+ let content: string;
250
+ try {
251
+ content = await readFile(filePath, "utf-8");
252
+ } catch (err) {
253
+ const code =
254
+ err instanceof Error && "code" in err
255
+ ? (err as NodeJS.ErrnoException).code
256
+ : undefined;
257
+ if (code === "EISDIR") {
258
+ return { ok: false, error: Err.notAFile(filePath) };
259
+ }
260
+ if (code === "ENOENT") {
261
+ return { ok: false, error: Err.notFound(filePath) };
262
+ }
263
+ const msg = err instanceof Error ? err.message : String(err);
264
+ return { ok: false, error: Err.ioError(filePath, msg) };
202
265
  }
203
- if (code === "ENOENT") {
204
- return { ok: false, error: Err.notFound(filePath) };
266
+
267
+ if (input.oldString.length === 0) {
268
+ return { ok: false, error: Err.matchNotFound(filePath) };
205
269
  }
206
- const msg = err instanceof Error ? err.message : String(err);
207
- return { ok: false, error: Err.ioError(filePath, msg) };
208
- }
209
270
 
210
- if (input.oldString.length === 0) {
211
- return { ok: false, error: Err.matchNotFound(filePath) };
212
- }
271
+ const result = applyEdit(
272
+ content,
273
+ input.oldString,
274
+ input.newString,
275
+ input.replaceAll,
276
+ );
213
277
 
214
- const result = applyEdit(
215
- content,
216
- input.oldString,
217
- input.newString,
218
- input.replaceAll,
219
- );
278
+ if (!result.ok) {
279
+ if (result.reason === "not_found") {
280
+ return { ok: false, error: Err.matchNotFound(filePath) };
281
+ }
282
+ return {
283
+ ok: false,
284
+ error: Err.matchAmbiguous(filePath, result.matchCount),
285
+ };
286
+ }
220
287
 
221
- if (!result.ok) {
222
- if (result.reason === "not_found") {
223
- return { ok: false, error: Err.matchNotFound(filePath) };
288
+ try {
289
+ await writeFile(filePath, result.updatedContent);
290
+ } catch (err) {
291
+ const msg = err instanceof Error ? err.message : String(err);
292
+ return { ok: false, error: Err.ioError(filePath, msg) };
224
293
  }
294
+
225
295
  return {
226
- ok: false,
227
- error: Err.matchAmbiguous(filePath, result.matchCount),
296
+ ok: true,
297
+ value: {
298
+ filePath,
299
+ matchCount: result.matchCount,
300
+ oldContent: content,
301
+ newContent: result.updatedContent,
302
+ matchMethod: result.matchMethod,
303
+ similarity: result.similarity,
304
+ actualOld: result.actualOld,
305
+ actualNew: result.actualNew,
306
+ },
228
307
  };
229
- }
230
-
231
- try {
232
- writeFileSync(filePath, result.updatedContent);
233
- } catch (err) {
234
- const msg = err instanceof Error ? err.message : String(err);
235
- return { ok: false, error: Err.ioError(filePath, msg) };
236
- }
237
-
238
- return {
239
- ok: true,
240
- value: {
241
- filePath,
242
- matchCount: result.matchCount,
243
- oldContent: content,
244
- newContent: result.updatedContent,
245
- matchMethod: result.matchMethod,
246
- similarity: result.similarity,
247
- actualOld: result.actualOld,
248
- actualNew: result.actualNew,
249
- },
250
- };
308
+ });
251
309
  }
252
310
 
253
311
  // -------------------------------------------------------------------------
254
312
  // List
255
313
  // -------------------------------------------------------------------------
256
314
 
257
- listDirSafe(input: ListInput): ListResult {
315
+ async listDirSafe(input: ListInput): Promise<ListResult> {
258
316
  const pathCheck = this.policy(input.path, { mustExist: true });
259
317
  if (!pathCheck.ok) {
260
318
  return {
@@ -268,13 +326,13 @@ export class FileSystemOps {
268
326
  return { ok: false, error: Err.notFound(resolved) };
269
327
  }
270
328
 
271
- const stat = statSync(resolved);
272
- if (!stat.isDirectory()) {
329
+ const pathStat = await stat(resolved);
330
+ if (!pathStat.isDirectory()) {
273
331
  return { ok: false, error: Err.notADirectory(resolved) };
274
332
  }
275
333
 
276
334
  try {
277
- let entries = readdirSync(resolved, { withFileTypes: true });
335
+ let entries = await readdir(resolved, { withFileTypes: true });
278
336
 
279
337
  if (input.glob) {
280
338
  const pattern = input.glob;
@@ -294,16 +352,18 @@ export class FileSystemOps {
294
352
  const truncated = sorted.length > MAX_ENTRIES;
295
353
  const visible = sorted.slice(0, MAX_ENTRIES);
296
354
 
297
- const lines = visible.map((entry) => {
298
- if (entry.isDirectory()) {
299
- return `${entry.name}/`;
300
- }
301
- if (entry.isSymbolicLink()) {
302
- return `${entry.name}@`;
303
- }
304
- const fileStat = lstatSync(join(resolved, entry.name));
305
- return `${entry.name} ${formatSize(fileStat.size)}`;
306
- });
355
+ const lines = await Promise.all(
356
+ visible.map(async (entry) => {
357
+ if (entry.isDirectory()) {
358
+ return `${entry.name}/`;
359
+ }
360
+ if (entry.isSymbolicLink()) {
361
+ return `${entry.name}@`;
362
+ }
363
+ const fileStat = await lstat(join(resolved, entry.name));
364
+ return `${entry.name} ${formatSize(fileStat.size)}`;
365
+ }),
366
+ );
307
367
 
308
368
  if (truncated) {
309
369
  lines.push(
@@ -1,4 +1,4 @@
1
- import { readFileSync, statSync } from "node:fs";
1
+ import { readFile, stat as fsStat } from "node:fs/promises";
2
2
 
3
3
  import { optimizeImageForTransport } from "../../../agent/image-optimize.js";
4
4
  import type { ImageContent } from "../../../providers/types.js";
@@ -63,10 +63,10 @@ export function detectMediaType(buf: Buffer): string | null {
63
63
  return null;
64
64
  }
65
65
 
66
- function buildImageToolResult(
66
+ async function buildImageToolResult(
67
67
  buffer: Buffer,
68
68
  sourceLabel: string,
69
- ): ToolExecutionResult {
69
+ ): Promise<ToolExecutionResult> {
70
70
  if (buffer.length > MAX_SOURCE_SIZE_BYTES) {
71
71
  const sizeMB = (buffer.length / (1024 * 1024)).toFixed(1);
72
72
  return {
@@ -87,10 +87,8 @@ function buildImageToolResult(
87
87
 
88
88
  // Optimize before size-checking — oversized images may compress under the limit.
89
89
  const rawBase64 = buffer.toString("base64");
90
- const { data: base64Data, mediaType: finalType } = optimizeImageForTransport(
91
- rawBase64,
92
- detectedType,
93
- );
90
+ const { data: base64Data, mediaType: finalType } =
91
+ await optimizeImageForTransport(rawBase64, detectedType);
94
92
  const optimized = base64Data !== rawBase64;
95
93
 
96
94
  const optimizedBytes = Buffer.from(base64Data, "base64").length;
@@ -124,10 +122,10 @@ function buildImageToolResult(
124
122
  };
125
123
  }
126
124
 
127
- export function readImageBase64(
125
+ export async function readImageBase64(
128
126
  base64Data: string,
129
127
  sourceLabel: string,
130
- ): ToolExecutionResult {
128
+ ): Promise<ToolExecutionResult> {
131
129
  return buildImageToolResult(Buffer.from(base64Data, "base64"), sourceLabel);
132
130
  }
133
131
 
@@ -138,10 +136,12 @@ export function readImageBase64(
138
136
  * The caller is responsible for path resolution and sandbox enforcement -
139
137
  * `resolvedPath` must be an already-validated absolute path.
140
138
  */
141
- export function readImageFile(resolvedPath: string): ToolExecutionResult {
139
+ export async function readImageFile(
140
+ resolvedPath: string,
141
+ ): Promise<ToolExecutionResult> {
142
142
  let stat;
143
143
  try {
144
- stat = statSync(resolvedPath);
144
+ stat = await fsStat(resolvedPath);
145
145
  } catch {
146
146
  return {
147
147
  content: `Error: file not found: ${resolvedPath}`,
@@ -163,7 +163,7 @@ export function readImageFile(resolvedPath: string): ToolExecutionResult {
163
163
 
164
164
  let buffer: Buffer;
165
165
  try {
166
- buffer = readFileSync(resolvedPath) as Buffer;
166
+ buffer = (await readFile(resolvedPath)) as Buffer;
167
167
  } catch (err) {
168
168
  const msg = err instanceof Error ? err.message : String(err);
169
169
  return { content: `Error reading file: ${msg}`, isError: true };