@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
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vellumai/assistant",
3
- "version": "0.11.2",
3
+ "version": "0.11.3-staging.2",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "exports": {
@@ -115,9 +115,9 @@ describe("upscaleTargetDimensions", () => {
115
115
  });
116
116
 
117
117
  describe("upscaleImageToMinimum", () => {
118
- it("returns null when dimensions are unparseable", () => {
118
+ it("returns null when dimensions are unparseable", async () => {
119
119
  expect(
120
- upscaleImageToMinimum(
120
+ await upscaleImageToMinimum(
121
121
  Buffer.from("not an image").toString("base64"),
122
122
  "image/png",
123
123
  ),
@@ -126,12 +126,12 @@ describe("upscaleImageToMinimum", () => {
126
126
  });
127
127
 
128
128
  describe("optimizeImageForTransport", () => {
129
- it("passes an undersized image through unchanged the minimum floor is rejection-path only", () => {
129
+ it("passes an undersized image through unchanged: the minimum floor is rejection-path only", async () => {
130
130
  // The floor is undocumented provider behavior, so pre-send transport
131
131
  // never enforces it; only the image-recovery plugin reacts to an actual
132
132
  // "Could not process image" rejection.
133
133
  const tiny = makePngBase64(16, 14);
134
- expect(optimizeImageForTransport(tiny, "image/png")).toEqual({
134
+ expect(await optimizeImageForTransport(tiny, "image/png")).toEqual({
135
135
  data: tiny,
136
136
  mediaType: "image/png",
137
137
  });
@@ -1,36 +1,22 @@
1
1
  /**
2
- * Verifies that the memory-v3-live cache-anchor signal surfaces on every
3
- * `SendMessageOptions.config` the loop emits. When v3-live is on, the latest
4
- * user message carries a volatile `<memory>` block, so the loop sets
5
- * `providerConfig.mutableLatestUserMessage` to tell the provider to anchor its
6
- * long-TTL cache breakpoint on the most recent STABLE message instead.
2
+ * Verifies how the loop derives the `mutableLatestUserMessage` cache-anchor
3
+ * signal on `SendMessageOptions.config`.
7
4
  *
8
- * The loop reads `config.memory.v3.live` directly where it assembles
9
- * `providerConfig`, so the signal is sourced from config rather than a run
10
- * option. When v3-live is off the field is omitted (not `false`/`undefined`) so
11
- * the wire stays byte-identical to today.
5
+ * The signal means "the latest user message's bytes do not recur next turn", so
6
+ * it is set from the history actually being sent: only when the latest user
7
+ * message carries a memory-v3 `<memory_spotlight>` block, the one injected
8
+ * block that is strip-and-replaced on the tail every turn. Every other injected
9
+ * block is frozen into history and re-renders byte-identically, so turns
10
+ * without a spotlight keep a normal cache anchor even when memory-v3 is live.
11
+ *
12
+ * When no spotlight is present the field is omitted entirely (not `false`) so
13
+ * the wire stays byte-identical for conversations that never see one.
12
14
  */
13
15
 
14
- import { afterEach, describe, expect, mock, test } from "bun:test";
15
-
16
- // AgentLoop reads the v3-live gate (`config.memory.v3.live`) via
17
- // `isMemoryV3Live` to decide the cache-anchor signal; drive it per-test.
18
- let memoryV3LiveSlot = false;
19
- mock.module("../config/memory-v3-gate.js", () => ({
20
- isMemoryV3Live: () => memoryV3LiveSlot,
21
- isV3TierActive: () => memoryV3LiveSlot,
22
- isMemoryEnabled: (config?: { memory?: { enabled?: boolean } }) =>
23
- config?.memory?.enabled !== false,
24
- usesConceptPageMemory: (memory?: {
25
- enabled?: boolean;
26
- v2?: { enabled?: boolean };
27
- v3?: { live?: boolean };
28
- }) =>
29
- memory?.enabled !== false &&
30
- (memory?.v3?.live === true || memory?.v2?.enabled === true),
31
- }));
16
+ import { describe, expect, test } from "bun:test";
32
17
 
33
18
  import { AgentLoop } from "../agent/loop.js";
19
+ import { wrapMemorySpotlightBlock } from "../plugins/defaults/memory/memory-marker.js";
34
20
  import type {
35
21
  Message,
36
22
  Provider,
@@ -44,6 +30,15 @@ const userMessage: Message = {
44
30
  content: [{ type: "text", text: "hi" }],
45
31
  };
46
32
 
33
+ /** A user turn whose tail block is the ephemeral memory-v3 spotlight. */
34
+ const spotlightUserMessage: Message = {
35
+ role: "user",
36
+ content: [
37
+ { type: "text", text: "hi" },
38
+ { type: "text", text: wrapMemorySpotlightBlock("recalled: Alice's plan") },
39
+ ],
40
+ };
41
+
47
42
  function textResponse(text: string): ProviderResponse {
48
43
  return {
49
44
  content: [{ type: "text", text }],
@@ -87,40 +82,52 @@ function makeRecordingProvider(responses: ProviderResponse[]): {
87
82
  return { provider, configs: () => configs };
88
83
  }
89
84
 
90
- describe("AgentLoop.run mutableLatestUserMessage from memory-v3-live", () => {
91
- afterEach(() => {
92
- memoryV3LiveSlot = false;
93
- });
85
+ const echoTool: ToolDefinition = {
86
+ name: "echo",
87
+ description: "Echo back the input",
88
+ input_schema: {
89
+ type: "object",
90
+ properties: { value: { type: "string" } },
91
+ },
92
+ };
94
93
 
95
- test("sets mutableLatestUserMessage on every LLM call when memory-v3-live is on (multi-turn)", async () => {
96
- // GIVEN memory-v3-live is enabled
97
- memoryV3LiveSlot = true;
94
+ describe("AgentLoop.run: mutableLatestUserMessage from spotlight presence", () => {
95
+ test("sets mutableLatestUserMessage when the latest user message carries a spotlight block", async () => {
96
+ // GIVEN a provider that records the config of each LLM call
97
+ const { provider, configs } = makeRecordingProvider([textResponse("done")]);
98
+ const loop = new AgentLoop({
99
+ provider,
100
+ systemPrompt: "system",
101
+ conversationId: "test-conversation",
102
+ config: { maxTokens: 1024 },
103
+ });
98
104
 
99
- // AND a provider that records the config of each LLM call across a tool round-trip
100
- const { provider, configs } = makeRecordingProvider([
101
- toolUseResponse("t1", "echo", { value: "first" }),
102
- textResponse("done"),
103
- ]);
104
- const dummyTools: ToolDefinition[] = [
105
- {
106
- name: "echo",
107
- description: "Echo back the input",
108
- input_schema: {
109
- type: "object",
110
- properties: { value: { type: "string" } },
111
- },
112
- },
113
- ];
105
+ // WHEN the loop runs over a history whose tail carries the spotlight
106
+ await loop.run({
107
+ requestId: "test-request",
108
+ messages: [spotlightUserMessage],
109
+ onEvent: () => {},
110
+ trust: { sourceChannel: "vellum", trustClass: "unknown" },
111
+ callSite: "mainAgent",
112
+ });
113
+
114
+ // THEN the send carries the cache-anchor signal
115
+ expect(configs()).toHaveLength(1);
116
+ expect(configs()[0]?.mutableLatestUserMessage).toBe(true);
117
+ });
118
+
119
+ test("omits mutableLatestUserMessage when the latest user message carries no spotlight", async () => {
120
+ // GIVEN a provider that records the config of each LLM call
121
+ const { provider, configs } = makeRecordingProvider([textResponse("hi")]);
114
122
  const loop = new AgentLoop({
115
- provider: provider,
123
+ provider,
116
124
  systemPrompt: "system",
117
125
  conversationId: "test-conversation",
118
126
  config: { maxTokens: 1024 },
119
- tools: dummyTools,
120
- toolExecutor: async () => ({ content: "ok", isError: false }),
121
127
  });
122
128
 
123
- // WHEN the loop runs
129
+ // WHEN the loop runs over a spotlight-free history: the shape memory-v3
130
+ // produces whenever it selects nothing to spotlight
124
131
  await loop.run({
125
132
  requestId: "test-request",
126
133
  messages: [userMessage],
@@ -129,21 +136,16 @@ describe("AgentLoop.run — mutableLatestUserMessage from memory-v3-live", () =>
129
136
  callSite: "mainAgent",
130
137
  });
131
138
 
132
- // THEN every send (initial + tool round-trip) carries the cache-anchor signal
133
- expect(configs()).toHaveLength(2);
134
- for (const cfg of configs()) {
135
- expect(cfg?.mutableLatestUserMessage).toBe(true);
136
- }
139
+ // THEN the field is omitted entirely, not carried as false/undefined
140
+ expect(configs()).toHaveLength(1);
141
+ expect("mutableLatestUserMessage" in (configs()[0] ?? {})).toBe(false);
137
142
  });
138
143
 
139
- test("omits mutableLatestUserMessage when memory-v3-live is off (flag-off byte-identity)", async () => {
140
- // GIVEN memory-v3-live is off (no override; registry default is false)
141
- memoryV3LiveSlot = false;
142
-
143
- // AND a provider that records the config of each LLM call
144
+ test("a text block merely opening with the spotlight tag is not treated as volatile", async () => {
145
+ // GIVEN a user message discussing the marker rather than carrying one
144
146
  const { provider, configs } = makeRecordingProvider([textResponse("hi")]);
145
147
  const loop = new AgentLoop({
146
- provider: provider,
148
+ provider,
147
149
  systemPrompt: "system",
148
150
  conversationId: "test-conversation",
149
151
  config: { maxTokens: 1024 },
@@ -152,14 +154,59 @@ describe("AgentLoop.run — mutableLatestUserMessage from memory-v3-live", () =>
152
154
  // WHEN the loop runs
153
155
  await loop.run({
154
156
  requestId: "test-request",
155
- messages: [userMessage],
157
+ messages: [
158
+ {
159
+ role: "user",
160
+ content: [
161
+ {
162
+ type: "text",
163
+ text: "<memory_spotlight>\nwhat does this tag do?",
164
+ },
165
+ ],
166
+ },
167
+ ],
156
168
  onEvent: () => {},
157
169
  trust: { sourceChannel: "vellum", trustClass: "unknown" },
158
170
  callSite: "mainAgent",
159
171
  });
160
172
 
161
- // THEN the field is omitted entirely, not carried as false/undefined
173
+ // THEN the unterminated wrapper does not count: matching the full-wrapper
174
+ // requirement the per-turn strip applies
162
175
  expect(configs()).toHaveLength(1);
163
176
  expect("mutableLatestUserMessage" in (configs()[0] ?? {})).toBe(false);
164
177
  });
178
+
179
+ test("the signal holds for every request in a tool loop", async () => {
180
+ // GIVEN a spotlight-bearing opening message and a tool round-trip
181
+ const { provider, configs } = makeRecordingProvider([
182
+ toolUseResponse("t1", "echo", { value: "first" }),
183
+ textResponse("done"),
184
+ ]);
185
+ const loop = new AgentLoop({
186
+ provider,
187
+ systemPrompt: "system",
188
+ conversationId: "test-conversation",
189
+ config: { maxTokens: 1024 },
190
+ tools: [echoTool],
191
+ toolExecutor: async () => ({ content: "ok", isError: false }),
192
+ });
193
+
194
+ // WHEN the loop runs
195
+ await loop.run({
196
+ requestId: "test-request",
197
+ messages: [spotlightUserMessage],
198
+ onEvent: () => {},
199
+ trust: { sourceChannel: "vellum", trustClass: "unknown" },
200
+ callSite: "mainAgent",
201
+ });
202
+
203
+ // THEN both calls report the turn as volatile. The signal describes the
204
+ // turn-starting message, not whichever user message happens to be last, so
205
+ // the trailing tool-result turn does not clear it. Holding it steady is
206
+ // what keeps the provider marking that one block at a single TTL for the
207
+ // whole turn instead of writing it twice.
208
+ expect(configs()).toHaveLength(2);
209
+ expect(configs()[0]?.mutableLatestUserMessage).toBe(true);
210
+ expect(configs()[1]?.mutableLatestUserMessage).toBe(true);
211
+ });
165
212
  });
@@ -1,16 +1,17 @@
1
1
  /**
2
2
  * Verifies that the per-turn `overrideProfile` plumbed into `AgentLoop.run()`
3
3
  * surfaces on every `SendMessageOptions.config` the loop emits, and that
4
- * `SubagentManager.spawn()` propagates an inherited `overrideProfile` from
5
- * its `SubagentConfig` into the subagent's `runAgentLoop()` call.
4
+ * `SubagentManager.spawn()` propagates an `overrideProfile` set on its
5
+ * `SubagentConfig` into the subagent's `runAgentLoop()` call.
6
6
  *
7
- * Together these two assertions establish the PR 6 contract: a parent
8
- * conversation that pins an inference profile sees that profile applied to
9
- * every LLM call within its agent-loop turn, and any subagent spawned during
10
- * that turn inherits the same profile automatically.
7
+ * Together these establish where a pinned profile does and does not travel: it
8
+ * applies to every LLM call within the pinning conversation's own turn, and it
9
+ * stops at the spawn boundary. `executeSubagentSpawn` sets `overrideProfile`
10
+ * on the `SubagentConfig` only for a profile named explicitly on the spawn, so
11
+ * the propagation above carries a caller's choice rather than a parent's pin.
11
12
  *
12
- * Default behavior (no `overrideProfile` set) must remain unchanged the
13
- * field is omitted from `providerConfig` rather than carrying `undefined`.
13
+ * With no `overrideProfile` set, the field is omitted from `providerConfig`
14
+ * rather than carrying `undefined`.
14
15
  */
15
16
 
16
17
  import { describe, expect, mock, test } from "bun:test";
@@ -183,7 +184,7 @@ describe("AgentLoop.run — overrideProfile plumbing", () => {
183
184
  });
184
185
  });
185
186
 
186
- // ── Subagent inheritance ─────────────────────────────────────────────────
187
+ // ── Subagent profile forwarding ──────────────────────────────────────────
187
188
 
188
189
  // Capture the SubagentManager → Conversation handshake so we can verify the
189
190
  // `overrideProfile` from `SubagentConfig` is forwarded into the spawned
@@ -329,7 +330,7 @@ seedLlmConfig();
329
330
 
330
331
  import { SubagentManager } from "../subagent/manager.js";
331
332
 
332
- describe("SubagentManager.spawn overrideProfile inheritance", () => {
333
+ describe("SubagentManager.spawn: forwards a configured overrideProfile", () => {
333
334
  test("forwards overrideProfile from SubagentConfig into runAgentLoop", async () => {
334
335
  capturedRunAgentLoopOptions.length = 0;
335
336
 
@@ -344,9 +345,9 @@ describe("SubagentManager.spawn — overrideProfile inheritance", () => {
344
345
  () => {},
345
346
  );
346
347
 
347
- // The spawned subagent's runAgentLoop must receive both the
348
- // `subagentSpawn` callSite (existing behavior) and the inherited
349
- // `overrideProfile` (new behavior).
348
+ // The spawned subagent's runAgentLoop receives the `subagentSpawn`
349
+ // callSite and whatever `overrideProfile` the config carries, which
350
+ // `executeSubagentSpawn` sets only for an explicitly named profile.
350
351
  expect(capturedRunAgentLoopOptions).toHaveLength(1);
351
352
  const captured = capturedRunAgentLoopOptions[0];
352
353
  expect(captured.callSite).toBe("subagentSpawn");
@@ -399,21 +400,18 @@ describe("SubagentManager.spawn — overrideProfile inheritance", () => {
399
400
  });
400
401
  });
401
402
 
402
- // ── Nested subagent spawn context.overrideProfile preferred ────────────
403
+ // ── Nested subagent spawn: no profile crosses the boundary ───────────────
403
404
 
404
- // Verify the third-level inheritance contract: when a subagent's agent loop
405
- // is running with `currentTurnOverrideProfile`, the executor closure plumbs
406
- // that value into `ToolContext.overrideProfile`. `executeSubagentSpawn` must
407
- // then prefer `context.overrideProfile` over a row read against the in-flight
408
- // subagent's own conversationId that row never has `inferenceProfile` set,
409
- // and `getConversationOverrideProfile` short-circuits for background
410
- // conversations regardless. Without preferring the in-memory context, the
411
- // inheritance chain breaks at the second nesting level.
405
+ // When a subagent's agent loop is running under a turn profile, the executor
406
+ // closure plumbs that value into `ToolContext.overrideProfile`. That value
407
+ // describes the spawning turn, so `executeSubagentSpawn` leaves it behind: a
408
+ // child resolves the `subagentSpawn` call site on its own, and a forwarded
409
+ // override would both re-price the child and file its spend as a pin nobody
410
+ // set. These tests cover the nesting levels where a profile could otherwise
411
+ // compound down a chain of children.
412
412
 
413
413
  mock.module("../persistence/conversation-crud.js", () => ({
414
414
  setConversationProcessingStartedAt: () => {},
415
- // Always return undefined for the row read so the test fails fast unless
416
- // executeSubagentSpawn reads from context.overrideProfile first.
417
415
  getConversationOverrideProfile: () => undefined,
418
416
  reserveMessage: mock(async () => ({ id: "msg-reserve" })),
419
417
  }));
@@ -421,8 +419,8 @@ mock.module("../persistence/conversation-crud.js", () => ({
421
419
  import { getSubagentManager } from "../subagent/index.js";
422
420
  import { executeSubagentSpawn } from "../tools/subagent/spawn.js";
423
421
 
424
- describe("executeSubagentSpawn nested inheritance via context.overrideProfile", () => {
425
- test("forwards context.overrideProfile to SubagentConfig (third-level inheritance)", async () => {
422
+ describe("executeSubagentSpawn: the spawn boundary carries no profile", () => {
423
+ test("a subagent spawning a subagent does not pass its own turn profile down", async () => {
426
424
  const manager = getSubagentManager();
427
425
  const originalSpawn = manager.spawn.bind(manager);
428
426
  let capturedConfig: Record<string, unknown> | undefined;
@@ -432,11 +430,11 @@ describe("executeSubagentSpawn — nested inheritance via context.overrideProfil
432
430
  };
433
431
 
434
432
  try {
435
- // Simulate the second-level spawn: tool invocation occurs inside the
436
- // first subagent's tool context, where `overrideProfile` was populated
437
- // by `runAgentLoopImpl` from its `currentTurnOverrideProfile` snapshot.
438
- // The first subagent's own conversation row has no `inferenceProfile`,
439
- // so the row-read fallback would otherwise return undefined.
433
+ // The second-level spawn: this tool call happens inside the first
434
+ // subagent's tool context, where `overrideProfile` was populated by
435
+ // `runAgentLoopImpl` from its own turn snapshot. That snapshot describes
436
+ // the parent's turn, so it stops here rather than compounding down a
437
+ // chain of nested children.
440
438
  const result = await executeSubagentSpawn(
441
439
  { label: "nested", objective: "do nested work" },
442
440
  {
@@ -450,16 +448,13 @@ describe("executeSubagentSpawn — nested inheritance via context.overrideProfil
450
448
 
451
449
  expect(result.isError).toBe(false);
452
450
  expect(capturedConfig).toBeDefined();
453
- // The forwarded SubagentConfig must carry the in-memory override so
454
- // SubagentManager.spawn forwards it into the nested subagent's
455
- // runAgentLoop options — preserving inheritance across the chain.
456
- expect(capturedConfig!.overrideProfile).toBe("fast");
451
+ expect("overrideProfile" in capturedConfig!).toBe(false);
457
452
  } finally {
458
453
  manager.spawn = originalSpawn;
459
454
  }
460
455
  });
461
456
 
462
- test("forwards the invoker's resolved default profile when neither context nor row carries an override", async () => {
457
+ test("no override anywhere leaves the child on the subagentSpawn default", async () => {
463
458
  const manager = getSubagentManager();
464
459
  const originalSpawn = manager.spawn.bind(manager);
465
460
  let capturedConfig: Record<string, unknown> | undefined;
@@ -481,22 +476,17 @@ describe("executeSubagentSpawn — nested inheritance via context.overrideProfil
481
476
  );
482
477
 
483
478
  expect(capturedConfig).toBeDefined();
484
- // With no explicit override anywhere, the invoker-default tier forwards
485
- // the invoking call site's resolved default profile — here `mainAgent`
486
- // with no active profile or pin, which resolves the catalog `balanced`
487
- // default — so the subagent matches its invoker's model selection.
488
- expect(capturedConfig!.overrideProfile).toBe("balanced");
479
+ expect("overrideProfile" in capturedConfig!).toBe(false);
489
480
  } finally {
490
481
  manager.spawn = originalSpawn;
491
482
  }
492
483
  });
493
484
 
494
- test("an explicit subagentSpawn call-site pin suppresses the invoker-default inheritance", async () => {
495
- // With an active profile the invoker-default tier resolves ("fast"), so
496
- // absent a pin it is forwarded as the inherited override; with an
497
- // explicit llm.callSites.subagentSpawn profile the heuristic must yield —
498
- // a forwarded override would outrank the pin under single-winner
499
- // resolution, overriding the user's explicit call-site choice.
485
+ test("a workspace active profile does not reach the child, pinned or not", async () => {
486
+ // The child resolves `llm.callSites.subagentSpawn` on its own, so the
487
+ // spawn must forward nothing either way: a forwarded override would
488
+ // outrank an explicit call-site pin under single-winner resolution and
489
+ // overturn the operator's choice.
500
490
  const manager = getSubagentManager();
501
491
  const originalSpawn = manager.spawn.bind(manager);
502
492
  let capturedConfig: Record<string, unknown> | undefined;
@@ -517,7 +507,7 @@ describe("executeSubagentSpawn — nested inheritance via context.overrideProfil
517
507
  { label: "nested", objective: "do nested work" },
518
508
  baseContext,
519
509
  );
520
- expect(capturedConfig!.overrideProfile).toBe("fast");
510
+ expect("overrideProfile" in capturedConfig!).toBe(false);
521
511
 
522
512
  seedLlmConfig({
523
513
  activeProfile: "fast",
@@ -0,0 +1,166 @@
1
+ /**
2
+ * The runtime proxy bills delegated work off `X-Vellum-Subagent-Role` and
3
+ * `X-Vellum-Subagent-Spawn-Mode`. `RetryProvider` resolves both from
4
+ * `config.conversationId`, so the headers only ever materialize in production
5
+ * if `AgentLoop` stamps its own conversation id onto the provider config it
6
+ * sends. These tests drive a real `AgentLoop` through a real `RetryProvider`
7
+ * so a config field dropped at the loop can never pass silently.
8
+ *
9
+ * Subagents run their turns through `ConversationRunner.runAgentLoop` with
10
+ * `callSite: "subagentSpawn"`, which is the shape reproduced here.
11
+ */
12
+
13
+ import { beforeEach, describe, expect, test } from "bun:test";
14
+
15
+ import { AgentLoop } from "../agent/loop.js";
16
+ import { initializeDb } from "../persistence/db-init.js";
17
+ import { rawRun } from "../persistence/raw-query.js";
18
+ import { RetryProvider } from "../providers/retry.js";
19
+ import type {
20
+ Message,
21
+ Provider,
22
+ ProviderResponse,
23
+ SendMessageOptions,
24
+ } from "../providers/types.js";
25
+ import { resetSubagentAttributionCacheForTests } from "../usage/subagent-attribution.js";
26
+ import { setConfig } from "./helpers/set-config.js";
27
+
28
+ const userMessage: Message = {
29
+ role: "user",
30
+ content: [{ type: "text", text: "hi" }],
31
+ };
32
+
33
+ /**
34
+ * Stands in for the transport underneath `RetryProvider`, recording the config
35
+ * it is handed. That config is what would go on the wire, so it is also where
36
+ * the resolved billing headers land.
37
+ */
38
+ function makeRecordingProvider(name: string): {
39
+ provider: Provider;
40
+ configs: () => Array<Record<string, unknown>>;
41
+ } {
42
+ const configs: Array<Record<string, unknown>> = [];
43
+ const provider: Provider = {
44
+ name,
45
+ async sendMessage(
46
+ _messages: Message[],
47
+ options?: SendMessageOptions,
48
+ ): Promise<ProviderResponse> {
49
+ configs.push((options?.config ?? {}) as Record<string, unknown>);
50
+ return {
51
+ content: [{ type: "text", text: "ok" }],
52
+ model: "gpt-sub",
53
+ usage: { inputTokens: 1, outputTokens: 1 },
54
+ stopReason: "end_turn",
55
+ };
56
+ },
57
+ };
58
+ return { provider, configs: () => configs };
59
+ }
60
+
61
+ async function seedConversation(
62
+ id: string,
63
+ subagentRole: string | null,
64
+ subagentSpawnMode: string | null,
65
+ ): Promise<void> {
66
+ await initializeDb();
67
+ resetSubagentAttributionCacheForTests();
68
+ rawRun(
69
+ "test:seedSubagentConversation",
70
+ `INSERT OR REPLACE INTO conversations (id, conversation_type, created_at, updated_at, subagent_role, subagent_spawn_mode)
71
+ VALUES (?, 'background', 1000, 1000, ?, ?)`,
72
+ id,
73
+ subagentRole,
74
+ subagentSpawnMode,
75
+ );
76
+ }
77
+
78
+ /** Runs one loop turn the way a subagent's conversation runner does. */
79
+ async function runSubagentTurn(
80
+ provider: Provider,
81
+ conversationId: string,
82
+ ): Promise<void> {
83
+ const loop = new AgentLoop({
84
+ provider,
85
+ systemPrompt: "system",
86
+ conversationId,
87
+ config: { maxTokens: 1024 },
88
+ });
89
+ await loop.run({
90
+ requestId: "test-request",
91
+ messages: [userMessage],
92
+ onEvent: () => {},
93
+ trust: { sourceChannel: "vellum", trustClass: "unknown" },
94
+ callSite: "subagentSpawn",
95
+ });
96
+ }
97
+
98
+ describe("AgentLoop subagent billing-header attribution", () => {
99
+ beforeEach(() => {
100
+ setConfig("llm", {
101
+ callSites: { subagentSpawn: { provider: "openai", model: "gpt-sub" } },
102
+ });
103
+ });
104
+
105
+ test("forwards the spawning role and mode on a subagent conversation's turn", async () => {
106
+ // GIVEN a conversation stamped as an advisor-consult subagent
107
+ await seedConversation("conv-loop-advisor", "advisor", "advisor_consult");
108
+
109
+ // AND a managed-proxy-shaped provider stack (headers forwarded)
110
+ const { provider, configs } = makeRecordingProvider("openai");
111
+ const wrapped = new RetryProvider(provider, {
112
+ forwardUsageAttributionHeaders: true,
113
+ });
114
+
115
+ // WHEN that subagent's loop runs a turn
116
+ await runSubagentTurn(wrapped, "conv-loop-advisor");
117
+
118
+ // THEN the transport sees the delegated-work attribution
119
+ expect(configs()).toHaveLength(1);
120
+ const headers = configs()[0]?.usageAttributionHeaders as Record<
121
+ string,
122
+ string
123
+ >;
124
+ expect(headers["X-Vellum-Subagent-Role"]).toBe("advisor");
125
+ expect(headers["X-Vellum-Subagent-Spawn-Mode"]).toBe("advisor_consult");
126
+ expect(headers["X-Vellum-LLM-Call-Site"]).toBe("subagentSpawn");
127
+
128
+ // AND the routing-time id itself never reaches the wire config
129
+ expect(configs()[0]?.conversationId).toBeUndefined();
130
+ });
131
+
132
+ test("omits the subagent headers on a turn for a plain conversation", async () => {
133
+ // GIVEN a conversation that was not spawned as a subagent
134
+ await seedConversation("conv-loop-plain", null, null);
135
+
136
+ const { provider, configs } = makeRecordingProvider("openai");
137
+ const wrapped = new RetryProvider(provider, {
138
+ forwardUsageAttributionHeaders: true,
139
+ });
140
+
141
+ // WHEN its loop runs a turn
142
+ await runSubagentTurn(wrapped, "conv-loop-plain");
143
+
144
+ // THEN the base attribution is present without the delegated-work fields
145
+ const headers = configs()[0]?.usageAttributionHeaders as Record<
146
+ string,
147
+ string
148
+ >;
149
+ expect(headers["X-Vellum-LLM-Call-Site"]).toBe("subagentSpawn");
150
+ expect(headers["X-Vellum-Subagent-Role"]).toBeUndefined();
151
+ expect(headers["X-Vellum-Subagent-Spawn-Mode"]).toBeUndefined();
152
+ });
153
+
154
+ test("stamps the conversation id on the provider config it sends", async () => {
155
+ // GIVEN a bare recording provider (no `RetryProvider` to consume the field)
156
+ const { provider, configs } = makeRecordingProvider("openai");
157
+
158
+ // WHEN a loop with a conversation id runs a call-site turn
159
+ await runSubagentTurn(provider, "conv-loop-raw");
160
+
161
+ // THEN the id the header lookup depends on is on the config, alongside the
162
+ // mix-expansion seed
163
+ expect(configs()[0]?.conversationId).toBe("conv-loop-raw");
164
+ expect(configs()[0]?.selectionSeed).toBe("conv-loop-raw");
165
+ });
166
+ });
@@ -88,6 +88,7 @@ function makeTarget(): Conversation {
88
88
  getTurnChannelContext: () => null,
89
89
  getTurnInterfaceContext: () => null,
90
90
  drainQueue: async () => {},
91
+ kickDrainQueue: async () => {},
91
92
  // Pre-run auto-compaction gate — no-op for these tests.
92
93
  maybeCompact: async () => null,
93
94
  };
@@ -100,6 +100,7 @@ function makeTarget(): {
100
100
  getTurnChannelContext: () => null,
101
101
  getTurnInterfaceContext: () => null,
102
102
  drainQueue: async () => {},
103
+ kickDrainQueue: async () => {},
103
104
  // Pre-run auto-compaction gate — no-op for these tests.
104
105
  maybeCompact: async () => null,
105
106
  buildCurrentSystemPrompt: () => "mock-system-prompt",