@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
@@ -6,6 +6,7 @@
6
6
  * - `ingress.publicBaseUrl` → set to `""`
7
7
  * - `ingress.enabled` → deleted
8
8
  * - `ingress.publicBaseUrlManagedBy` → deleted
9
+ * - `telegram.registeredWebhookUrl` → deleted
9
10
  * - `daemon` → deleted entirely
10
11
  * - `skills.load.extraDirs` → set to `[]`
11
12
  * - `hostBrowser.cdpInspect.desktopAuto` → deleted **only when the source
@@ -45,6 +46,16 @@ export function sanitizeConfigForTransfer(configJson: string): string {
45
46
  delete ingress.publicBaseUrlManagedBy;
46
47
  }
47
48
 
49
+ // Strip the recorded Telegram webhook URL. It records where *this*
50
+ // deployment pointed Telegram, so carrying it across a teleport would have
51
+ // the destination compare its own registration against the source's address
52
+ // and report a mismatch on a channel that is fine. Absent, the health sweep
53
+ // reports unverified until the destination reconciles and records its own.
54
+ if (config.telegram && typeof config.telegram === "object") {
55
+ const telegram = config.telegram as Record<string, unknown>;
56
+ delete telegram.registeredWebhookUrl;
57
+ }
58
+
48
59
  // Strip daemon entirely
49
60
  delete config.daemon;
50
61
 
@@ -50,6 +50,7 @@ import {
50
50
  PlatformConfigSchema,
51
51
  UiConfigSchema,
52
52
  } from "./schemas/platform.js";
53
+ import { PluginUpdatesConfigSchema } from "./schemas/plugin-updates.js";
53
54
  import { SecretDetectionConfigSchema } from "./schemas/security.js";
54
55
  import { ServicesSchema } from "./schemas/services.js";
55
56
  import { SkillsConfigSchema } from "./schemas/skills.js";
