@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
@@ -116,7 +116,10 @@ import {
116
116
  } from "./conversation-runtime-assembly.js";
117
117
  import type { CurrentTurnSurface } from "./conversation-surfaces.js";
118
118
  import { markSurfaceCompleted } from "./conversation-surfaces.js";
119
- import { runDeferredTurnTail } from "./conversation-turn-finalize.js";
119
+ import {
120
+ runDeferredTurnTail,
121
+ settleTurnContent,
122
+ } from "./conversation-turn-finalize.js";
120
123
  import { recordUsage } from "./conversation-usage.js";
121
124
  import { resolveTurnTimezoneContext } from "./date-context.js";
122
125
  import { getDiskPressureStatus } from "./disk-pressure-guard.js";
@@ -133,6 +136,7 @@ import {
133
136
  MEMORY_CONTEXT_PHASE_KEY,
134
137
  TurnLatencyTracker,
135
138
  } from "./turn-latency-tracker.js";
139
+ import { chainTurnTail } from "./turn-tail-chain.js";
136
140
 
137
141
  const log = getLogger("conversation-agent-loop");
138
142
 
@@ -152,6 +156,43 @@ const DISK_PRESSURE_ERROR_CATEGORY = "disk_pressure";
152
156
  const OUT_OF_CREDITS_ASSISTANT_REPLY =
153
157
  "I couldn't reply because you ran out of credits. Add credits in Settings → Billing and we can pick up where we left off.";
154
158
 
159
+ /**
160
+ * Assistant-voice wording persisted as the synthetic assistant row when a turn
161
+ * terminates because the daily credit limit is reached. The shared
162
+ * classification (`dailyLimitClassification` in conversation-error.ts) keeps
163
+ * its `userMessage` in banner voice because it also feeds the live
164
+ * `conversation_error` event the composer renders; here the row re-enters LLM
165
+ * history and is displayed as assistant speech, so first-person copy is
166
+ * accurate. Says "stop" rather than "reply" because the row can land mid-turn,
167
+ * after a tool round-trip the user already saw. Phrased about the past failure,
168
+ * not the current limit, because the row stays in the transcript (and renders
169
+ * as a plain bubble) after the limit resets.
170
+ */
171
+ const DAILY_LIMIT_REACHED_ASSISTANT_REPLY =
172
+ "I had to stop because you hit your daily credit limit. Raise the limit in Settings → Billing and we can pick up where we left off, or I can continue once it resets.";
173
+
174
+ /**
175
+ * The assistant-voice text a managed-billing failure persists in place of the
176
+ * classification's own `userMessage`, or `null` when the classification copy is
177
+ * already right for a transcript row.
178
+ */
179
+ function managedBillingAssistantReply(
180
+ providerErrorCode: string | null,
181
+ providerErrorCategory: string | null,
182
+ ): string | null {
183
+ if (providerErrorCode !== "PROVIDER_BILLING") {
184
+ return null;
185
+ }
186
+ switch (providerErrorCategory) {
187
+ case "credits_exhausted":
188
+ return OUT_OF_CREDITS_ASSISTANT_REPLY;
189
+ case "daily_limit_reached":
190
+ return DAILY_LIMIT_REACHED_ASSISTANT_REPLY;
191
+ default:
192
+ return null;
193
+ }
194
+ }
195
+
155
196
  /** Title-cased friendly labels for tool names, used in confirmation chips. */