@@ -142,6 +143,9 @@ export const AssistantConfigSchema = z.object({
142
143
  .describe(
143
144
  "Per-plugin configuration keyed by plugin name. Validated downstream by each plugin's manifest.config validator at bootstrap.",
144
145
  ),
146
+ pluginUpdates: PluginUpdatesConfigSchema.default(
147
+ PluginUpdatesConfigSchema.parse({}),
148
+ ),
145
149
  legacyTelemetryOptOut: z
146
150
  .boolean()
147
151
  .optional()
@@ -66,11 +66,22 @@ describe("SttServiceSchema", () => {
66
66
  );
67
67
  });
68
68
 
69
- test("language is optional and stays absent when unset", () => {
70
- // Absent (not defaulted to "en") so the resolver can tell "user chose
71
- // nothing" from "user chose English" and omit the param entirely.
69
+ test("language defaults to multilingual rather than staying absent", () => {
70
+ // Every config carries a real language, so nothing downstream has to
71
+ // infer one from an absent key. Omitting the param entirely is not a
72
+ // neutral state on Deepgram and the managed relay: it decodes as
73
+ // English, which is what this default exists to stop.
72
74
  const parsed = SttServiceSchema.parse({ provider: "vellum" });
73
- expect(parsed.language).toBeUndefined();
75
+ expect(parsed.language).toBe("multi");
76
+ });
77
+
78
+ test("a chosen language is never replaced by the default", () => {
79
+ expect(
80
+ SttServiceSchema.parse({ provider: "deepgram", language: "ta" }).language,
81
+ ).toBe("ta");
82
+ expect(
83
+ SttServiceSchema.parse({ provider: "deepgram", language: "en" }).language,
84
+ ).toBe("en");
74
85
  });
75
86
 
76
87
  test("language round-trips a code and the multi code-switching mode", () => {
@@ -107,6 +118,10 @@ describe("managed provider", () => {
107
118
  mode: "managed",
108
119
  provider: "vellum",
109
120
  });
110
- expect(parsed).toEqual({ provider: "vellum", providers: {} });
121
+ expect(parsed).toEqual({
122
+ provider: "vellum",
123
+ language: "multi",
124
+ providers: {},
125
+ });
111
126
  });
112
127
  });
@@ -68,6 +68,19 @@ export const TelegramConfigSchema = z
68
68
  .string({ error: "telegram.apiBaseUrl must be a string" })
69
69
  .default("https://api.telegram.org")
70
70
  .describe("Base URL for the Telegram Bot API"),
71
+ // Written by the gateway reconciler, read by the health sweep. Deployment
72
+ // state rather than user configuration: it records where this deployment
73
+ // last pointed Telegram, so the sweep can tell our own registration from a
74
+ // stale tunnel address or another deployment's callback. Unset until a
75
+ // reconciliation succeeds, which the sweep reports as unverified rather
76
+ // than healthy. Stripped by sanitize-for-transfer, like
77
+ // ingress.publicBaseUrlManagedBy, because it does not travel between hosts.
78
+ registeredWebhookUrl: z
79
+ .string({ error: "telegram.registeredWebhookUrl must be a string" })
80
+ .optional()
81
+ .describe(
82
+ "Webhook URL this deployment last registered with Telegram (managed by the gateway)",
83
+ ),
71
84
  deliverAuthBypass: z
72
85
  .boolean({ error: "telegram.deliverAuthBypass must be a boolean" })
73
86
  .default(false)
@@ -2,6 +2,28 @@ import { z } from "zod";
2
2
 
3
3
  export const MemoryRetrospectiveConfigSchema = z
4
4
  .object({
5
+ // This kill switch exists only because conversation forking is expensive:
6
+ // a fork full-copies the source conversation (messages + attachments), so
7
+ // on a large conversation the retrospective's write burst can lock the
8
+ // SQLite table against other writers. Until forking is referential, an
9
+ // operator needs a way to stop retrospectives without turning off memory.
10
+ enabled: z
11
+ .boolean({ error: "memory.retrospective.enabled must be a boolean" })
12
+ .default(true)
13
+ .describe(
14
+ "Whether the memory-retrospective background pass runs. When false, no retrospective is enqueued by any trigger (interval, message count, pre-compaction, scheduled sweep), the scheduled sweep job stops being queued, and any row already queued completes as a no-op. The rest of the memory system (extraction, retrieval, embeddings, `<memory>` injection) is unaffected. Use `memory.enabled` to disable memory as a whole.",
15
+ ),
16
+
17
+ forkStrategy: z
18
+ .enum(["cloning", "reference"], {
19
+ error:
20
+ "memory.retrospective.forkStrategy must be 'cloning' or 'reference'",
21
+ })
22
+ .default("cloning")
23
+ .describe(
24
+ "How a retrospective's fork of the source conversation is materialized. `cloning` (default) full-copies the source conversation's messages and attachments into the fork. `reference` makes the fork referential: it holds only its own messages and reads the source's through `fork_parent_message_id`, so a fork costs a single row instead of a full copy of the conversation.",
25
+ ),
26
+
5
27
  timeThresholdMs: z
6
28
  .number({
7
29
  error: "memory.retrospective.timeThresholdMs must be a number",
@@ -108,3 +130,6 @@ export const MemoryRetrospectiveConfigSchema = z
108
130
  export type MemoryRetrospectiveConfig = z.infer<
109
131
  typeof MemoryRetrospectiveConfigSchema
110
132
  >;
133
+
134
+ /** How a retrospective materializes its fork of the source conversation. */
135
+ export type MemoryForkStrategy = MemoryRetrospectiveConfig["forkStrategy"];
@@ -0,0 +1,55 @@
1
+ import { z } from "zod";
2
+
3
+ /**
4
+ * Unattended plugin upgrade configuration.
5
+ *
6
+ * The resource monitor process runs a periodic sweep (see
7
+ * `monitoring/plugin-auto-update.ts`) that moves every installed, enabled
8
+ * plugin to its source's current revision. The sweep is off by default:
9
+ * `mode: "manual"` means nothing upgrades unless a human runs
10
+ * `assistant plugins upgrade <name>` (or the equivalent route), which is the
11
+ * behavior every workspace had before this block existed. Setting
12
+ * `mode: "auto"` opts the workspace into the hourly sweep.
13
+ */
14
+ export const PluginUpdatesConfigSchema = z
15
+ .object({
16
+ mode: z
17
+ .enum(["manual", "auto"], {
18
+ error: 'pluginUpdates.mode must be "manual" or "auto"',
19
+ })
20
+ .default("manual")
21
+ .describe(
22
+ 'How installed plugins move to newer revisions. "manual" (default) never upgrades on its own — upgrades happen only when a human asks for one. "auto" lets the resource monitor upgrade every installed, enabled plugin on the `checkIntervalMs` cadence.',
23
+ ),
24
+ strategy: z
25
+ // Deliberately narrower than the full `PluginUpgradeStrategy` union: the
26
+ // `assistant` strategy resolves conflicts by writing git conflict
27
+ // markers into the plugin's files and expecting someone to resolve
28
+ // them. Unattended, that would leave a syntactically broken plugin on
29
+ // disk with nobody in the loop, so it is not selectable here. It stays
30
+ // available for interactive `assistant plugins upgrade --strategy
31
+ // assistant`.
32
+ .enum(["theirs", "ours", "overwrite"], {
33
+ error:
34
+ 'pluginUpdates.strategy must be "theirs", "ours", or "overwrite"',
35
+ })
36
+ .default("theirs")
37
+ .describe(
38
+ 'How an automatic upgrade reconciles local edits to a plugin with the incoming revision. "theirs" (default) three-way merges and resolves conflicting hunks toward the incoming revision, keeping non-conflicting local edits; "ours" merges the same way but resolves conflicts toward the local edit; "overwrite" discards local edits and re-installs the revision wholesale. Only consulted when `mode` is "auto".',
39
+ ),
40
+ checkIntervalMs: z
41
+ .number({ error: "pluginUpdates.checkIntervalMs must be a number" })
42
+ .int("pluginUpdates.checkIntervalMs must be an integer")
43
+ .min(300_000, "pluginUpdates.checkIntervalMs must be at least 300000ms")
44
+ .max(
45
+ 604_800_000,
46
+ "pluginUpdates.checkIntervalMs must be <= 604800000ms (7 days)",
47
+ )
48
+ .default(3_600_000)
49
+ .describe(
50
+ 'Minimum interval between automatic upgrade sweeps, in milliseconds (default 1 hour). The last sweep is stamped on disk, so restarting the daemon does not re-run a sweep that already ran within the interval. Only consulted when `mode` is "auto".',
51
+ ),
52
+ })
53
+ .describe("Unattended plugin upgrade configuration");
54
+
55
+ export type PluginUpdatesConfig = z.infer<typeof PluginUpdatesConfigSchema>;
@@ -139,10 +139,14 @@ export const ServicesSchema = z.object({
139
139
  WebSearchServiceSchema.parse({}),
140
140
  ),
141
141
  "web-fetch": WebFetchServiceSchema.default(WebFetchServiceSchema.parse({})),
142
- stt: SttServiceSchema.default({
143
- provider: "deepgram" as const,
144
- providers: {},
145
- }),
142
+ // Parsed rather than handed a literal, like every sibling here: a literal
143
+ // default short-circuits the inner parse, so field-level defaults inside
144
+ // the block (the multilingual `language`) would never materialize for a
145
+ // config that omits `services.stt` entirely, which is every fresh
146
+ // workspace.
147
+ stt: SttServiceSchema.default(
148
+ SttServiceSchema.parse({ provider: "deepgram", providers: {} }),
149
+ ),
146
150
  tts: TtsServiceSchema.default(TtsServiceSchema.parse({})),
147
151
  "google-oauth": GoogleOAuthServiceSchema.default(
148
152
  GoogleOAuthServiceSchema.parse({}),
@@ -67,22 +67,34 @@ export const SttServiceSchema = z
67
67
  .describe("Active STT provider used for speech-to-text transcription"),
68
68
  /**
69
69
  * Spoken-language selection, forwarded to providers whose adapters accept
70
- * a language (Deepgram, xAI, and the managed relay). Left unset the
71
- * provider applies its own default, which for Deepgram (and therefore
72
- * for the managed relay) is English, NOT auto-detection.
70
+ * a language (Deepgram, xAI, and the managed relay).
73
71
  *
74
72
  * `"multi"` selects Deepgram's nova-3 code-switching mode, which follows
75
73
  * a speaker moving between languages inside a single utterance (e.g.
76
74
  * Hinglish). Providers that auto-detect natively and take no language
77
75
  * option (Gemini, Whisper) ignore this field.
76
+ *
77
+ * Defaults to `"multi"` rather than staying unset, so there is no state
78
+ * where the answer to "what language is this assistant listening for"
79
+ * has to be inferred. Every config carries the answer, and the settings
80
+ * surfaces render a real selection rather than a sentinel standing in
81
+ * for one.
82
+ *
83
+ * The default applies on load, not just at creation, so an existing
84
+ * config that never set a language materializes `"multi"` on its next
85
+ * start. That changes no behavior: `effectiveSttLanguage` already
86
+ * resolved unset to `"multi"` on Deepgram and the managed relay, and the
87
+ * providers that detect natively ignore the field either way. It only
88
+ * makes the value explicit. An assistant that has chosen a language
89
+ * keeps it, since a default fills nothing that is already set.
78
90
  */
79
91
  language: z
80
92
  .string({ error: "services.stt.language must be a string" })
81
93
  .trim()
82
94
  .min(1, { error: "services.stt.language must not be empty" })
83
- .optional()
95
+ .default("multi")
84
96
  .describe(
85
- "BCP-47 language code (e.g. 'en-US', 'hi') or 'multi' for code-switching across languages. Unset uses the provider default (English on Deepgram/managed, not auto-detect)",
97
+ "BCP-47 language code (e.g. 'en-US', 'hi') or 'multi' for code-switching across languages. Defaults to 'multi'; providers that detect natively ignore it",
86
98
  ),
87
99
  providers: SttProvidersSchema.default({}),
88
100
  })
@@ -26,7 +26,7 @@ export const TimeoutConfigSchema = z
26
26
  .positive("timeouts.permissionTimeoutSec must be a positive number")
27
27
  .default(300)
28
28
  .describe(
29
- "How long to wait for user permission approval before timing out (seconds)",
29
+ "How long to wait for a human to approve a tool call before timing out (seconds). Spent by both approval paths: the interactive permission prompt, and the inline wait when a contact's sensitive tool call is escalated to the guardian.",
30
30
  ),
31
31
  questionResponseTimeoutSec: z
32
32
  .number({ error: "timeouts.questionResponseTimeoutSec must be a number" })
@@ -44,6 +44,7 @@ import {
44
44
  getGuardianDeliveryFresh,
45
45
  guardianForChannel,
46
46
  invalidateGuardianDeliveryCache,
47
+ warmGuardianBindings,
47
48
  } from "../guardian-delivery-reader.js";
48
49
 
49
50
  const METHOD = "resolve_guardian_delivery";
@@ -299,3 +300,18 @@ describe("selectors", () => {
299
300
  expect(anyGuardian([])).toBeUndefined();
300
301
  });
301
302
  });
303
+
304
+ describe("warmGuardianBindings", () => {
305
+ test("warms both the vellum-channel key and the unfiltered fallback key", async () => {
306
+ const keys: string[] = [];
307
+ await warmGuardianBindings(async (input) => {
308
+ keys.push(input?.channelTypes?.join(",") ?? "ALL");
309
+ return null;
310
+ });
311
+ // Both keys the sync persona resolvers read — peekGuardianForChannel
312
+ // ("vellum") and the peekAnyGuardian() fallback — must be warmed;
313
+ // warming only the channel key leaves the any-channel fallback cold for
314
+ // guardians bound on non-vellum channels.
315
+ expect(keys.sort()).toEqual(["ALL", "vellum"]);
316
+ });
317
+ });
@@ -151,8 +151,8 @@ export async function getGuardianDelivery(input?: {
151
151
  * Returns the fresh cached list for the given channel filter, or `undefined`
152
152
  * when the cache is cold or expired. Used by sync hot paths (SSE subscribe)
153
153
  * that cannot await {@link getGuardianDelivery} but must resolve the SAME
154
- * gateway-owned principal the async paths land on. A cold/expired return lets
155
- * the caller fall back to the local store as before.
154
+ * gateway-owned principal the async paths land on. There is no fallback
155
+ * fetch: on a cold/expired cache the caller proceeds without a principal.
156
156
  */
157
157
  export function peekCachedGuardianDelivery(input?: {
158
158
  channelTypes?: string[];
@@ -181,6 +181,33 @@ export async function getGuardianDeliveryFresh(input?: {
181
181
  });
182
182
  }
183
183
 
184
+ type GuardianDeliveryFetch = (input?: {
185
+ channelTypes?: string[];
186
+ }) => Promise<unknown>;
187
+
188
+ /**
189
+ * Warm both guardian-binding cache keys the sync persona resolvers read: the
190
+ * `"vellum"`-channel key for `peekGuardianForChannel("vellum")` and the
191
+ * unfiltered key for the `peekAnyGuardian()` fallback. Warming only one key
192
+ * still resolves `users/default.md` when the guardian lives on a non-vellum
193
+ * channel (phone / Telegram).
194
+ *
195
+ * Uses the FRESH reader so warmup bypasses a stale/empty cached entry: a
196
+ * gateway-side binding write (onboarding, rebind) does not invalidate the
197
+ * daemon cache, so a non-fresh read could return an empty binding cached
198
+ * before the guardian existed and freeze `users/default.md` until the TTL
199
+ * expires. The fresh reads repopulate the cache the sync `peek*` resolvers
200
+ * then read. Best-effort: the reader swallows failures.
201
+ */
202
+ export async function warmGuardianBindings(
203
+ fetchDelivery: GuardianDeliveryFetch = getGuardianDeliveryFresh,
204
+ ): Promise<void> {
205
+ await Promise.all([
206
+ fetchDelivery({ channelTypes: ["vellum"] }),
207
+ fetchDelivery(),
208
+ ]);
209
+ }
210
+
184
211
  /**
185
212
  * Clear ALL cached guardian deliveries so contact mutations refetch on the next
186
213
  * read. Called by {@link notifyContactsChanged} after a contact write, and
@@ -865,10 +865,10 @@ function resolveTailFloorIndex(messages: Message[], tailIndex: number): number {
865
865
  // Retained-image hydration
866
866
  // ---------------------------------------------------------------------------
867
867
 
868
- function buildRetainedImageBlocks(
868
+ async function buildRetainedImageBlocks(
869
869
  filenames: string[],
870
870
  manifest: ManifestEntry[],
871
- ): { blocks: ImageContent[]; resolved: string[]; missing: string[] } {
871
+ ): Promise<{ blocks: ImageContent[]; resolved: string[]; missing: string[] }> {
872
872
  const blocks: ImageContent[] = [];
873
873
  const resolved: string[] = [];
874
874
  const missing: string[] = [];
@@ -887,7 +887,7 @@ function buildRetainedImageBlocks(
887
887
  // Run the same downscale pass the agent uses when first sending an
888
888
  // image. Without this, attachments that exceed the provider's per-image
889
889
  // byte limit (Anthropic: 5 MB) crash the next turn after compaction.
890
- const optimized = optimizeImageForTransport(
890
+ const optimized = await optimizeImageForTransport(
891
891
  content.toString("base64"),
892
892
  sourceMime,
893
893
  );
@@ -1393,7 +1393,7 @@ export async function runAssistantDrivenCompaction(
1393
1393
  blocks: retainedImageBlocks,
1394
1394
  resolved,
1395
1395
  missing,
1396
- } = buildRetainedImageBlocks(parsed.retainedImageFilenames, manifest);
1396
+ } = await buildRetainedImageBlocks(parsed.retainedImageFilenames, manifest);
1397
1397
  if (missing.length > 0) {
1398
1398
  log.warn(
1399
1399
  { missing },
@@ -37,20 +37,43 @@ export const TRUNCATION_MARKER = "\u2014 full result:";
37
37
  export const TRUNCATION_EXEMPT_TOOLS = new Set<string>(["skill_load"]);
38
38
 
39
39
  /**
40
- * File-read tools that page spooled `.tool-results/` content back into context.
41
- * `file_read` reads inside the workspace; `host_file_read` reads anywhere on the
42
- * host. Both are (a) exempt from the result-time spool pass re-stubbing a read
43
- * of a spooled file would hand back another stub, so the content could never be
44
- * paged back at all — and (b) recognized by {@link derefToolResultReReads}, so a
45
- * re-read of a spooled file collapses to a stub instead of duplicating content
46
- * already in context. Sharing one set keeps the two passes from drifting if a
47
- * new file-read tool is added.
40
+ * File-read tools that can page spooled `.tool-results/` content back into
41
+ * context. `file_read` reads inside the workspace; `host_file_read` reads
42
+ * anywhere on the host. A new file-read tool belongs here so
43
+ * {@link isSpooledToolResultRead} recognizes it.
48
44
  */
49
45
  export const FILE_READ_TOOL_NAMES = new Set<string>([
50
46
  "file_read",
51
47
  "host_file_read",
52
48
  ]);
53
49
 
50
+ /**
51
+ * Whether a tool call is a file read aimed at a spooled `.tool-results/` file.
52
+ *
53
+ * Such a read is the model paging content back in, so both passes treat it
54
+ * specially: the result-time spool pass leaves it alone (re-stubbing it would
55
+ * write a fresh copy and hand back another stub, putting the content out of
56
+ * reach for good), and {@link derefToolResultReReads} collapses it at turn end
57
+ * (its prefix and suffix are already in context above). A file read aimed
58
+ * anywhere else is ordinary tool output and gets no such treatment.
59
+ */
60
+ export function isSpooledToolResultRead(
61
+ toolName: string | undefined,
62
+ input: Record<string, unknown> | undefined,
63
+ ): boolean {
64
+ if (toolName === undefined || !FILE_READ_TOOL_NAMES.has(toolName)) {
65
+ return false;
66
+ }
67
+ const filePath = input?.path ?? input?.file_path;
68
+ if (typeof filePath !== "string") {
69
+ return false;
70
+ }
71
+ // The path in the stub comes from `join`, so it is backslash-separated on
72
+ // Windows. Compare on a normalized form: a missed match would let the spool
73
+ // pass stub this read and put the saved content permanently out of reach.
74
+ return filePath.replace(/\\/g, "/").includes(`/${TOOL_RESULT_DIR}/`);
75
+ }
76
+
54
77
  /**
55
78
  * Build a map of tool_use_id -> originating tool name by walking the tool_use
56
79
  * blocks in assistant messages. A tool_result only carries `tool_use_id`, so
@@ -329,14 +352,7 @@ export function derefToolResultReReads(messages: Message[]): {
329
352
  continue;
330
353
  }
331
354
  const tu = block as ToolUseContent;
332
- if (!FILE_READ_TOOL_NAMES.has(tu.name)) {
333
- continue;
334
- }
335
- const filePath = tu.input.path ?? tu.input.file_path;
336
- if (typeof filePath !== "string") {
337
- continue;
338
- }
339
- if (filePath.includes(`/${TOOL_RESULT_DIR}/`)) {
355
+ if (isSpooledToolResultRead(tu.name, tu.input)) {
340
356
  reReadToolUseIds.add(tu.id);
341
357
  }
342
358
  }
@@ -136,6 +136,48 @@ export function stripSpotlightInjections(messages: Message[]): Message[] {
136
136
  return stripUserTextBlocksByPrefix(messages, [MEMORY_SPOTLIGHT_MATCHER]);
137
137
  }
138
138
 
139
+ /**
140
+ * Whether the TURN-STARTING user message carries a memory-v3
141
+ * `<memory_spotlight>` block.
142
+ *
143
+ * The spotlight is the only injected block that is strip-and-replaced from
144
+ * every user message each turn, so its presence is exactly what makes that
145
+ * message volatile across turns. Every other injected block (turn context,
146
+ * workspace, `<info>`, `<memory>` cards, NOW.md) is frozen into history and
147
+ * re-renders byte-identically, so a message without a spotlight is a stable
148
+ * cache anchor. Providers consume this through the `mutableLatestUserMessage`
149
+ * config field when placing cache breakpoints.
150
+ *
151
+ * The turn start is the most recent user message carrying TEXT content, which
152
+ * is the same message the Anthropic client anchors on. Tool-result messages
153
+ * are skipped: they are user-role but carry no injected blocks, and letting
154
+ * them answer this question would flip the signal to `false` partway through a
155
+ * tool loop. The provider would then mark the very same turn-start block at a
156
+ * different TTL than it did on the turn's first request, billing a second
157
+ * write for one reusable prefix. Anchoring on the turn start keeps the signal
158
+ * constant for every request in the turn.
159
+ *
160
+ * Detection uses {@link MEMORY_SPOTLIGHT_MATCHER}, the same full-wrapper
161
+ * matcher {@link stripSpotlightInjections} strips with, so the two can never
162
+ * disagree about which blocks are ephemeral.
163
+ */
164
+ export function turnStartUserMessageHasSpotlight(messages: Message[]): boolean {
165
+ for (let i = messages.length - 1; i >= 0; i--) {
166
+ const message = messages[i];
167
+ if (message.role !== "user") {
168
+ continue;
169
+ }
170
+ const textBlocks = message.content.filter((block) => block.type === "text");
171
+ if (textBlocks.length === 0) {
172
+ continue;
173
+ }
174
+ return textBlocks.some((block) =>
175
+ textBlockMatchesInjection(block.text, [MEMORY_SPOTLIGHT_MATCHER]),
176
+ );
177
+ }
178
+ return false;
179
+ }
180
+
139
181
  /** `<NOW.md>` scratchpad prefixes (current tag, pre-line-limit variant, legacy `<now_scratchpad>`) — shared with `stripNowScratchpad` so the two strip paths can't drift. */
140
182
  export const NOW_SCRATCHPAD_STRIP_PREFIXES: InjectionMatcher[] = [
141
183
  "<NOW.md Always keep this up to date",
@@ -4,8 +4,8 @@ import { join } from "node:path";
4
4
  import type { ContentBlock, ToolResultContent } from "../providers/types.js";
5
5
  import {
6
6
  buildTruncatedContent,
7
- FILE_READ_TOOL_NAMES,
8
7
  getToolResultFilePath,
8
+ isSpooledToolResultRead,
9
9
  isTruncationEligible,
10
10
  TOOL_RESULT_DIR,
11
11
  } from "./post-turn-tool-result-truncation.js";
@@ -32,27 +32,32 @@ export const RESULT_TIME_SPOOL_EXEMPT_TOOLS = new Set<string>(["web_fetch"]);
32
32
 
33
33
  /**
34
34
  * Whether a tool result is eligible for the result-time spool/stub pass: the
35
- * post-turn pass's shared rules plus the AX-tree exemption, minus the file-read
36
- * tools ({@link FILE_READ_TOOL_NAMES}) and the explicit self-sized reads
37
- * ({@link RESULT_TIME_SPOOL_EXEMPT_TOOLS}). The file-read tools are the model's
38
- * only way to page spooled content back into context: stubbing a read of a
39
- * `.tool-results/` file would spool a fresh copy and hand back another stub, so
40
- * oversized content could never be read at all. Explicit reads are therefore
41
- * honored in full; the post-turn pass still truncates them at turn end, after
42
- * the model has consumed the content.
35
+ * post-turn pass's shared rules plus the AX-tree exemption, minus reads of
36
+ * already-spooled `.tool-results/` files ({@link isSpooledToolResultRead}) and
37
+ * the explicit self-sized reads ({@link RESULT_TIME_SPOOL_EXEMPT_TOOLS}).
38
+ *
39
+ * The spooled-read exemption is what keeps paging possible: a file read is the
40
+ * model's only way to pull spooled content back into context, so stubbing a
41
+ * read of a `.tool-results/` file would write a fresh copy and hand back
42
+ * another stub, putting that content out of reach for good. It is scoped by
43
+ * target path rather than by tool name, because a file read aimed anywhere
44
+ * else is ordinary oversized output with no such circularity, and exempting it
45
+ * keeps a whole file inline on every LLM call for the rest of the turn.
46
+ * Explicit self-sized reads are honored in full; the post-turn pass still
47
+ * truncates them at turn end, after the model has consumed the content.
43
48
  */
44
49
  function isSpoolEligible(
45
50
  tr: ToolResultContent,
46
51
  toolName: string | undefined,
52
+ toolInput: Record<string, unknown> | undefined,
47
53
  ): boolean {
48
54
  if (!isTruncationEligible(tr, toolName)) {
49
55
  return false;
50
56
  }
51
- if (
52
- toolName !== undefined &&
53
- (FILE_READ_TOOL_NAMES.has(toolName) ||
54
- RESULT_TIME_SPOOL_EXEMPT_TOOLS.has(toolName))
55
- ) {
57
+ if (isSpooledToolResultRead(toolName, toolInput)) {
58
+ return false;
59
+ }
60
+ if (toolName !== undefined && RESULT_TIME_SPOOL_EXEMPT_TOOLS.has(toolName)) {
56
61
  return false;
57
62
  }
58
63
  if (tr.content.includes(AX_TREE_TAG)) {
@@ -72,8 +77,8 @@ function isSpoolEligible(
72
77
  * rewriting an earlier message between calls would invalidate the cache from
73
78
  * that point on every iteration. The model still gets the head/tail preview
74
79
  * plus the on-disk path, so it can page the full content back in with
75
- * `file_read` or `host_file_read` (whose results are exempt from this pass)
76
- * when it actually needs it.
80
+ * `file_read` or `host_file_read` (a read of a `.tool-results/` path is exempt
81
+ * from this pass) when it actually needs it.
77
82
  *
78
83
  * Uses the same file paths, stub bytes, and eligibility rules as
79
84
  * `postTurnTruncateToolResults`, whose `TRUNCATION_MARKER` guard then skips
@@ -89,7 +94,15 @@ export function spoolAndStubOversizedToolResults(
89
94
  blocks: ContentBlock[],
90
95
  options: {
91
96
  conversationDir: string;
92
- toolNameById: (toolUseId: string) => string | undefined;
97
+ /**
98
+ * The originating call for a `tool_use_id`. A `tool_result` carries only
99
+ * the id, and eligibility turns on both the tool's name and the path it
100
+ * was pointed at, so the two travel together rather than as separate
101
+ * lookups that could disagree.
102
+ */
103
+ toolCallById: (
104
+ toolUseId: string,
105
+ ) => { name: string; input: Record<string, unknown> } | undefined;
93
106
  },
94
107
  ): number {
95
108
  let stubbedCount = 0;
@@ -98,7 +111,8 @@ export function spoolAndStubOversizedToolResults(
98
111
  if (block.type !== "tool_result") {
99
112
  continue;
100
113
  }
101
- if (!isSpoolEligible(block, options.toolNameById(block.tool_use_id))) {
114
+ const toolCall = options.toolCallById(block.tool_use_id);
115
+ if (!isSpoolEligible(block, toolCall?.name, toolCall?.input)) {
102
116
  continue;
103
117
  }
104
118
 
@@ -176,6 +176,9 @@ export async function startCes(config: AssistantConfig): Promise<void> {
176
176
  },
177
177
  });
178
178
  if (client) {
179
+ log.info(
180
+ "CES client injected into credential resolver at startup; credential reads route through CES RPC",
181
+ );
179
182
  setCesClient(client);
180
183
  } else {
181
184
  // The handshake lost the startup race, so provider init proceeds on the
@@ -9,6 +9,15 @@
9
9
  * delegated to the shared package.
10
10
  */
11
11
 
12
+ import {
13
+ type CesRpcClient,
14
+ type CesRpcClientConfig,
15
+ type CesTransport,
16
+ createCesRpcClient,
17
+ } from "@vellumai/ces-client/rpc-client";
18
+
19
+ import { getLogger } from "../util/logger.js";
20
+
12
21
  export {
13
22
  type CesRpcClient as CesClient,
14
23
  type CesRpcClientConfig as CesClientConfig,
@@ -17,5 +26,17 @@ export {
17
26
  CesHandshakeError,
18
27
  type CesTransport,
19
28
  CesTransportError,
20
- createCesRpcClient as createCesClient,
21
29
  } from "@vellumai/ces-client/rpc-client";
30
+
31
+ const log = getLogger("ces-rpc-client");
32
+
33
+ /**
34
+ * Construct a CES RPC client wired to the assistant logger so handshake-ready
35
+ * and close transitions are visible in the daemon logs.
36
+ */
37
+ export function createCesClient(
38
+ transport: CesTransport,
39
+ config?: CesRpcClientConfig,
40
+ ): CesRpcClient {
41
+ return createCesRpcClient(transport, { logger: log, ...config });
42
+ }