156
197
  const TOOL_FRIENDLY_LABEL: Record<string, string> = {
157
198
  bash: "Run Command",
@@ -325,6 +366,13 @@ export async function runAgentLoopImpl(
325
366
  * scheduled execute turn attributes its LLM spend to that firing.
326
367
  */
327
368
  cronRunId?: string | null;
369
+ /**
370
+ * Trust this turn runs under. Queue drains pass the trust captured from
371
+ * the sender at enqueue; without it the initialization below would reset
372
+ * the turn to the conversation slot, which holds whichever actor sent
373
+ * most recently rather than the one this turn belongs to.
374
+ */
375
+ turnTrustContext?: TrustContext;
328
376
  },
329
377
  ): Promise<void> {
330
378
  if (!ctx.abortController) {
@@ -335,9 +383,21 @@ export async function runAgentLoopImpl(
335
383
  // voice-session-bridge, regenerate, etc.) that invoke runAgentLoop directly
336
384
  // without going through processMessage/drainQueue. This ensures the system
337
385
  // prompt callback always reads a valid snapshot rather than undefined.
338
- // processMessage/drainQueue set these fields before calling runAgentLoop;
339
- // those existing assignments remain correct and are merely redundant here.
340
- ctx.currentTurnTrustContext = ctx.trustContext;
386
+ //
387
+ // The invariant: a turn runs under the trust captured when that turn
388
+ // started, never under whatever the slot holds when the loop happens to
389
+ // open. Awaits sit between capture and this line, and the slot is writable
390
+ // throughout by paths that do not own the turn (channel ingress for another
391
+ // actor, live-voice hydration, pointer elevation, the voice bridge).
392
+ //
393
+ // Callers that own a turn are expected to capture at turn start and pass
394
+ // `turnTrustContext`. This has not been swept across all 13 call sites, so
395
+ // the fallback below is load-bearing for two different populations: callers
396
+ // that legitimately have no turn to capture (subagent manager, voice
397
+ // bridge, regenerate), and callers that should pass it and do not yet.
398
+ // Treat a caller reaching the fallback as unverified, not as correct.
399
+ // LUM-3148 removes the ambiguity by making trust ride the turn.
400
+ ctx.currentTurnTrustContext = options?.turnTrustContext ?? ctx.trustContext;
341
401
  ctx.currentTurnChannelCapabilities = ctx.channelCapabilities;
342
402
 
343
403
  // Re-resolve the system prompt under the snapshots just set and push it into
@@ -560,6 +620,11 @@ export async function runAgentLoopImpl(
560
620
  // applies to later tool executions and nested subagents in the same turn.
561
621
  ctx.currentTurnOverrideProfile = turnOverrideProfile;
562
622
 
623
+ // Mirrored onto the live conversation for `createToolExecutor` to read into
624
+ // `ToolContext.cronRunId`, so a tool that delegates LLM work (subagent spawn
625
+ // or message) stamps the delegated usage with this firing.
626
+ ctx.currentTurnCronRunId = turnCronRunId;
627
+
563
628
  // Capture the turn channel context *before* any awaits so a second
564
629
  // message from a different channel can't overwrite it mid-flight.
565
630
  // When context is unavailable (e.g. regenerate after daemon restart),
@@ -637,6 +702,7 @@ export async function runAgentLoopImpl(
637
702
  );
638
703
  ctx.diskPressureCleanupModeActive =
639
704
  diskPressureDecision.action === "allow-cleanup-mode";
705
+ const toolsDisabledForTurn = ctx.toolsDisabledDepth > 0;
640
706
 
641
707
  ctx.lastAssistantAttachments = [];
642
708
  ctx.lastAttachmentWarnings = [];
@@ -659,14 +725,106 @@ export async function runAgentLoopImpl(
659
725
  | { outcome: "failed" | "cancelled"; failureCode?: string }
660
726
  | undefined;
661
727
  // True once a replied terminal SSE (message_complete / generation_handoff)
662
- // has been emitted. Guards the catch block: an error thrown afterwards
663
- // (deferred turn-tail bookkeeping) must not relabel a visibly-replied turn.
728
+ // has been emitted. Guards the catch block: an error thrown by the
729
+ // content-settle steps that run after it must not relabel a visibly-replied
730
+ // turn.
664
731
  let turnReplied = false;
665
732
  // Narrower than `turnReplied`: true only for a `message_complete` branch that
666
733
  // produced a genuine reply. A handed-off generation continues the run, and a
667
734
  // provider-error turn's only assistant row is the synthetic error text, so
668
735
  // the deferred tail must not treat either as a final reply.
669
736
  let turnCompleted = false;
737
+ // True once `releaseTurn` has run. The happy path releases as soon as the
738
+ // turn's content is settled; the `finally` calls it again as the backstop for
739
+ // the cancel/error paths that never reached the early release.
740
+ let turnReleased = false;
741
+
742
+ /**
743
+ * Free the conversation for its next turn.
744
+ *
745
+ * Ordering is load-bearing and identical on both call paths: stamp the
746
+ * turn's abnormal outcome first, because the telemetry reporter's
747
+ * settled-turn barrier only releases this turn once processing stops; null
748
+ * `abortController` before clearing the flag so a concurrent abort takes the
749
+ * force-clear branch rather than signalling a dead controller; unregister the
750
+ * in-flight turn only after processing stops, since daemon-side callers fall
751
+ * back to the live seq counter once that registration is gone (safe because
752
+ * `settlePendingPartialFlush` has already flushed the turn's content).
753
+ * Idempotent, so the `finally` can call it unconditionally.
754
+ *
755
+ * The release frees direct sends only. Queued messages are drained from the
756
+ * loop's `finally`, after the turn-boundary commit, so a dequeued turn's
757
+ * first file writes cannot be swept into the finished turn's commit.
758
+ */
759
+ const releaseTurn = (): void => {
760
+ if (turnReleased) {
761
+ return;
762
+ }
763
+ turnReleased = true;
764
+ if (abnormalOutcome) {
765
+ try {
766
+ stampTurnOutcome(userMessageId, abnormalOutcome.outcome, {
767
+ failureCode: abnormalOutcome.failureCode,
768
+ });
769
+ } catch (err) {
770
+ rlog.warn(
771
+ { err },
772
+ "Failed to stamp turn outcome (non-fatal); releasing the turn anyway",
773
+ );
774
+ }
775
+ }
776
+ ctx.abortController = null;
777
+ ctx.setProcessing(false);
778
+ unregisterInflightTurn(ctx.conversationId, state);
779
+
780
+ // Tear down the remaining per-turn state in the same synchronous step as
781
+ // the release, so a turn that starts against the freed conversation cannot
782
+ // inherit stale turn scope (task-run permissions, allowed tools, override
783
+ // profile, command intent) or observe a half-cleared conversation.
784
+ ctx.onConfirmationOutcome = undefined;
785
+ ctx.surfaceActionRequestIds.delete(ctx.currentRequestId ?? "");
786
+ ctx.approvedViaPromptThisTurn = false;
787
+ ctx.currentRequestId = undefined;
788
+ ctx.currentActiveSurfaceId = undefined;
789
+ ctx.allowedToolNames = undefined;
790
+ ctx.diskPressureCleanupModeActive = false;
791
+ ctx.preactivatedSkillIds = undefined;
792
+ ctx.currentTurnOverrideProfile = undefined;
793
+ ctx.currentTurnCronRunId = undefined;
794
+ ctx.currentTurnModelProfileNoticeKey = undefined;
795
+ // Turn-scoped interactivity. Clear it so paths that bypass this loop
796
+ // (e.g. opportunity wakes calling `agentLoop.run` directly) don't inherit
797
+ // a stale value and instead fall back to live client state in the tool
798
+ // context.
799
+ ctx.currentTurnIsNonInteractive = undefined;
800
+ // Turn-scoped request origin. Clear so a later turn on a reused
801
+ // conversation cannot inherit a stale origin-scoped permission grant.
802
+ ctx.currentTurnRequestOrigin = undefined;
803
+ // Channel command intents (e.g. Telegram /start) are single-turn
804
+ // metadata. Clear at turn end so they never leak into subsequent
805
+ // unrelated messages.
806
+ ctx.commandIntent = undefined;
807
+ // The app on screen is view state owned by the message that reported it,
808
+ // not durable conversation state: nothing tells the daemon when the user
809
+ // closes an app. Clear both copies at turn end so a later turn that brings
810
+ // no client transport (a scheduled wake, a background follow-up) cannot
811
+ // claim the user is looking at an app they may have closed. Every inbound
812
+ // send re-applies it from transport before its own turn, including the
813
+ // queue drain, so live turns are unaffected.
814
+ ctx.visibleAppId = undefined;
815
+ ctx.currentTurnVisibleAppId = undefined;
816
+ // taskRunId scopes ephemeral task-run permissions to a single turn. Clear
817
+ // before the queue drain so queued/drained turns on a reused conversation
818
+ // cannot inherit stale in-task-run scope from the turn that just finished.
819
+ ctx.taskRunId = undefined;
820
+
821
+ // Close the turn's profiling window here rather than after the queue
822
+ // drain: the profiler is per-conversation and the next turn's
823
+ // `startToolProfilingRequest` resets it, so a summary emitted after a
824
+ // drained turn started would report that turn's window under this turn's
825
+ // request id.
826
+ emitToolProfilingSummary(ctx.conversationId, reqId);
827
+ };
670
828
 
671
829
  const publishLoopMessagesChanged = (): void => {
672
830
  if (
@@ -717,14 +875,20 @@ export async function runAgentLoopImpl(
717
875
  return;
718
876
  }
719
877
 
720
- // Ensure workspace git repo is initialized before any tools run.
721
- try {
722
- const getWorkspaceGitServiceFn =
723
- ctx.getWorkspaceGitService ?? getWorkspaceGitService;
724
- const gitService = getWorkspaceGitServiceFn(ctx.workingDir);
725
- await gitService.ensureInitialized();
726
- } catch (err) {
727
- rlog.warn({ err }, "Failed to initialize workspace git repo (non-fatal)");
878
+ // Workspace Git readiness is required only when tools can run. Tool-less
879
+ // callers use the same depth gate consumed by tool resolution.
880
+ if (!toolsDisabledForTurn) {
881
+ try {
882
+ const getWorkspaceGitServiceFn =
883
+ ctx.getWorkspaceGitService ?? getWorkspaceGitService;
884
+ const gitService = getWorkspaceGitServiceFn(ctx.workingDir);
885
+ await gitService.ensureInitialized();
886
+ } catch (err) {
887
+ rlog.warn(
888
+ { err },
889
+ "Failed to initialize workspace git repo (non-fatal)",
890
+ );
891
+ }
728
892
  }
729
893
 
730
894
  // Auto-complete stale interactive surfaces from previous turns.
@@ -737,13 +901,20 @@ export async function runAgentLoopImpl(
737
901
  if (entry.surfaceType === "dynamic_page") {
738
902
  continue;
739
903
  }
904
+ // Persist before announcing: a client told the card was dismissed
905
+ // while the write failed would watch the next reseed revert it.
906
+ // An unannounced dismissal keeps its pending entry so client and
907
+ // daemon agree the card is still live and the one-interactive-surface
908
+ // gate keeps holding; the next user message sweeps it again.
909
+ if (!markSurfaceCompleted(ctx, surfaceId, "Dismissed")) {
910
+ continue;
911
+ }
740
912
  onEvent({
741
913
  type: "ui_surface_complete",
742
914
  conversationId: ctx.conversationId,
743
915
  surfaceId,
744
916
  summary: "Dismissed",
745
917
  });
746
- markSurfaceCompleted(ctx, surfaceId, "Dismissed");
747
918
  ctx.pendingSurfaceActions.delete(surfaceId);
748
919
  }
749
920
  }
@@ -1341,9 +1512,16 @@ export async function runAgentLoopImpl(
1341
1512
  return { ...msg, content: cleanedBlocks };
1342
1513
  });
1343
1514
 
1344
- const hasAssistantResponse = newMessages.some(
1345
- (msg) => msg.role === "assistant",
1346
- );
1515
+ // "The run delivered a final assistant reply", not "the run produced any
1516
+ // assistant-role message". A run that called tools already carries
1517
+ // assistant `tool_use` messages, and those are not a delivered reply: a
1518
+ // provider error that kills the run mid-turn leaves the tail as the
1519
+ // tool_result (user role), while a run that replied normally ends on the
1520
+ // assistant message. Testing only the tail keeps the synthetic error row
1521
+ // below reachable for mid-turn failures, which is the only durable record
1522
+ // of why the turn stopped.
1523
+ const hasAssistantResponse =
1524
+ newMessages[newMessages.length - 1]?.role === "assistant";
1347
1525
  if (
1348
1526
  !hasAssistantResponse &&
1349
1527
  state.providerErrorUserMessage &&
@@ -1403,13 +1581,13 @@ export async function runAgentLoopImpl(
1403
1581
  providerErrorCategory: state.providerErrorCategory ?? undefined,
1404
1582
  };
1405
1583
  // The persisted row re-enters LLM history and is displayed as
1406
- // assistant speech, so managed-credits exhaustion swaps the
1407
- // context-neutral classification copy for assistant-voice wording.
1584
+ // assistant speech, so the managed-billing categories swap the
1585
+ // banner-voice classification copy for assistant-voice wording.
1408
1586
  const persistedErrorText =
1409
- state.providerErrorCode === "PROVIDER_BILLING" &&
1410
- state.providerErrorCategory === "credits_exhausted"
1411
- ? OUT_OF_CREDITS_ASSISTANT_REPLY
1412
- : state.providerErrorUserMessage;
1587
+ managedBillingAssistantReply(
1588
+ state.providerErrorCode,
1589
+ state.providerErrorCategory,
1590
+ ) ?? state.providerErrorUserMessage;
1413
1591
  const errorAssistantMessage =
1414
1592
  createAssistantMessage(persistedErrorText);
1415
1593
  const errorRow = await addMessage(
@@ -1471,10 +1649,10 @@ export async function runAgentLoopImpl(
1471
1649
  // compaction/overflow recovery (where a cache miss is expected).
1472
1650
  //
1473
1651
  // Post-turn tool-result truncation (spooling large results to disk and
1474
- // shrinking the next turn's context) is deferred to `runDeferredTurnTail`
1652
+ // shrinking the next turn's context) is deferred to `settleTurnContent`
1475
1653
  // below. It only rewrites the in-memory history the NEXT turn is built
1476
- // from never the just-delivered reply so it must not sit on the
1477
- // critical path to the terminal SSE that re-enables the composer.
1654
+ // from, never the just-delivered reply, so it must not sit on the critical
1655
+ // path to the terminal SSE that re-enables the composer.
1478
1656
  ctx.messages = restoredHistory;
1479
1657
 
1480
1658
  emitUsage(
@@ -1504,7 +1682,7 @@ export async function runAgentLoopImpl(
1504
1682
  // Fast-path: when the user cancelled, skip expensive post-loop work
1505
1683
  // (attachment resolution) and emit the cancellation event immediately
1506
1684
  // so the client can re-enable the UI without delay. Disk sync and the rest
1507
- // of the bookkeeping run in `runDeferredTurnTail` after this SSE.
1685
+ // of the bookkeeping run after this SSE.
1508
1686
  if (abortController.signal.aborted) {
1509
1687
  abnormalOutcome = { outcome: "cancelled" };
1510
1688
  ctx.emitActivityState("idle", "generation_cancelled", {
@@ -1600,24 +1778,42 @@ export async function runAgentLoopImpl(
1600
1778
 
1601
1779
  // The terminal SSE for this turn has now been emitted (message_complete,
1602
1780
  // generation_handoff, or generation_cancelled), so the composer is already
1603
- // re-enabling. Settle any pending debounced partial flush FIRST a
1781
+ // re-enabling. Settle any pending debounced partial flush FIRST: a
1604
1782
  // cancelled turn exits with the timer still pending, and a flush firing
1605
- // after the tail's stranded fold (or the voice bridge's transcript
1606
- // hygiene) would write raw content into an already-settled row. Then
1607
- // drain the deferred bookkeeping after the SSE, before the `finally`
1608
- // commits and drains the queue for the next turn.
1783
+ // after the stranded fold (or the voice bridge's transcript hygiene) would
1784
+ // write raw content into an already-settled row. Then settle the rest of
1785
+ // the turn's content, which rewrites state the next turn also writes and so
1786
+ // has to complete under the processing lock.
1609
1787
  await settlePendingPartialFlush(state, deps);
1610
- await runDeferredTurnTail({
1611
- ctx,
1612
- state,
1613
- rlog,
1614
- generationCompletedAt,
1615
- turnCompleted,
1616
- userMessageId: options?.notifyUserMessageId ?? userMessageId,
1617
- ...(options?.replyDeliveredInAppOnly
1618
- ? { replyDeliveredInAppOnly: true }
1619
- : {}),
1620
- });
1788
+ await settleTurnContent({ ctx, state, rlog });
1789
+
1790
+ // Content is settled, so the conversation is free. Release before the
1791
+ // deferred tail rather than in the `finally`: the tail's memory indexing is
1792
+ // network-bound and unbounded, and holding the lock across it is what makes
1793
+ // a fully-delivered reply still answer a follow-up send with a queue slot.
1794
+ // The queue itself is drained later, from the `finally` after the
1795
+ // turn-boundary commit.
1796
+ const criticalSectionMs = Date.now() - generationCompletedAt;
1797
+ releaseTurn();
1798
+
1799
+ // Detached, and chained on the conversation's tail chain so two turns'
1800
+ // tails run in turn order instead of concurrently. The chain is keyed by
1801
+ // conversation id rather than held on this instance, because the tail runs
1802
+ // while the conversation reads idle and can therefore outlive the instance
1803
+ // that scheduled it.
1804
+ chainTurnTail(ctx.conversationId, () =>
1805
+ runDeferredTurnTail({
1806
+ conversationId: ctx.conversationId,
1807
+ state,
1808
+ rlog,
1809
+ criticalSectionMs,
1810
+ turnCompleted,
1811
+ userMessageId: options?.notifyUserMessageId ?? userMessageId,
1812
+ ...(options?.replyDeliveredInAppOnly
1813
+ ? { replyDeliveredInAppOnly: true }
1814
+ : {}),
1815
+ }),
1816
+ );
1621
1817
  } catch (err) {
1622
1818
  clearConversationNotices(ctx.conversationId);
1623
1819
  const errorCtx = {
@@ -1625,9 +1821,9 @@ export async function runAgentLoopImpl(
1625
1821
  aborted: abortController.signal.aborted,
1626
1822
  };
1627
1823
  if (isUserCancellation(err, errorCtx)) {
1628
- // Only label the turn when it hadn't already replied a cancellation
1629
- // surfacing after the terminal SSE (deferred turn-tail bookkeeping)
1630
- // must not relabel a visibly-replied turn.
1824
+ // Only label the turn when it hadn't already replied: a cancellation
1825
+ // surfacing after the terminal SSE (the content-settle steps) must not
1826
+ // relabel a visibly-replied turn.
1631
1827
  if (!turnReplied) {
1632
1828
  abnormalOutcome = { outcome: "cancelled" };
1633
1829
  }
@@ -1665,112 +1861,86 @@ export async function runAgentLoopImpl(
1665
1861
  publishLoopMessagesChanged();
1666
1862
  }
1667
1863
  } finally {
1668
- // Clear the processing flag first. It is the release that frees the
1669
- // conversation for its next turn, so nothing that can throw (the
1670
- // turn-boundary commit, profiling) is allowed to run ahead of it and leave
1671
- // the row latched "mid-turn". Stamp the turn's abnormal outcome first
1672
- // because the telemetry reporter's settled-turn barrier only releases this
1673
- // turn once processing stops; null `abortController` first so a concurrent
1674
- // abort takes the force-clear branch rather than signalling a dead one.
1675
- if (abnormalOutcome) {
1676
- stampTurnOutcome(userMessageId, abnormalOutcome.outcome, {
1677
- failureCode: abnormalOutcome.failureCode,
1678
- });
1679
- }
1680
- ctx.abortController = null;
1681
- ctx.setProcessing(false);
1682
- // The turn's content is fully flushed by here (`settlePendingPartialFlush`
1683
- // ran inside the try), so daemon-side callers can safely fall back to the
1684
- // live seq counter once this registration is gone.
1685
- unregisterInflightTurn(ctx.conversationId, state);
1864
+ // Backstop release for the cancel/error paths, which throw out of the try
1865
+ // before the happy path's release runs. Idempotent, so the happy path
1866
+ // reaches here already released. It stays first in the `finally` so the
1867
+ // turn-boundary commit below cannot throw ahead of it and leave the row
1868
+ // latched "mid-turn".
1869
+ releaseTurn();
1870
+
1871
+ // The queue drain runs after the turn-boundary commit, under a `finally`
1872
+ // so a throw in the commit section cannot strand the queue with nothing
1873
+ // left to re-trigger it. Ordering is load-bearing: `commitTurnChanges`
1874
+ // attributes the working tree's changes to the finished turn, so a queued
1875
+ // turn that started while it was preparing would have its first file
1876
+ // writes swept into the wrong commit. The wait is deadline-bounded, and
1877
+ // direct sends are already unblocked by the release above.
1878
+ try {
1879
+ if (turnStarted) {
1880
+ ctx.turnCount++;
1881
+ const runTurnCommit = async (): Promise<void> => {
1882
+ const config = getConfig();
1883
+ const maxWait = config.workspaceGit?.turnCommitMaxWaitMs ?? 4000;
1884
+ const deadlineMs = Date.now() + maxWait;
1885
+
1886
+ const commitTurnChangesFn =
1887
+ ctx.commitTurnChanges ?? commitTurnChanges;
1888
+ const commitPromise = commitTurnChangesFn(
1889
+ ctx.workingDir,
1890
+ ctx.conversationId,
1891
+ ctx.turnCount,
1892
+ undefined,
1893
+ deadlineMs,
1894
+ );
1895
+ const outcome = await raceWithTimeout(commitPromise, maxWait);
1896
+ if (outcome === "timed_out") {
1897
+ rlog.warn(
1898
+ {
1899
+ turnNumber: ctx.turnCount,
1900
+ maxWaitMs: maxWait,
1901
+ conversationId: ctx.conversationId,
1902
+ },
1903
+ "Turn-boundary commit timed out; continuing without waiting (commit still runs in background)",
1904
+ );
1905
+ }
1906
+ };
1686
1907
 
1687
- if (turnStarted) {
1688
- ctx.turnCount++;
1689
- const config = getConfig();
1690
- const maxWait = config.workspaceGit?.turnCommitMaxWaitMs ?? 4000;
1691
- const deadlineMs = Date.now() + maxWait;
1908
+ if (toolsDisabledForTurn) {
1909
+ // Let the caller and the queue drain below claim an immediate
1910
+ // follow-up turn before starting Git. That turn will commit the
1911
+ // accumulated disk view.
1912
+ setImmediate(() => {
1913
+ if (ctx.isProcessing()) {
1914
+ return;
1915
+ }
1916
+ void runTurnCommit().catch((err) => {
1917
+ rlog.warn({ err }, "Deferred turn-boundary commit failed");
1918
+ });
1919
+ });
1920
+ } else {
1921
+ await runTurnCommit();
1922
+ }
1692
1923
 
1693
- const commitTurnChangesFn = ctx.commitTurnChanges ?? commitTurnChanges;
1694
- const commitPromise = commitTurnChangesFn(
1695
- ctx.workingDir,
1696
- ctx.conversationId,
1697
- ctx.turnCount,
1698
- undefined,
1699
- deadlineMs,
1700
- );
1701
- const outcome = await raceWithTimeout(commitPromise, maxWait);
1702
- if (outcome === "timed_out") {
1703
- rlog.warn(
1704
- {
1705
- turnNumber: ctx.turnCount,
1706
- maxWaitMs: maxWait,
1707
- conversationId: ctx.conversationId,
1708
- },
1709
- "Turn-boundary commit timed out — continuing without waiting (commit still runs in background)",
1710
- );
1924
+ // Recompute relationship-state.json at turn boundary (fire-and-forget).
1925
+ // The writer swallows its own errors, but we still guard with catch()
1926
+ // here so a regression in the writer can never bubble out of the
1927
+ // agent loop and reject an otherwise-complete turn.
1928
+ void writeRelationshipState().catch(() => {});
1711
1929
  }
1712
1930
 
1713
- // Recompute relationship-state.json at turn boundary (fire-and-forget).
1714
- // The writer swallows its own errors, but we still guard with catch()
1715
- // here so a regression in the writer can never bubble out of the
1716
- // agent loop and reject an otherwise-complete turn.
1717
- void writeRelationshipState().catch(() => {});
1931
+ // Consolidation deferred to compaction: keeping assistant + tool_result
1932
+ // messages unconsolidated preserves the exact message structure sent to
1933
+ // the API, enabling stable prefix caching across turns. Compaction
1934
+ // consolidates when it summarizes old messages (cache miss is expected).
1935
+ } finally {
1936
+ // kickDrainQueue never rejects: a drain failure here would otherwise be
1937
+ // an unhandled rejection that strands the queue with nothing left to
1938
+ // re-trigger it.
1939
+ void ctx.kickDrainQueue(
1940
+ yieldedForHandoff ? "checkpoint_handoff" : "loop_complete",
1941
+ "agent_loop_finally",
1942
+ );
1718
1943
  }
1719
-
1720
- emitToolProfilingSummary(ctx.conversationId, reqId);
1721
-
1722
- // Tear down the remaining per-turn state. Abort reliably drives the loop to
1723
- // this `finally` within a bounded time — cooperative signal propagation
1724
- // (provider fetch + tool race) backed by the abort watchdog — so a
1725
- // cancelled turn always unwinds before any resend can start a new one.
1726
- // There is therefore only ever one turn alive, and clearing the shared
1727
- // state below cannot clobber a concurrent turn.
1728
- ctx.onConfirmationOutcome = undefined;
1729
- ctx.surfaceActionRequestIds.delete(ctx.currentRequestId ?? "");
1730
- ctx.approvedViaPromptThisTurn = false;
1731
- ctx.currentRequestId = undefined;
1732
- ctx.currentActiveSurfaceId = undefined;
1733
- ctx.allowedToolNames = undefined;
1734
- ctx.diskPressureCleanupModeActive = false;
1735
- ctx.preactivatedSkillIds = undefined;
1736
- ctx.currentTurnOverrideProfile = undefined;
1737
- ctx.currentTurnModelProfileNoticeKey = undefined;
1738
- // Turn-scoped interactivity. Clear it so paths that bypass this loop (e.g.
1739
- // opportunity wakes calling `agentLoop.run` directly) don't inherit a stale
1740
- // value and instead fall back to live client state in the tool context.
1741
- ctx.currentTurnIsNonInteractive = undefined;
1742
- // Turn-scoped request origin. Clear so a later turn on a reused
1743
- // conversation cannot inherit a stale origin-scoped permission grant.
1744
- ctx.currentTurnRequestOrigin = undefined;
1745
- // Channel command intents (e.g. Telegram /start) are single-turn metadata.
1746
- // Clear at turn end so they never leak into subsequent unrelated messages.
1747
- ctx.commandIntent = undefined;
1748
- // The app on screen is view state owned by the message that reported it,
1749
- // not durable conversation state: nothing tells the daemon when the user
1750
- // closes an app. Clear both copies at turn end so a later turn that brings
1751
- // no client transport (a scheduled wake, a background follow-up) cannot
1752
- // claim the user is looking at an app they may have closed. Every inbound
1753
- // send re-applies it from transport before its own turn, including the
1754
- // queue drain, so live turns are unaffected.
1755
- ctx.visibleAppId = undefined;
1756
- ctx.currentTurnVisibleAppId = undefined;
1757
- // taskRunId scopes ephemeral task-run permissions to a single turn. Clear
1758
- // before drainQueue so queued/drained turns on a reused conversation can't
1759
- // inherit stale in-task-run scope from the turn that just finished.
1760
- ctx.taskRunId = undefined;
1761
-
1762
- // Consolidation deferred to compaction: keeping assistant + tool_result
1763
- // messages unconsolidated preserves the exact message structure sent to
1764
- // the API, enabling stable prefix caching across turns. Compaction
1765
- // consolidates when it summarizes old messages (cache miss is expected).
1766
-
1767
- // kickDrainQueue never rejects — a drain failure here would otherwise be
1768
- // an unhandled rejection that strands the queue with nothing left to
1769
- // re-trigger it.
1770
- void ctx.kickDrainQueue(
1771
- yieldedForHandoff ? "checkpoint_handoff" : "loop_complete",
1772
- "agent_loop_finally",
1773
- );
1774
1944
  }
1775
1945
  }
1776
1946
 
@@ -163,7 +163,7 @@ export async function resolveAssistantAttachments(
163
163
  const draft = assistantAttachments[i];
164
164
  let stored;
165
165
  try {
166
- stored = attachInlineAttachmentToMessage(
166
+ stored = await attachInlineAttachmentToMessage(
167
167
  lastAssistantMessageId,
168
168
  i,
169
169
  draft.filename,
@@ -6,6 +6,7 @@ import type {
6
6
  ConversationErrorCode,
7
7
  ConversationErrorEvent,
8
8
  } from "../api/events/conversation-error.js";
9
+ import { getIsPlatform } from "../config/env-registry.js";
9
10
  import {
10
11
  isImageDimensionsTooLargeError,
11
12
  isImageMediaTypeMismatchError,
@@ -352,7 +353,7 @@ function connectionResolutionUserMessage(
352
353
  case "provider_mismatch":
353
354
  return `${connection}${usedBy} is bound to a different provider than the profile declares. Update the profile's connection in ${fixPath}.`;
354
355
  case "missing_connection":
355
- return `No provider connection is configured${usedBy}. Add an API key or log in via ${fixPath}.`;
356
+ return `No provider connection is configured${usedBy}. Ask me to set one up right here, or add an API key in ${fixPath}.`;
356
357
  case "unroutable_managed_model":
357
358
  return `The model "${error.model ?? "<unset>"}"${usedBy} isn't served by the Vellum managed route. Pick a model from the Vellum catalog, or choose a concrete provider in ${fixPath}.`;
358
359
  case "missing_credential":
@@ -361,6 +362,12 @@ function connectionResolutionUserMessage(
361
362
  // doesn't.
362
363
  return `${connection}${usedBy} has no stored credential. Add an API key or reconnect it in ${fixPath}.`;
363
364
  case "platform_unauthenticated":
365
+ // A platform-managed assistant cannot log in or switch providers, and
366
+ // this classifier only sees the reason code, so one honest wording covers
367
+ // both the transient and the re-provision cases.
368
+ if (getIsPlatform()) {
369
+ return `${connection}${usedBy} is unavailable. If this persists, the platform credential may need to be re-provisioned on the Vellum platform.`;
370
+ }
364
371
  return `${connection}${usedBy} requires a Vellum platform login. Log in, or pick a different provider in ${fixPath}.`;
365
372
  case "model_incompatible":
366
373
  return `${error.model ? `Model "${error.model}"` : "The requested model"} isn't available on ${connectionName ? `connection "${connectionName}"` : "the configured connection"}${usedBy}. Pick a different model or connection in ${fixPath}.`;