@vellumai/assistant 0.11.2 → 0.11.3-staging.1

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 (709) 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-queue.test.ts +265 -1
  72. package/src/__tests__/conversation-retry-route.test.ts +3 -1
  73. package/src/__tests__/conversation-store-ephemeral.test.ts +4 -0
  74. package/src/__tests__/conversation-store.test.ts +26 -10
  75. package/src/__tests__/conversation-summarize-route.test.ts +1 -1
  76. package/src/__tests__/conversation-surfaces-action-delivery.test.ts +15 -6
  77. package/src/__tests__/conversation-surfaces-activation-emit.test.ts +6 -5
  78. package/src/__tests__/conversation-surfaces-app-control.test.ts +12 -11
  79. package/src/__tests__/conversation-surfaces-app-open.test.ts +11 -5
  80. package/src/__tests__/conversation-surfaces-data-persist.test.ts +5 -4
  81. package/src/__tests__/conversation-surfaces-history-restored-completion.test.ts +967 -0
  82. package/src/__tests__/conversation-surfaces-persisted-info.test.ts +312 -0
  83. package/src/__tests__/conversation-surfaces-queued-emit.test.ts +6 -5
  84. package/src/__tests__/conversation-surfaces-standalone-payloads.test.ts +12 -8
  85. package/src/__tests__/conversation-surfaces-standalone.test.ts +21 -11
  86. package/src/__tests__/conversation-surfaces-state-update.test.ts +7 -11
  87. package/src/__tests__/conversation-surfaces-table-action.test.ts +7 -6
  88. package/src/__tests__/conversation-surfaces-task-progress.test.ts +11 -6
  89. package/src/__tests__/conversation-tool-setup-app-refresh.test.ts +9 -6
  90. package/src/__tests__/conversation-tool-setup-attribution.test.ts +70 -7
  91. package/src/__tests__/conversation-wait-for-idle.test.ts +6 -11
  92. package/src/__tests__/credential-prompt-route.test.ts +57 -7
  93. package/src/__tests__/credential-security-invariants.test.ts +1 -0
  94. package/src/__tests__/cu-unified-flow.test.ts +89 -28
  95. package/src/__tests__/custom-group-listing-visibility.test.ts +1 -1
  96. package/src/__tests__/db-schedule-syntax-migration.test.ts +4 -1
  97. package/src/__tests__/discord-access-request-privacy.test.ts +366 -0
  98. package/src/__tests__/discord-requester-notice-privacy.test.ts +87 -0
  99. package/src/__tests__/disk-pressure-guard.test.ts +55 -55
  100. package/src/__tests__/disk-pressure-routes.test.ts +10 -10
  101. package/src/__tests__/disk-pressure-tools.test.ts +2 -2
  102. package/src/__tests__/disk-usage.test.ts +35 -20
  103. package/src/__tests__/document-sync-tags.test.ts +379 -0
  104. package/src/__tests__/document-update-default-surface.test.ts +28 -0
  105. package/src/__tests__/dynamic-skill-background-guard.test.ts +2 -2
  106. package/src/__tests__/emit-signal-routing-intent.test.ts +3 -4
  107. package/src/__tests__/ensure-conversation-exists.test.ts +8 -5
  108. package/src/__tests__/events-dev-bypass-actor.test.ts +183 -1
  109. package/src/__tests__/evict-conversations-for-reload.test.ts +90 -0
  110. package/src/__tests__/file-list-tool.test.ts +12 -10
  111. package/src/__tests__/file-ops-service.test.ts +151 -44
  112. package/src/__tests__/file-read-container-boundary.test.ts +137 -0
  113. package/src/__tests__/file-read-tool.test.ts +21 -9
  114. package/src/__tests__/file-url-path-guard.test.ts +83 -0
  115. package/src/__tests__/filesystem-tools.test.ts +82 -66
  116. package/src/__tests__/guardian-outbound-http.test.ts +8 -8
  117. package/src/__tests__/helpers/gateway-verification-sessions-stub.ts +14 -1
  118. package/src/__tests__/helpers/hub-clients.ts +42 -0
  119. package/src/__tests__/helpers/mock-conversation.ts +46 -0
  120. package/src/__tests__/helpers/verification-sessions-ipc-sim.ts +61 -57
  121. package/src/__tests__/history-repair.test.ts +112 -12
  122. package/src/__tests__/host-bash-proxy.test.ts +10 -5
  123. package/src/__tests__/host-bash-routes.test.ts +67 -0
  124. package/src/__tests__/host-browser-proxy.test.ts +2 -2
  125. package/src/__tests__/host-cu-proxy.test.ts +3 -7
  126. package/src/__tests__/host-file-proxy.test.ts +3 -7
  127. package/src/__tests__/host-file-read-tool.test.ts +5 -1
  128. package/src/__tests__/host-transfer-proxy-targeted.test.ts +6 -4
  129. package/src/__tests__/http-conversation-lineage.test.ts +2 -5
  130. package/src/__tests__/http-user-message-parity.test.ts +44 -0
  131. package/src/__tests__/identity-routes.test.ts +13 -13
  132. package/src/__tests__/image-conversion.test.ts +29 -26
  133. package/src/__tests__/image-source-path-reinject.test.ts +2 -2
  134. package/src/__tests__/inference-profile-session-handler.test.ts +95 -0
  135. package/src/__tests__/inference-profile-session-ipc.test.ts +25 -0
  136. package/src/__tests__/introduction-card-resolver.test.ts +6 -1
  137. package/src/__tests__/list-messages-attachments.test.ts +56 -32
  138. package/src/__tests__/list-messages-background-tool-completion.test.ts +4 -4
  139. package/src/__tests__/list-messages-client-message-id.test.ts +2 -2
  140. package/src/__tests__/list-messages-hidden-metadata.test.ts +16 -16
  141. package/src/__tests__/list-messages-page-latest.test.ts +61 -61
  142. package/src/__tests__/list-messages-provider-error.test.ts +6 -6
  143. package/src/__tests__/list-messages-queued.test.ts +49 -6
  144. package/src/__tests__/list-messages-system-card.test.ts +4 -4
  145. package/src/__tests__/list-messages-tool-merge.test.ts +9 -9
  146. package/src/__tests__/llm-resolver-override-or-default.test.ts +141 -0
  147. package/src/__tests__/llm-usage-store.test.ts +55 -0
  148. package/src/__tests__/manual-token-reconciliation.test.ts +45 -0
  149. package/src/__tests__/mcp-tool-annotations-risk.test.ts +133 -0
  150. package/src/__tests__/media-stream-server-integration.test.ts +36 -0
  151. package/src/__tests__/migration-export-http.test.ts +1 -1
  152. package/src/__tests__/migration-import-commit-http.test.ts +1 -1
  153. package/src/__tests__/migration-import-preflight-http.test.ts +1 -1
  154. package/src/__tests__/migration-validate-http.test.ts +1 -1
  155. package/src/__tests__/mtime-cache.test.ts +65 -3
  156. package/src/__tests__/notification-platform-adapter.test.ts +104 -0
  157. package/src/__tests__/notification-schedule-notify-dedup.test.ts +1 -1
  158. package/src/__tests__/oauth-provider-profiles.test.ts +4 -2
  159. package/src/__tests__/oauth-provider-serializer.test.ts +17 -0
  160. package/src/__tests__/oauth-providers-routes.test.ts +1 -0
  161. package/src/__tests__/openai-responses-prompt-cache.test.ts +5 -2
  162. package/src/__tests__/path-policy.test.ts +164 -1
  163. package/src/__tests__/persist-media-references.test.ts +2 -2
  164. package/src/__tests__/persist-unsendable-image-downscale.test.ts +5 -5
  165. package/src/__tests__/persist-unsendable-image.test.ts +23 -23
  166. package/src/__tests__/persona-resolver.test.ts +154 -0
  167. package/src/__tests__/plugin-api-webhook-url.test.ts +170 -0
  168. package/src/__tests__/plugin-import-boundary-guard.test.ts +6 -0
  169. package/src/__tests__/processing-flag-persist-failure.test.ts +15 -10
  170. package/src/__tests__/profile-availability-incomplete.test.ts +113 -0
  171. package/src/__tests__/profiler-routes.test.ts +13 -13
  172. package/src/__tests__/prompt-cache-cross-turn-stability.test.ts +593 -0
  173. package/src/__tests__/proxy-approval-callback.test.ts +3 -3
  174. package/src/__tests__/queued-message-cancel-and-steer.test.ts +189 -0
  175. package/src/__tests__/queued-message-steer-actor-scoping.test.ts +164 -0
  176. package/src/__tests__/resolve-trust-class.test.ts +20 -0
  177. package/src/__tests__/run-conversation-turn-persistence.test.ts +1 -1
  178. package/src/__tests__/run-due-schedules.test.ts +174 -0
  179. package/src/__tests__/runtime-attachment-metadata.test.ts +15 -7
  180. package/src/__tests__/scaffold-managed-skill-tool.test.ts +166 -0
  181. package/src/__tests__/schedule-routes.test.ts +730 -8
  182. package/src/__tests__/schedule-store.test.ts +540 -0
  183. package/src/__tests__/schedule-tools.test.ts +103 -0
  184. package/src/__tests__/scheduler-wake.test.ts +5 -0
  185. package/src/__tests__/secret-routes-platform-proxy.test.ts +29 -1
  186. package/src/__tests__/secure-keys.test.ts +91 -0
  187. package/src/__tests__/server-history-render.test.ts +97 -7
  188. package/src/__tests__/size-guard.test.ts +8 -8
  189. package/src/__tests__/skill-docs-sync-guard.test.ts +3 -2
  190. package/src/__tests__/skill-tool-factory.test.ts +247 -0
  191. package/src/__tests__/skills-install-staging.test.ts +15 -6
  192. package/src/__tests__/slack-edit-ordering-characterization.test.ts +103 -0
  193. package/src/__tests__/slack-inbound-verification.test.ts +4 -1
  194. package/src/__tests__/slack-mention-binding-invariance.test.ts +103 -0
  195. package/src/__tests__/slack-mention-provider-leak-audit.test.ts +135 -0
  196. package/src/__tests__/starter-task-flow.test.ts +5 -4
  197. package/src/__tests__/steer-on-enqueue-question.test.ts +3 -2
  198. package/src/__tests__/stt-language-catalog-parity.test.ts +38 -0
  199. package/src/__tests__/subagent-allowlist-validation.test.ts +26 -4
  200. package/src/__tests__/subagent-cron-run-attribution.test.ts +301 -0
  201. package/src/__tests__/subagent-disposal.test.ts +10 -0
  202. package/src/__tests__/subagent-fork-notifications.test.ts +10 -0
  203. package/src/__tests__/subagent-fork-prompt-role.test.ts +85 -11
  204. package/src/__tests__/subagent-fork-spawn.test.ts +15 -7
  205. package/src/__tests__/subagent-manager-notify.test.ts +148 -0
  206. package/src/__tests__/subagent-notify-parent.test.ts +1 -1
  207. package/src/__tests__/subagent-persistence.test.ts +141 -0
  208. package/src/__tests__/subagent-role-registry.test.ts +172 -63
  209. package/src/__tests__/subagent-role-resolution.test.ts +88 -0
  210. package/src/__tests__/subagent-spawn-and-await.test.ts +48 -0
  211. package/src/__tests__/subagent-spawn-tool-fork.test.ts +111 -10
  212. package/src/__tests__/subagent-terminal-message.test.ts +88 -1
  213. package/src/__tests__/subagent-tool-gate-mode.test.ts +225 -10
  214. package/src/__tests__/subagent-tools.test.ts +1901 -108
  215. package/src/__tests__/surface-completion-compaction-boundary.test.ts +124 -0
  216. package/src/__tests__/surface-completion-in-flight-snapshot.test.ts +298 -0
  217. package/src/__tests__/system-prompt.test.ts +82 -7
  218. package/src/__tests__/tool-result-spool.test.ts +74 -18
  219. package/src/__tests__/tool-side-effects-documents.test.ts +174 -0
  220. package/src/__tests__/tool-side-effects-hook-failures.test.ts +129 -0
  221. package/src/__tests__/tools-audio-read.test.ts +10 -10
  222. package/src/__tests__/tools-get-route.test.ts +85 -12
  223. package/src/__tests__/tools-list-cli.test.ts +171 -0
  224. package/src/__tests__/top-level-renderer.test.ts +54 -1
  225. package/src/__tests__/trusted-contact-multichannel.test.ts +4 -1
  226. package/src/__tests__/turn-tail-chain.test.ts +135 -0
  227. package/src/__tests__/ui-choice-copy-surfaces.test.ts +6 -4
  228. package/src/__tests__/ui-visual-surface.test.ts +5 -4
  229. package/src/__tests__/ui-work-result-surface.test.ts +5 -4
  230. package/src/__tests__/unified-turn-context-visible-app.test.ts +7 -8
  231. package/src/__tests__/usage-routes.test.ts +62 -0
  232. package/src/__tests__/verification-outbound-delivery.test.ts +176 -0
  233. package/src/__tests__/visible-app-context.test.ts +20 -4
  234. package/src/__tests__/web-search-history.test.ts +96 -0
  235. package/src/__tests__/workspace-migration-139-clear-renamed-cost-profile-label.test.ts +6 -6
  236. package/src/__tests__/workspace-migration-140-repair-seed-pinned-memory-v3-live.test.ts +330 -0
  237. package/src/acp/failure-error.ts +3 -5
  238. package/src/agent/attachments.ts +26 -24
  239. package/src/agent/history-repair/history-repair.ts +27 -26
  240. package/src/agent/image-optimize.ts +6 -6
  241. package/src/agent/loop.ts +40 -22
  242. package/src/agent/message-types.ts +3 -3
  243. package/src/api/constants/document-tools.ts +55 -0
  244. package/src/api/events/question-answered.ts +53 -0
  245. package/src/api/events/tool-result.ts +7 -0
  246. package/src/api/index.ts +13 -0
  247. package/src/api/responses/conversation-message.ts +11 -0
  248. package/src/api/surfaces-oauth-connect.test.ts +96 -0
  249. package/src/api/surfaces.ts +36 -8
  250. package/src/approvals/guardian-channel-delivery.ts +83 -0
  251. package/src/approvals/guardian-expiry-notifier.ts +13 -11
  252. package/src/approvals/guardian-request-resolvers.ts +216 -132
  253. package/src/calls/__tests__/voice-session-bridge.test.ts +50 -0
  254. package/src/calls/media-stream-server.ts +9 -1
  255. package/src/calls/voice-session-bridge.ts +38 -0
  256. package/src/channels/__tests__/gateway-verification-sessions.test.ts +3 -3
  257. package/src/channels/config.ts +7 -2
  258. package/src/channels/gateway-verification-sessions.ts +7 -2
  259. package/src/channels/types.ts +7 -1
  260. package/src/cli/commands/__tests__/cache.test.ts +7 -4
  261. package/src/cli/commands/__tests__/cli-test-harness.ts +82 -0
  262. package/src/cli/commands/__tests__/conversations-wake.test.ts +51 -6
  263. package/src/cli/commands/__tests__/inference-models.test.ts +14 -0
  264. package/src/cli/commands/__tests__/inference-profiles.test.ts +44 -1
  265. package/src/cli/commands/__tests__/inference-providers.test.ts +7 -0
  266. package/src/cli/commands/__tests__/plugins.test.ts +692 -0
  267. package/src/cli/commands/__tests__/schedules.test.ts +136 -37
  268. package/src/cli/commands/conversations.ts +7 -3
  269. package/src/cli/commands/credentials.help.ts +7 -0
  270. package/src/cli/commands/inference-profiles.ts +9 -3
  271. package/src/cli/commands/inference.help.ts +16 -2
  272. package/src/cli/commands/mcp.help.ts +4 -2
  273. package/src/cli/commands/memory/memory-retrospective.ts +18 -0
  274. package/src/cli/commands/notifications.ts +3 -6
  275. package/src/cli/commands/oauth/connect-surface-guidance.test.ts +16 -0
  276. package/src/cli/commands/oauth/connect-surface-guidance.ts +25 -7
  277. package/src/cli/commands/oauth/connect.test.ts +26 -1
  278. package/src/cli/commands/oauth/connect.ts +4 -1
  279. package/src/cli/commands/oauth/index.help.ts +3 -1
  280. package/src/cli/commands/oauth/providers.ts +3 -0
  281. package/src/cli/commands/plugins.help.ts +25 -2
  282. package/src/cli/commands/plugins.ts +289 -10
  283. package/src/cli/commands/schedules.help.ts +14 -12
  284. package/src/cli/commands/schedules.ts +19 -5
  285. package/src/cli/commands/tools.help.ts +4 -2
  286. package/src/cli/commands/tools.ts +38 -0
  287. package/src/cli/lib/__tests__/inspect-plugin.test.ts +1 -0
  288. package/src/cli/lib/__tests__/install-from-github.test.ts +79 -0
  289. package/src/cli/lib/__tests__/install-from-platform.test.ts +47 -1
  290. package/src/cli/lib/__tests__/plugin-surfaces.test.ts +86 -7
  291. package/src/cli/lib/__tests__/upgrade-plugin.test.ts +122 -0
  292. package/src/cli/lib/install-from-github.ts +61 -0
  293. package/src/cli/lib/install-from-platform.ts +13 -2
  294. package/src/cli/lib/plugin-surfaces.ts +124 -4
  295. package/src/cli/lib/upgrade-plugin.ts +18 -0
  296. package/src/cli.ts +1 -1
  297. package/src/config/__tests__/deployment-context-defaults.test.ts +73 -0
  298. package/src/config/__tests__/memory-retrospective-schema.test.ts +40 -0
  299. package/src/config/__tests__/plugin-updates-schema.test.ts +43 -0
  300. package/src/config/__tests__/profile-tool-support.test.ts +131 -0
  301. package/src/config/bundled-skills/AGENTS.md +1 -1
  302. package/src/config/bundled-skills/app-builder/SKILL.md +1 -1
  303. package/src/config/bundled-skills/app-builder/references/RESPONSIVE.md +3 -3
  304. package/src/config/bundled-skills/schedule/SKILL.md +5 -1
  305. package/src/config/bundled-skills/schedule/TOOLS.json +2 -2
  306. package/src/config/bundled-skills/settings/TOOLS.json +2 -2
  307. package/src/config/bundled-skills/settings/tools/voice-config-update.ts +1 -0
  308. package/src/config/bundled-skills/subagent/SKILL.md +53 -22
  309. package/src/config/bundled-skills/subagent/TOOLS.json +14 -13
  310. package/src/config/bundled-skills/visualize/SKILL.md +1 -1
  311. package/src/config/default-profile-catalog.ts +6 -2
  312. package/src/config/feature-flag-registry.json +25 -17
  313. package/src/config/llm-resolver.ts +45 -15
  314. package/src/config/loader.ts +70 -1
  315. package/src/config/profile-tool-support.ts +110 -0
  316. package/src/config/sanitize-for-transfer.ts +11 -0
  317. package/src/config/schema.ts +4 -0
  318. package/src/config/schemas/__tests__/stt.test.ts +20 -5
  319. package/src/config/schemas/channels.ts +13 -0
  320. package/src/config/schemas/memory-retrospective.ts +25 -0
  321. package/src/config/schemas/plugin-updates.ts +55 -0
  322. package/src/config/schemas/services.ts +8 -4
  323. package/src/config/schemas/stt.ts +17 -5
  324. package/src/contacts/__tests__/guardian-delivery-reader.test.ts +16 -0
  325. package/src/contacts/guardian-delivery-reader.ts +29 -2
  326. package/src/context/compactor.ts +4 -4
  327. package/src/context/post-turn-tool-result-truncation.ts +32 -16
  328. package/src/context/strip-injections.ts +42 -0
  329. package/src/context/tool-result-spool.ts +32 -18
  330. package/src/credential-execution/ces-runtime.ts +3 -0
  331. package/src/credential-execution/client.ts +22 -1
  332. package/src/credential-execution/process-manager.test.ts +99 -0
  333. package/src/credential-execution/process-manager.ts +62 -16
  334. package/src/daemon/__tests__/abort-null-controller.test.ts +141 -5
  335. package/src/daemon/__tests__/conversation-surfaces-launch.test.ts +8 -12
  336. package/src/daemon/__tests__/install-assistant-command.test.ts +234 -0
  337. package/src/daemon/__tests__/turn-tail-assistant-reply-notify.test.ts +2 -4
  338. package/src/daemon/__tests__/turn-tail-deleted-conversation.test.ts +20 -29
  339. package/src/daemon/channel-ui-capability.ts +4 -4
  340. package/src/daemon/conversation-agent-loop-handlers.ts +117 -61
  341. package/src/daemon/conversation-agent-loop.ts +296 -145
  342. package/src/daemon/conversation-attachments.ts +1 -1
  343. package/src/daemon/conversation-error.ts +8 -1
  344. package/src/daemon/conversation-initial-prompt.ts +16 -38
  345. package/src/daemon/conversation-lifecycle.ts +154 -32
  346. package/src/daemon/conversation-messaging.ts +85 -42
  347. package/src/daemon/conversation-process.ts +76 -28
  348. package/src/daemon/conversation-runtime-assembly.ts +9 -4
  349. package/src/daemon/conversation-store.ts +52 -3
  350. package/src/daemon/conversation-surface-state.ts +9 -0
  351. package/src/daemon/conversation-surfaces.ts +403 -210
  352. package/src/daemon/conversation-tool-setup.ts +56 -7
  353. package/src/daemon/conversation-turn-finalize.ts +134 -89
  354. package/src/daemon/conversation.ts +105 -27
  355. package/src/daemon/disk-pressure-guard-lifecycle.ts +2 -2
  356. package/src/daemon/disk-pressure-guard.ts +3 -3
  357. package/src/daemon/doordash-steps.ts +5 -5
  358. package/src/daemon/external-plugins-bootstrap.ts +7 -1
  359. package/src/daemon/handlers/config-channels.ts +48 -5
  360. package/src/daemon/handlers/conversation-history.ts +1 -1
  361. package/src/daemon/handlers/conversations.ts +62 -17
  362. package/src/daemon/handlers/shared.ts +12 -0
  363. package/src/daemon/handlers/skills.ts +11 -1
  364. package/src/daemon/identity-helpers.ts +38 -10
  365. package/src/daemon/install-assistant-command.ts +322 -0
  366. package/src/daemon/lifecycle.ts +14 -3
  367. package/src/daemon/message-types/conversations.ts +6 -7
  368. package/src/daemon/message-types/sync.ts +1 -0
  369. package/src/daemon/persist-media-references.ts +23 -14
  370. package/src/daemon/process-message.ts +6 -6
  371. package/src/daemon/tool-setup-types.ts +19 -122
  372. package/src/daemon/tool-side-effects.ts +17 -94
  373. package/src/daemon/trust-context.ts +14 -6
  374. package/src/daemon/turn-tail-chain.ts +73 -0
  375. package/src/daemon/web-search-history.ts +47 -33
  376. package/src/heartbeat/heartbeat-service.ts +6 -6
  377. package/src/ipc/__tests__/socket-path.test.ts +21 -0
  378. package/src/ipc/assistant-server.ts +17 -10
  379. package/src/ipc/cli-client.ts +10 -1
  380. package/src/ipc/routes/__tests__/documents-sync-ipc-routes.test.ts +56 -0
  381. package/src/ipc/routes/documents-sync-ipc-routes.ts +42 -0
  382. package/src/ipc/socket-cleanup.ts +9 -7
  383. package/src/ipc/socket-path.ts +4 -96
  384. package/src/live-voice/__tests__/live-voice-archive.test.ts +16 -16
  385. package/src/live-voice/__tests__/live-voice-attach-image.test.ts +209 -0
  386. package/src/live-voice/__tests__/live-voice-connection.test.ts +20 -0
  387. package/src/live-voice/__tests__/live-voice-integration.test.ts +7 -5
  388. package/src/live-voice/__tests__/live-voice-session-telemetry.test.ts +265 -0
  389. package/src/live-voice/__tests__/live-voice-stt.test.ts +6 -1
  390. package/src/live-voice/__tests__/live-voice-vad.test.ts +35 -15
  391. package/src/live-voice/__tests__/protocol.test.ts +52 -0
  392. package/src/live-voice/__tests__/runtime-websocket-shell.test.ts +3 -0
  393. package/src/live-voice/live-voice-archive.ts +13 -13
  394. package/src/live-voice/live-voice-connection.ts +7 -1
  395. package/src/live-voice/live-voice-photo.ts +169 -0
  396. package/src/live-voice/live-voice-session.ts +108 -4
  397. package/src/live-voice/protocol.ts +89 -1
  398. package/src/mcp/client.ts +10 -0
  399. package/src/messaging/providers/__tests__/callback-routing.test.ts +5 -1
  400. package/src/messaging/providers/__tests__/transport-dispatch.test.ts +66 -4
  401. package/src/messaging/providers/callback-routing.ts +1 -0
  402. package/src/messaging/providers/discord/api.ts +312 -0
  403. package/src/messaging/providers/discord/dm-delivery.test.ts +204 -0
  404. package/src/messaging/providers/discord/render.test.ts +204 -0
  405. package/src/messaging/providers/discord/render.ts +195 -0
  406. package/src/messaging/providers/discord/send.test.ts +179 -0
  407. package/src/messaging/providers/discord/send.ts +212 -0
  408. package/src/messaging/providers/discord/transport.ts +81 -0
  409. package/src/messaging/providers/index.ts +3 -1
  410. package/src/messaging/providers/retry-policy.test.ts +247 -0
  411. package/src/messaging/providers/retry-policy.ts +206 -0
  412. package/src/messaging/providers/slack/mention-source.test.ts +285 -0
  413. package/src/messaging/providers/slack/mention-source.ts +287 -0
  414. package/src/messaging/providers/telegram-bot/api.ts +47 -130
  415. package/src/messaging/providers/whatsapp/api.ts +47 -136
  416. package/src/monitoring/__tests__/db-integrity-sample.test.ts +4 -1
  417. package/src/monitoring/__tests__/plugin-auto-update.test.ts +332 -0
  418. package/src/monitoring/db-integrity-sample.ts +6 -1
  419. package/src/monitoring/plugin-auto-update.ts +359 -0
  420. package/src/monitoring/resource-sampler.ts +21 -5
  421. package/src/monitoring/worker.ts +13 -0
  422. package/src/notifications/__tests__/assistant-reply-producer.test.ts +375 -2
  423. package/src/notifications/__tests__/broadcaster.test.ts +37 -1
  424. package/src/notifications/__tests__/copy-composer.test.ts +165 -0
  425. package/src/notifications/__tests__/emit-signal-pipeline-failure.test.ts +205 -0
  426. package/src/notifications/__tests__/notification-utils.test.ts +249 -0
  427. package/src/notifications/adapters/platform.ts +31 -2
  428. package/src/notifications/assistant-reply-producer.ts +79 -32
  429. package/src/notifications/broadcaster.ts +30 -10
  430. package/src/notifications/copy-composer.ts +69 -0
  431. package/src/notifications/emit-signal.ts +82 -15
  432. package/src/notifications/events-store.ts +10 -2
  433. package/src/notifications/home-feed-side-effect.ts +6 -3
  434. package/src/notifications/notification-utils.ts +173 -5
  435. package/src/notifications/signal.ts +12 -0
  436. package/src/oauth/byo-connection.ts +6 -1
  437. package/src/oauth/credential-token-resolver.ts +3 -10
  438. package/src/oauth/manual-token-connection.ts +29 -64
  439. package/src/oauth/manual-token-providers.ts +62 -0
  440. package/src/oauth/provider-serializer.ts +13 -0
  441. package/src/oauth/seed-providers.ts +56 -8
  442. package/src/onboarding/onboarding-events-store.ts +91 -1
  443. package/src/permissions/question-prompter.test.ts +4 -0
  444. package/src/permissions/question-prompter.ts +21 -4
  445. package/src/persistence/__tests__/conversation-lineage.test.ts +248 -0
  446. package/src/persistence/attachments-store.ts +15 -15
  447. package/src/persistence/conversation-attention-store.ts +30 -10
  448. package/src/persistence/conversation-bootstrap.ts +17 -0
  449. package/src/persistence/conversation-crud.ts +363 -97
  450. package/src/persistence/conversation-group-migration.ts +1 -1
  451. package/src/persistence/conversation-key-store.ts +38 -10
  452. package/src/persistence/conversation-lineage.ts +226 -0
  453. package/src/persistence/conversation-plugin-facade.ts +28 -3
  454. package/src/persistence/conversation-queries.ts +207 -33
  455. package/src/persistence/conversation-types.ts +107 -1
  456. package/src/persistence/jobs-store.ts +32 -8
  457. package/src/persistence/llm-usage-store.ts +22 -0
  458. package/src/persistence/migrations/361-normalize-managed-connection-rows.test.ts +168 -0
  459. package/src/persistence/migrations/361-normalize-managed-connection-rows.ts +76 -0
  460. package/src/persistence/migrations/362-add-conversation-subagent-kind.test.ts +187 -0
  461. package/src/persistence/migrations/362-add-conversation-subagent-kind.ts +93 -0
  462. package/src/persistence/migrations/363-backfill-schedule-inference-profile.test.ts +229 -0
  463. package/src/persistence/migrations/363-backfill-schedule-inference-profile.ts +127 -0
  464. package/src/persistence/migrations/364-add-schedule-source-key.test.ts +120 -0
  465. package/src/persistence/migrations/364-add-schedule-source-key.ts +43 -0
  466. package/src/persistence/migrations/365-add-conversation-fork-strategy.ts +27 -0
  467. package/src/persistence/schema/conversations.ts +32 -0
  468. package/src/persistence/schema/infrastructure.ts +3 -0
  469. package/src/persistence/steps.ts +30 -0
  470. package/src/persistence/subagent-store.ts +148 -6
  471. package/src/platform/managed-speech.test.ts +20 -0
  472. package/src/platform/managed-speech.ts +9 -0
  473. package/src/plugin-api/index.ts +8 -0
  474. package/src/plugin-api/webhook-url.ts +151 -0
  475. package/src/plugins/__tests__/installed-plugin-dirs.test.ts +82 -0
  476. package/src/plugins/__tests__/source-fingerprint.test.ts +5 -1
  477. package/src/plugins/collect-source-versions.ts +4 -27
  478. package/src/plugins/defaults/exploration-drift/hooks/post-tool-use.ts +5 -5
  479. package/src/plugins/defaults/exploration-drift/package.json +1 -1
  480. package/src/plugins/defaults/image-recovery/hooks/post-model-call.ts +4 -2
  481. package/src/plugins/defaults/image-recovery/recover.ts +67 -57
  482. package/src/plugins/defaults/index.ts +1 -1
  483. package/src/plugins/defaults/memory/__tests__/conversation-queries.test.ts +64 -18
  484. package/src/plugins/defaults/memory/__tests__/jobs-worker-outcome-contract.test.ts +242 -0
  485. package/src/plugins/defaults/memory/__tests__/jobs-worker-retrospective-sweep.test.ts +24 -1
  486. package/src/plugins/defaults/memory/__tests__/memory-retrospective-enqueue.test.ts +48 -3
  487. package/src/plugins/defaults/memory/__tests__/memory-retrospective-job.test.ts +511 -19
  488. package/src/plugins/defaults/memory/__tests__/memory-retrospective-prompt.test.ts +39 -3
  489. package/src/plugins/defaults/memory/__tests__/memory-retrospective-provider-path.test.ts +493 -0
  490. package/src/plugins/defaults/memory/__tests__/memory-retrospective-wake-chain.test.ts +5 -4
  491. package/src/plugins/defaults/memory/__tests__/memory-tier-boundary-guard.test.ts +5 -1
  492. package/src/plugins/defaults/memory/job-handlers.ts +87 -5
  493. package/src/plugins/defaults/memory/jobs-worker.ts +60 -6
  494. package/src/plugins/defaults/memory/memory-retrospective-constants.ts +11 -0
  495. package/src/plugins/defaults/memory/memory-retrospective-enqueue.ts +29 -0
  496. package/src/plugins/defaults/memory/memory-retrospective-job.ts +342 -60
  497. package/src/plugins/defaults/memory/memory-retrospective-prompt.ts +23 -3
  498. package/src/plugins/defaults/memory/substrate/consolidation-lock.ts +9 -4
  499. package/src/plugins/defaults/memory/substrate/sweep-job.ts +7 -0
  500. package/src/plugins/defaults/memory/v1/graph/injection.ts +1 -1
  501. package/src/plugins/defaults/memory/v2/router.ts +8 -0
  502. package/src/plugins/defaults/turn-context/unified-turn-context.ts +1 -2
  503. package/src/plugins/external-plugin-loader.ts +17 -8
  504. package/src/plugins/installed-plugin-dirs.ts +101 -0
  505. package/src/plugins/mtime-cache.ts +34 -22
  506. package/src/plugins/types.ts +69 -8
  507. package/src/prompts/persona-resolver.ts +6 -2
  508. package/src/prompts/system-prompt.ts +72 -17
  509. package/src/providers/__tests__/inference.test.ts +40 -0
  510. package/src/providers/__tests__/preflight-resolved-config.test.ts +58 -1
  511. package/src/providers/__tests__/retry-callsite.test.ts +67 -0
  512. package/src/providers/__tests__/vellum-connection-routing.test.ts +42 -11
  513. package/src/providers/anthropic/client.ts +112 -95
  514. package/src/providers/connection-resolution.ts +35 -1
  515. package/src/providers/gemini/client.ts +5 -5
  516. package/src/providers/inference/adapter-factory.ts +16 -10
  517. package/src/providers/inference/auth.ts +16 -0
  518. package/src/providers/inference/connection-availability.ts +164 -12
  519. package/src/providers/inference/connections.ts +25 -1
  520. package/src/providers/media-resolve.ts +21 -11
  521. package/src/providers/model-catalog.ts +15 -0
  522. package/src/providers/openai/chat-completions-provider.ts +4 -4
  523. package/src/providers/openai/responses-provider.ts +12 -25
  524. package/src/providers/registry.ts +9 -4
  525. package/src/providers/retry.ts +25 -0
  526. package/src/providers/server-tool-pairing.ts +134 -0
  527. package/src/providers/speech-to-text/__tests__/resolve.test.ts +187 -6
  528. package/src/providers/speech-to-text/deepgram.ts +1 -0
  529. package/src/providers/speech-to-text/resolve.ts +80 -10
  530. package/src/providers/speech-to-text/vellum-managed.ts +7 -1
  531. package/src/providers/speech-to-text/vellum-speech-relay-connection.ts +3 -1
  532. package/src/providers/types.ts +26 -6
  533. package/src/providers/vellum-model-routing.ts +6 -7
  534. package/src/runtime/AGENTS.md +2 -0
  535. package/src/runtime/__tests__/agent-wake.test.ts +16 -16
  536. package/src/runtime/__tests__/background-job-runner.test.ts +0 -144
  537. package/src/runtime/__tests__/desktop-presence.test.ts +162 -0
  538. package/src/runtime/agent-wake.ts +63 -24
  539. package/src/runtime/assistant-event-hub.ts +87 -12
  540. package/src/runtime/auth/__tests__/guard-tests.test.ts +2 -0
  541. package/src/runtime/auth/__tests__/scopes.test.ts +20 -0
  542. package/src/runtime/auth/same-actor.ts +56 -8
  543. package/src/runtime/auth/scopes.ts +12 -0
  544. package/src/runtime/auth/types.ts +2 -0
  545. package/src/runtime/background-job-runner.ts +5 -27
  546. package/src/runtime/channel-readiness-service.ts +59 -3
  547. package/src/runtime/channel-readiness-types.ts +18 -0
  548. package/src/runtime/desktop-presence.ts +69 -0
  549. package/src/runtime/http-server.ts +12 -0
  550. package/src/runtime/local-actor-identity.ts +11 -9
  551. package/src/runtime/migrations/__tests__/vbundle-symlink-streaming-importer.test.ts +0 -0
  552. package/src/runtime/routes/__tests__/channel-verification-routes.test.ts +1 -1
  553. package/src/runtime/routes/__tests__/client-routes.test.ts +186 -14
  554. package/src/runtime/routes/__tests__/conversation-list-routes.test.ts +547 -1
  555. package/src/runtime/routes/__tests__/conversation-management-routes.test.ts +162 -7
  556. package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +14 -10
  557. package/src/runtime/routes/__tests__/inference-profiles-routes.test.ts +340 -3
  558. package/src/runtime/routes/__tests__/llm-call-sites-routes.test.ts +28 -1
  559. package/src/runtime/routes/__tests__/plugins-routes.test.ts +173 -1
  560. package/src/runtime/routes/__tests__/question-routes.test.ts +64 -0
  561. package/src/runtime/routes/__tests__/retrospective-routes.test.ts +20 -0
  562. package/src/runtime/routes/__tests__/skill-history-route.test.ts +116 -0
  563. package/src/runtime/routes/__tests__/wake-conversation-routes.test.ts +60 -2
  564. package/src/runtime/routes/archive-utils.ts +16 -12
  565. package/src/runtime/routes/attachment-routes.ts +13 -6
  566. package/src/runtime/routes/canned-reply-release.ts +54 -0
  567. package/src/runtime/routes/channel-verification-routes.ts +2 -27
  568. package/src/runtime/routes/chatgpt-subscription-auth-routes.ts +5 -0
  569. package/src/runtime/routes/client-routes.ts +114 -2
  570. package/src/runtime/routes/conversation-cli-routes.ts +10 -12
  571. package/src/runtime/routes/conversation-list-routes.ts +59 -10
  572. package/src/runtime/routes/conversation-management-routes.ts +47 -18
  573. package/src/runtime/routes/conversation-query-routes.ts +32 -53
  574. package/src/runtime/routes/conversation-routes.ts +350 -293
  575. package/src/runtime/routes/credential-prompt-routes.ts +23 -2
  576. package/src/runtime/routes/debug-routes.ts +4 -1
  577. package/src/runtime/routes/default-provider-routes.ts +7 -11
  578. package/src/runtime/routes/documents-routes.ts +44 -1
  579. package/src/runtime/routes/events-routes.ts +28 -1
  580. package/src/runtime/routes/host-app-control-routes.ts +2 -0
  581. package/src/runtime/routes/host-bash-routes.ts +2 -0
  582. package/src/runtime/routes/host-browser-routes.ts +2 -0
  583. package/src/runtime/routes/host-cu-routes.ts +2 -0
  584. package/src/runtime/routes/host-file-routes.ts +2 -0
  585. package/src/runtime/routes/host-transfer-routes.ts +4 -0
  586. package/src/runtime/routes/identity-routes.ts +6 -6
  587. package/src/runtime/routes/inbound-message-handler.ts +5 -5
  588. package/src/runtime/routes/inbound-stages/acl-enforcement.ts +7 -3
  589. package/src/runtime/routes/inbound-stages/background-dispatch.ts +14 -0
  590. package/src/runtime/routes/inbound-stages/bootstrap-intercept.ts +2 -2
  591. package/src/runtime/routes/inbound-stages/guardian-activation-intercept.test.ts +14 -11
  592. package/src/runtime/routes/inbound-stages/guardian-activation-intercept.ts +14 -17
  593. package/src/runtime/routes/inference-profile-availability-guard.ts +171 -0
  594. package/src/runtime/routes/inference-profile-session-handler.ts +26 -2
  595. package/src/runtime/routes/inference-profile-session-routes.ts +2 -1
  596. package/src/runtime/routes/inference-profiles-routes.ts +152 -73
  597. package/src/runtime/routes/inference-provider-connection-routes.ts +0 -16
  598. package/src/runtime/routes/llm-call-sites-routes.ts +13 -0
  599. package/src/runtime/routes/log-export-routes.ts +1 -1
  600. package/src/runtime/routes/notification-routes.ts +0 -14
  601. package/src/runtime/routes/oauth-providers.ts +1 -0
  602. package/src/runtime/routes/plugins-routes.ts +106 -1
  603. package/src/runtime/routes/profiler-routes.ts +4 -2
  604. package/src/runtime/routes/question-routes.ts +28 -10
  605. package/src/runtime/routes/retrospective-routes.ts +9 -3
  606. package/src/runtime/routes/schedule-routes.ts +210 -12
  607. package/src/runtime/routes/secret-routes.ts +40 -23
  608. package/src/runtime/routes/settings-routes.ts +41 -13
  609. package/src/runtime/routes/skills-routes.ts +79 -0
  610. package/src/runtime/routes/ui-snapshot-routes.ts +1 -0
  611. package/src/runtime/routes/wake-conversation-routes.ts +23 -0
  612. package/src/runtime/services/__tests__/conversation-serializer.test.ts +1 -0
  613. package/src/runtime/services/conversation-serializer.ts +24 -9
  614. package/src/runtime/sync/documents-sidecar-publish.test.ts +101 -0
  615. package/src/runtime/sync/resource-sync-events.ts +79 -1
  616. package/src/runtime/sync/worker-daemon-notify.test.ts +34 -0
  617. package/src/runtime/sync/worker-daemon-notify.ts +44 -6
  618. package/src/runtime/verification-outbound-actions.ts +250 -222
  619. package/src/runtime/verification-templates.ts +41 -30
  620. package/src/schedule/__tests__/plugin-schedule-declarations.test.ts +614 -0
  621. package/src/schedule/__tests__/plugin-schedule-reconciler.test.ts +1066 -0
  622. package/src/schedule/inference-profile.ts +64 -3
  623. package/src/schedule/plugin-schedule-declarations.ts +518 -0
  624. package/src/schedule/plugin-schedule-reconciler.ts +605 -0
  625. package/src/schedule/plugin-schedules-gate.ts +23 -0
  626. package/src/schedule/schedule-store.ts +520 -26
  627. package/src/schedule/scheduler.ts +38 -0
  628. package/src/security/credential-backend.ts +51 -4
  629. package/src/security/encrypted-store.ts +64 -42
  630. package/src/security/secure-keys.ts +55 -7
  631. package/src/skills/catalog-install.ts +20 -7
  632. package/src/skills/inline-command-runner.ts +2 -7
  633. package/src/skills/skill-history.test.ts +269 -0
  634. package/src/skills/skill-history.ts +250 -0
  635. package/src/skills/skillssh-registry.ts +1 -1
  636. package/src/stt/daemon-batch-transcriber.ts +8 -6
  637. package/src/subagent/__tests__/consult-prompt.test.ts +25 -0
  638. package/src/subagent/consult-prompt.ts +3 -1
  639. package/src/subagent/consult-transcript.ts +1 -1
  640. package/src/subagent/manager.ts +295 -42
  641. package/src/subagent/progress-events.ts +35 -0
  642. package/src/subagent/role-resolution.ts +88 -0
  643. package/src/subagent/types.ts +302 -67
  644. package/src/subagent/validate-allowlists.ts +3 -4
  645. package/src/telegram/__tests__/webhook-health.test.ts +73 -1
  646. package/src/telegram/webhook-health.ts +101 -3
  647. package/src/telemetry/__tests__/telemetry-event-fixtures.ts +2 -0
  648. package/src/telemetry/activation-funnel.ts +6 -1
  649. package/src/telemetry/live-voice-funnel.ts +75 -0
  650. package/src/telemetry/telemetry-event-sources.ts +6 -0
  651. package/src/telemetry/telemetry-wire-source.json +1 -1
  652. package/src/telemetry/telemetry-wire.generated.ts +2 -0
  653. package/src/telemetry/types.ts +23 -4
  654. package/src/telemetry/usage-telemetry-reporter.test.ts +4 -0
  655. package/src/tools/__tests__/subagent-workflow-tool-input-schemas.test.ts +1 -1
  656. package/src/tools/ask-question/ask-question-tool.test.ts +118 -3
  657. package/src/tools/ask-question/ask-question-tool.ts +56 -2
  658. package/src/tools/browser/browser-execution.ts +1 -1
  659. package/src/tools/calls/call-start.ts +8 -5
  660. package/src/tools/conversation-groups/move_to_group.ts +10 -12
  661. package/src/tools/document/document-tool.ts +11 -4
  662. package/src/tools/executor.ts +7 -6
  663. package/src/tools/filesystem/edit.ts +1 -1
  664. package/src/tools/filesystem/list.ts +1 -1
  665. package/src/tools/filesystem/read.ts +7 -13
  666. package/src/tools/filesystem/search.ts +23 -21
  667. package/src/tools/filesystem/write.ts +4 -1
  668. package/src/tools/host-filesystem/edit.ts +7 -2
  669. package/src/tools/host-filesystem/read.ts +21 -8
  670. package/src/tools/host-filesystem/write.ts +10 -2
  671. package/src/tools/host-terminal/host-shell.ts +2 -2
  672. package/src/tools/mcp/mcp-tool-factory.ts +25 -1
  673. package/src/tools/permission-checker.ts +8 -3
  674. package/src/tools/schedule/delete.ts +13 -1
  675. package/src/tools/schedule/list.ts +14 -3
  676. package/src/tools/schedule/update.ts +56 -33
  677. package/src/tools/shared/filesystem/audio-read.ts +6 -4
  678. package/src/tools/shared/filesystem/file-ops-service.ts +176 -116
  679. package/src/tools/shared/filesystem/image-read.ts +12 -12
  680. package/src/tools/shared/filesystem/path-policy.ts +70 -5
  681. package/src/tools/shared/filesystem/size-guard.ts +6 -6
  682. package/src/tools/shared/input-misuse.ts +72 -0
  683. package/src/tools/skills/scaffold-managed.ts +82 -0
  684. package/src/tools/skills/skill-tool-factory.ts +14 -1
  685. package/src/tools/subagent/consult-deadline.ts +12 -7
  686. package/src/tools/subagent/message.ts +3 -1
  687. package/src/tools/subagent/read.ts +66 -4
  688. package/src/tools/subagent/spawn.ts +500 -78
  689. package/src/tools/types.ts +22 -3
  690. package/src/tools/ui-surface/surface-shape-docs.ts +1 -1
  691. package/src/tools/ui-surface/visual-validation.ts +2 -0
  692. package/src/usage/__tests__/subagent-attribution.test.ts +109 -0
  693. package/src/usage/subagent-attribution.ts +108 -0
  694. package/src/util/__tests__/sqlite-retry.test.ts +112 -0
  695. package/src/util/abort-reasons.ts +16 -0
  696. package/src/util/ansi.ts +44 -0
  697. package/src/util/disk-usage.ts +17 -13
  698. package/src/util/image-conversion.ts +20 -13
  699. package/src/util/schedule-source-key.ts +23 -0
  700. package/src/util/worker-process.ts +7 -1
  701. package/src/workspace/git-service.ts +33 -9
  702. package/src/workspace/migrations/140-repair-seed-pinned-memory-v3-live.ts +260 -0
  703. package/src/workspace/migrations/141-stt-english-default-to-multilingual.ts +111 -0
  704. package/src/workspace/migrations/__tests__/141-stt-english-default-to-multilingual.test.ts +126 -0
  705. package/src/workspace/migrations/registry.ts +4 -0
  706. package/src/workspace/top-level-renderer.ts +15 -2
  707. package/src/__tests__/tool-side-effects-slack-dm.test.ts +0 -357
  708. package/src/daemon/install-symlink.ts +0 -207
  709. package/src/notifications/deferred-emit.ts +0 -147
@@ -63,6 +63,8 @@ function seedVellumGuardian(userFile: string | null): void {
63
63
 
64
64
  mock.module("../util/platform.js", () => ({
65
65
  getWorkspaceDir: () => mockWorkspaceDir,
66
+ getWorkspacePromptPath: (filename: string) =>
67
+ join(mockWorkspaceDir, "prompts", filename),
66
68
  }));
67
69
 
68
70
  mock.module("../contacts/contact-store.js", () => ({
@@ -96,6 +98,7 @@ mock.module("../contacts/guardian-delivery-reader.js", () => ({
96
98
  // Import AFTER mocks so the module under test binds to the stubbed
97
99
  // implementations.
98
100
  import { getGuardianDelivery } from "../contacts/guardian-delivery-reader.js";
101
+ import { resolveUserName } from "../daemon/identity-helpers.js";
99
102
  import type { TrustContext } from "../daemon/trust-context-types.js";
100
103
  import {
101
104
  ensureGuardianPersonaFile,
@@ -408,3 +411,154 @@ describe("resolveUserSlug (guardian trust WITH requester identity)", () => {
408
411
  expect(resolveUserSlug(trustContext)).toBeNull();
409
412
  });
410
413
  });
414
+
415
+ // ── resolveUserSlug — guardian bound on a non-vellum channel ───────
416
+ //
417
+ // warmGuardianBindings warms both the channel-filtered and unfiltered cache
418
+ // keys, but a guardian may be bound only on a non-vellum channel (phone,
419
+ // Telegram). A guardian-class turn sourced elsewhere must still resolve that
420
+ // guardian instead of silently landing on users/default.md.
421
+
422
+ describe("resolveUserSlug (guardian bound on a non-vellum channel)", () => {
423
+ test("falls back to the any-channel guardian when the source channel has none", () => {
424
+ mockGuardianDeliveries = [
425
+ { channelType: "telegram", address: "guardian-tg", status: "active" },
426
+ ];
427
+ mockContactsByAddress["telegram:guardian-tg"] = { userFile: "alice.md" };
428
+
429
+ const trustContext = {
430
+ sourceChannel: "vellum",
431
+ trustClass: "guardian",
432
+ } as TrustContext;
433
+
434
+ expect(resolveUserSlug(trustContext)).toBe("alice");
435
+ });
436
+
437
+ test("any-channel guardian with no userFile resolves the guardian.md fallback", () => {
438
+ mockGuardianDeliveries = [
439
+ { channelType: "telegram", address: "guardian-tg", status: "active" },
440
+ ];
441
+ mockContactsByAddress["telegram:guardian-tg"] = { userFile: null };
442
+
443
+ const trustContext = {
444
+ sourceChannel: "vellum",
445
+ trustClass: "guardian",
446
+ } as TrustContext;
447
+
448
+ expect(resolveUserSlug(trustContext)).toBe("guardian");
449
+ });
450
+ });
451
+
452
+ // ── resolveUserSlug — cold cache then construction-time warm ───────
453
+ //
454
+ // The exact shape scheduled tasks and memory consolidation pass through
455
+ // resolveInitialSystemPrompt: guardian class, no requester identity, built in
456
+ // a worker process whose guardian-delivery cache starts cold.
457
+
458
+ describe("resolveUserSlug (cold cache, background shape)", () => {
459
+ test("misses on a cold cache, resolves the guardian after the construction-time warm", async () => {
460
+ pendingWarmDeliveries = [
461
+ { channelType: "vellum", address: "vellum:self", status: "active" },
462
+ ];
463
+ mockContactsByAddress["vellum:vellum:self"] = { userFile: "alice.md" };
464
+
465
+ const trustContext = {
466
+ sourceChannel: "vellum",
467
+ trustClass: "guardian",
468
+ } as TrustContext;
469
+
470
+ // Cold: the sync peek finds nothing and the slug falls back to default.
471
+ expect(resolveUserSlug(trustContext)).toBeNull();
472
+
473
+ // resolveInitialSystemPrompt warms guardian bindings for guardian-class
474
+ // contexts before building; afterwards the same resolution succeeds.
475
+ await getGuardianDelivery({ channelTypes: ["vellum"] });
476
+
477
+ expect(resolveUserSlug(trustContext)).toBe("alice");
478
+ });
479
+ });
480
+
481
+ // ── resolveUserName — guardian-preferred display name ──────────────
482
+ //
483
+ // The sweep job and v2 router address the user by name in their prompts. The
484
+ // name must come from the guardian's own persona file when one is resolvable,
485
+ // falling back to users/default.md.
486
+
487
+ describe("resolveUserName", () => {
488
+ test("prefers the Name label from the guardian's own persona file over default.md", () => {
489
+ seedVellumGuardian("alice.md");
490
+ const usersDir = join(mockWorkspaceDir, "users");
491
+ mkdirSync(usersDir, { recursive: true });
492
+ writeFileSync(join(usersDir, "alice.md"), "**Name:** Alice\n", "utf-8");
493
+ writeFileSync(
494
+ join(usersDir, "default.md"),
495
+ "**Name:** Default User\n",
496
+ "utf-8",
497
+ );
498
+
499
+ expect(resolveUserName(mockWorkspaceDir)).toBe("Alice");
500
+ });
501
+
502
+ test("reads the onboarding-written Preferred name bullet", () => {
503
+ seedVellumGuardian("alice.md");
504
+ const usersDir = join(mockWorkspaceDir, "users");
505
+ mkdirSync(usersDir, { recursive: true });
506
+ writeFileSync(
507
+ join(usersDir, "alice.md"),
508
+ "# User Profile\n\n## Onboarding Context\n\n- **Preferred name:** Ali\n",
509
+ "utf-8",
510
+ );
511
+
512
+ expect(resolveUserName(mockWorkspaceDir)).toBe("Ali");
513
+ });
514
+
515
+ test("reads the scaffold's filled Preferred name/reference line", () => {
516
+ seedVellumGuardian("alice.md");
517
+ const usersDir = join(mockWorkspaceDir, "users");
518
+ mkdirSync(usersDir, { recursive: true });
519
+ writeFileSync(
520
+ join(usersDir, "alice.md"),
521
+ "- Preferred name/reference: Alice\n- Pronouns: she/her\n",
522
+ "utf-8",
523
+ );
524
+
525
+ expect(resolveUserName(mockWorkspaceDir)).toBe("Alice");
526
+ });
527
+
528
+ test("an unfilled scaffold does not swallow the next line and falls back to default.md", () => {
529
+ seedVellumGuardian("alice.md");
530
+ const usersDir = join(mockWorkspaceDir, "users");
531
+ mkdirSync(usersDir, { recursive: true });
532
+ // The bare scaffold: every label present, every value unfilled.
533
+ ensureGuardianPersonaFile("alice.md");
534
+ writeFileSync(join(usersDir, "default.md"), "**Name:** Bob\n", "utf-8");
535
+
536
+ expect(resolveUserName(mockWorkspaceDir)).toBe("Bob");
537
+ });
538
+
539
+ test("a declined_by_user sentinel is not used as a name", () => {
540
+ seedVellumGuardian("alice.md");
541
+ const usersDir = join(mockWorkspaceDir, "users");
542
+ mkdirSync(usersDir, { recursive: true });
543
+ writeFileSync(
544
+ join(usersDir, "alice.md"),
545
+ "- Preferred name/reference: declined_by_user\n",
546
+ "utf-8",
547
+ );
548
+ writeFileSync(join(usersDir, "default.md"), "**Name:** Bob\n", "utf-8");
549
+
550
+ expect(resolveUserName(mockWorkspaceDir)).toBe("Bob");
551
+ });
552
+
553
+ test("falls back to default.md when no guardian resolves", () => {
554
+ const usersDir = join(mockWorkspaceDir, "users");
555
+ mkdirSync(usersDir, { recursive: true });
556
+ writeFileSync(join(usersDir, "default.md"), "**Name:** Bob\n", "utf-8");
557
+
558
+ expect(resolveUserName(mockWorkspaceDir)).toBe("Bob");
559
+ });
560
+
561
+ test("returns null when neither file yields a name", () => {
562
+ expect(resolveUserName(mockWorkspaceDir)).toBeNull();
563
+ });
564
+ });
@@ -0,0 +1,170 @@
1
+ /**
2
+ * `resolveWebhookUrl`: the URL a plugin hands a vendor.
3
+ *
4
+ * The resolution order itself belongs to `resolveCallbackUrl`, which is spied
5
+ * on here rather than reimplemented. Restating it would recreate the
6
+ * duplication this module exists to remove. What is covered is what this
7
+ * module adds: which plugin the route belongs to, the path it composes, the
8
+ * registration type it derives, and what it refuses.
9
+ */
10
+
11
+ import { describe, expect, spyOn, test } from "bun:test";
12
+
13
+ import * as registration from "../inbound/platform-callback-registration.js";
14
+ import { resolveWebhookUrl } from "../plugin-api/webhook-url.js";
15
+ import { runInPluginContext } from "../plugins/plugin-execution-context.js";
16
+
17
+ describe("resolveWebhookUrl", () => {
18
+ test("composes the plugin's namespaced path and delegates the tier choice", async () => {
19
+ const spy = spyOn(registration, "resolveCallbackUrl").mockResolvedValue(
20
+ "https://callbacks.vellum.ai/abc/webhooks/plugins/imessage/events-photon",
21
+ );
22
+
23
+ const url = await resolveWebhookUrl({
24
+ plugin: "imessage",
25
+ path: "events-photon",
26
+ });
27
+
28
+ expect(url).toBe(
29
+ "https://callbacks.vellum.ai/abc/webhooks/plugins/imessage/events-photon",
30
+ );
31
+ expect(spy.mock.calls[0]![1]).toBe(
32
+ "webhooks/plugins/imessage/events-photon",
33
+ );
34
+ spy.mockRestore();
35
+ });
36
+
37
+ test("defaults to the plugin in context", async () => {
38
+ // Hooks and tools run in plugin context, so the common caller passes no
39
+ // plugin at all.
40
+ const spy = spyOn(registration, "resolveCallbackUrl").mockResolvedValue(
41
+ "https://example.test/x",
42
+ );
43
+
44
+ await runInPluginContext("imessage", () =>
45
+ resolveWebhookUrl({ path: "events-comms" }),
46
+ );
47
+
48
+ expect(spy.mock.calls[0]![1]).toBe(
49
+ "webhooks/plugins/imessage/events-comms",
50
+ );
51
+ spy.mockRestore();
52
+ });
53
+
54
+ test("an explicit plugin wins over the one in context", async () => {
55
+ const spy = spyOn(registration, "resolveCallbackUrl").mockResolvedValue(
56
+ "https://example.test/x",
57
+ );
58
+
59
+ await runInPluginContext("other", () =>
60
+ resolveWebhookUrl({ plugin: "imessage", path: "events-comms" }),
61
+ );
62
+
63
+ expect(spy.mock.calls[0]![1]).toBe(
64
+ "webhooks/plugins/imessage/events-comms",
65
+ );
66
+ spy.mockRestore();
67
+ });
68
+
69
+ test("says so when there is no plugin to attribute the route to", async () => {
70
+ await expect(resolveWebhookUrl({ path: "events" })).rejects.toThrow(
71
+ /No plugin is in context/,
72
+ );
73
+ });
74
+
75
+ test("gives routes that share a slug distinct registration types", async () => {
76
+ // The platform keys a callback route by type, so a collision has the
77
+ // second registration replace the first. `events-v1` and `events_v1`
78
+ // slugify identically.
79
+ const spy = spyOn(registration, "resolveCallbackUrl").mockResolvedValue(
80
+ "https://example.test/x",
81
+ );
82
+
83
+ await resolveWebhookUrl({ plugin: "imessage", path: "events-v1" });
84
+ await resolveWebhookUrl({ plugin: "imessage", path: "events_v1" });
85
+
86
+ const types = spy.mock.calls.map((call) => call[2]);
87
+ expect(new Set(types).size).toBe(2);
88
+ spy.mockRestore();
89
+ });
90
+
91
+ test("accepts the paths the gateway's route schema accepts", async () => {
92
+ // A path this rejects but the gateway serves is a route a plugin can
93
+ // install and then never resolve a URL for.
94
+ const spy = spyOn(registration, "resolveCallbackUrl").mockResolvedValue(
95
+ "https://example.test/x",
96
+ );
97
+
98
+ for (const path of [
99
+ "events-photon",
100
+ "events:v1",
101
+ "hooks/@vendor",
102
+ "a/b/c",
103
+ ]) {
104
+ await expect(
105
+ resolveWebhookUrl({ plugin: "imessage", path }),
106
+ ).resolves.toBeDefined();
107
+ }
108
+ spy.mockRestore();
109
+ });
110
+
111
+ test("refuses a plugin name or path that escapes the namespace", async () => {
112
+ // The prefix is fixed so a plugin cannot claim another's routes. That only
113
+ // holds if the segments cannot escape it either.
114
+ for (const options of [
115
+ { plugin: "../vellum", path: "events" },
116
+ { plugin: "imessage", path: "../../admin" },
117
+ { plugin: "imessage", path: "/events" },
118
+ { plugin: "imessage", path: "events/" },
119
+ { plugin: "imessage", path: "events?x=1" },
120
+ { plugin: "imessage", path: "%2e%2e/admin" },
121
+ { plugin: "imessage", path: "" },
122
+ ]) {
123
+ await expect(resolveWebhookUrl(options)).rejects.toThrow(/Invalid/);
124
+ }
125
+ });
126
+
127
+ test("hands the vendor a URL with no trailing slash", async () => {
128
+ // The platform appends one, and a provider delivers to the URL it was
129
+ // given verbatim. The gateway matches the declared path, which never ends
130
+ // in a slash, so a registration carrying one 404s every delivery.
131
+ const spy = spyOn(registration, "resolveCallbackUrl").mockResolvedValue(
132
+ "https://callbacks.vellum.ai/abc/webhooks/plugins/imessage/events-comms/",
133
+ );
134
+
135
+ await expect(
136
+ resolveWebhookUrl({ plugin: "imessage", path: "events-comms" }),
137
+ ).resolves.toBe(
138
+ "https://callbacks.vellum.ai/abc/webhooks/plugins/imessage/events-comms",
139
+ );
140
+ spy.mockRestore();
141
+ });
142
+
143
+ test("leaves a URL carrying a query string alone", async () => {
144
+ // Trimming there would cut into the query rather than the path.
145
+ const spy = spyOn(registration, "resolveCallbackUrl").mockResolvedValue(
146
+ "https://example.test/webhooks/plugins/imessage/events-comms?token=abc/",
147
+ );
148
+
149
+ await expect(
150
+ resolveWebhookUrl({ plugin: "imessage", path: "events-comms" }),
151
+ ).resolves.toBe(
152
+ "https://example.test/webhooks/plugins/imessage/events-comms?token=abc/",
153
+ );
154
+ spy.mockRestore();
155
+ });
156
+
157
+ test("propagates the failure when nothing can answer", async () => {
158
+ // No ingress and no platform connection means there is no URL that works.
159
+ // Returning a plausible one would produce a vendor registration that
160
+ // silently receives nothing.
161
+ const spy = spyOn(registration, "resolveCallbackUrl").mockRejectedValue(
162
+ new Error("Public ingress URL is not configured"),
163
+ );
164
+
165
+ await expect(
166
+ resolveWebhookUrl({ plugin: "imessage", path: "events-photon" }),
167
+ ).rejects.toThrow("Public ingress URL is not configured");
168
+ spy.mockRestore();
169
+ });
170
+ });
@@ -115,6 +115,12 @@ const BASELINE: Record<string, readonly string[]> = {
115
115
  "../../../../config/skill-state.js",
116
116
  "../../../../config/skills.js",
117
117
  "../../../../config/types.js",
118
+ // Same host module the retrospective job couples to (the `../../../`
119
+ // entry below): the sweep job and v2 router warm the guardian-delivery
120
+ // cache before their sync `resolveUserName` reads so worker-process
121
+ // prompts address the guardian instead of the default profile. No
122
+ // plugin-api equivalent.
123
+ "../../../../contacts/guardian-delivery-reader.js",
118
124
  "../../../../context/token-estimator.js",
119
125
  "../../../../daemon/conversation-error.js",
120
126
  "../../../../daemon/conversation-notices.js",
@@ -1,9 +1,15 @@
1
1
  /**
2
2
  * Regression: `Conversation.setProcessing(value)` flips the in-memory flag and
3
- * then persists it to the `processing_started_at` column. If that DB write
4
- * throws (e.g. SQLITE_BUSY under contention), the in-memory flag must not be
5
- * left stranded out of sync with the column — it reverts to its prior value
6
- * and the error re-throws so callers' existing failure handling still runs.
3
+ * then persists it to the `processing_started_at` column. The two directions
4
+ * treat a failed write differently, and both are covered here.
5
+ *
6
+ * Acquiring is strict: the in-memory flag reverts to its prior value and the
7
+ * error re-throws so callers' existing failure handling still runs.
8
+ *
9
+ * Clearing is not: the in-memory flag is this process's queue gate while the
10
+ * column is advisory state the boot-time stale-processing sweep recovers, so a
11
+ * failed mirror write must still leave the conversation released rather than
12
+ * latching it "busy" for the rest of the daemon's life.
7
13
  */
8
14
  import { beforeEach, describe, expect, mock, test } from "bun:test";
9
15
 
@@ -209,17 +215,16 @@ describe("Conversation.setProcessing persistence failure", () => {
209
215
  expect(conversation.isProcessing()).toBe(false);
210
216
  });
211
217
 
212
- test("restores the prior value when clearing fails mid-turn", () => {
218
+ test("releases in memory when the clear's mirror write fails", () => {
213
219
  conversation.setProcessing(true);
214
220
  expect(conversation.isProcessing()).toBe(true);
215
221
 
216
222
  persistShouldThrow = true;
217
- expect(() => conversation.setProcessing(false)).toThrow(
218
- "database is locked",
219
- );
223
+ expect(() => conversation.setProcessing(false)).not.toThrow();
220
224
 
221
- // Clear didn't persist, so the flag stays consistent with the column.
222
- expect(conversation.isProcessing()).toBe(true);
225
+ // The queue gate is the in-memory flag, so a failed advisory write must
226
+ // not leave the conversation rejecting every later send as "busy".
227
+ expect(conversation.isProcessing()).toBe(false);
223
228
  });
224
229
 
225
230
  test("commits the flag when the DB write succeeds", () => {
@@ -0,0 +1,113 @@
1
+ /**
2
+ * `computeProfileAvailability` on profiles that carry no dispatchable pair.
3
+ *
4
+ * A rung only wins if its profile carries both a provider and a model
5
+ * (`usableEntry` in config/llm-resolver.ts). A profile missing either is
6
+ * skipped and the call site resolves elsewhere, so the profiles read has to
7
+ * report that state rather than returning "nothing to judge", which clients
8
+ * render as healthy.
9
+ */
10
+
11
+ import { describe, expect, test } from "bun:test";
12
+
13
+ import {
14
+ computeProfileAvailability,
15
+ isUnavailable,
16
+ } from "../providers/inference/connection-availability.js";
17
+ import { describeUnavailableProfile } from "../runtime/routes/inference-profile-availability-guard.js";
18
+
19
+ describe("incomplete profiles", () => {
20
+ test("no provider and no model reports both as missing", async () => {
21
+ const availability = await computeProfileAvailability({});
22
+ expect(availability?.status).toBe("incomplete");
23
+ expect(availability?.message).toContain("a provider and a model");
24
+ });
25
+
26
+ test("a model with no provider names the provider", async () => {
27
+ const availability = await computeProfileAvailability({
28
+ model: "claude-opus-5",
29
+ });
30
+ expect(availability?.status).toBe("incomplete");
31
+ expect(availability?.message).toContain("a provider");
32
+ expect(availability?.message).not.toContain("a provider and a model");
33
+ });
34
+
35
+ test("a provider with no model names the model", async () => {
36
+ const availability = await computeProfileAvailability({
37
+ provider: "anthropic",
38
+ });
39
+ expect(availability?.status).toBe("incomplete");
40
+ expect(availability?.message).toContain("a model");
41
+ });
42
+
43
+ // The destination is deliberately unnamed: which profile a call site falls
44
+ // back to depends on the call site, so the message states the effect only.
45
+ test("the message names the effect without naming a destination", async () => {
46
+ const availability = await computeProfileAvailability({});
47
+ expect(availability?.message).toContain("fall back to another profile");
48
+ });
49
+ });
50
+
51
+ describe("mix profiles", () => {
52
+ // A mix carries no provider or model of its own: the resolver expands it to
53
+ // a seeded arm and judges that arm. Reporting a mix as incomplete would put
54
+ // a warning on every working mix profile.
55
+ test("a mix still reports nothing to judge", async () => {
56
+ const availability = await computeProfileAvailability({
57
+ mix: [
58
+ { profile: "balanced", weight: 1 },
59
+ { profile: "quality", weight: 1 },
60
+ ],
61
+ });
62
+ expect(availability).toBeNull();
63
+ });
64
+
65
+ test("a mix that also names a provider and model is judged normally", async () => {
66
+ const availability = await computeProfileAvailability({
67
+ mix: [{ profile: "balanced", weight: 1 }],
68
+ provider: "anthropic",
69
+ model: "claude-opus-5",
70
+ });
71
+ expect(availability?.status).not.toBe("incomplete");
72
+ });
73
+ });
74
+
75
+ describe("selection guards", () => {
76
+ // The resolver skips an incomplete profile on every turn, so pinning one
77
+ // means the user's selection is not what runs. Before `incomplete` existed,
78
+ // a provider without a model reached the auto-resolve branch and could
79
+ // return `missing_connection`, which these guards already rejected; the new
80
+ // verdict has to keep that door shut rather than open it.
81
+ test("an incomplete profile counts as unavailable", async () => {
82
+ expect(isUnavailable(await computeProfileAvailability({}))).toBe(true);
83
+ expect(
84
+ isUnavailable(
85
+ await computeProfileAvailability({ provider: "anthropic" }),
86
+ ),
87
+ ).toBe(true);
88
+ });
89
+
90
+ test("a mix is still not judged, so it is not blocked", async () => {
91
+ expect(
92
+ isUnavailable(
93
+ await computeProfileAvailability({
94
+ mix: [{ profile: "balanced", weight: 1 }],
95
+ }),
96
+ ),
97
+ ).toBe(false);
98
+ });
99
+
100
+ // `describeUnavailableProfile` interpolates the provider, which an
101
+ // incomplete profile may not have. Its guidance must not leak "undefined".
102
+ test("the repair guidance names the fix without inventing a provider", async () => {
103
+ const availability = await computeProfileAvailability({});
104
+ const message = await describeUnavailableProfile({
105
+ availability: availability!,
106
+ provider: String(undefined),
107
+ repair: { kind: "repoint", profileName: "half-made" },
108
+ escapeHatch: false,
109
+ });
110
+ expect(message).not.toContain("undefined");
111
+ expect(message).toContain("provider and a model");
112
+ });
113
+ });
@@ -185,11 +185,11 @@ describe("Profiler routes", () => {
185
185
  );
186
186
  });
187
187
 
188
- test(`POST export rejects runId "${payload}"`, () => {
188
+ test(`POST export rejects runId "${payload}"`, async () => {
189
189
  const route = findRoute("profiler_runs_by_runId_export_post")!;
190
- expect(() => route.handler({ pathParams: { runId: payload } })).toThrow(
191
- BadRequestError,
192
- );
190
+ await expect(
191
+ route.handler({ pathParams: { runId: payload } }),
192
+ ).rejects.toThrow(BadRequestError);
193
193
  });
194
194
 
195
195
  test(`DELETE rejects runId "${payload}"`, () => {
@@ -270,29 +270,29 @@ describe("Profiler routes", () => {
270
270
  });
271
271
 
272
272
  describe("POST /v1/profiler/runs/:runId/export", () => {
273
- test("returns 404 for missing run", () => {
273
+ test("returns 404 for missing run", async () => {
274
274
  const route = findRoute("profiler_runs_by_runId_export_post")!;
275
- expect(() =>
275
+ await expect(
276
276
  route.handler({ pathParams: { runId: "nonexistent" } }),
277
- ).toThrow(NotFoundError);
277
+ ).rejects.toThrow(NotFoundError);
278
278
  });
279
279
 
280
- test("returns tar.gz bytes for a valid run", () => {
280
+ test("returns tar.gz bytes for a valid run", async () => {
281
281
  createRun("exportable-run", {
282
282
  sizeBytes: 512,
283
283
  manifest: { status: "completed" },
284
284
  });
285
285
 
286
286
  const route = findRoute("profiler_runs_by_runId_export_post")!;
287
- const result = route.handler({
287
+ const result = (await route.handler({
288
288
  pathParams: { runId: "exportable-run" },
289
- }) as Uint8Array;
289
+ })) as Uint8Array;
290
290
 
291
291
  expect(result).toBeInstanceOf(Uint8Array);
292
292
  expect(result.byteLength).toBeGreaterThan(0);
293
293
  });
294
294
 
295
- test("returns 500 when archive exceeds size limit", () => {
295
+ test("returns 500 when archive exceeds size limit", async () => {
296
296
  // Create a run with a very large file that will exceed the 50MB archive cap.
297
297
  const runDir = join(runsDir, "huge-run");
298
298
  mkdirSync(runDir, { recursive: true });
@@ -325,9 +325,9 @@ describe("Profiler routes", () => {
325
325
  }
326
326
 
327
327
  const route = findRoute("profiler_runs_by_runId_export_post")!;
328
- expect(() =>
328
+ await expect(
329
329
  route.handler({ pathParams: { runId: "huge-run" } }),
330
- ).toThrow(InternalError);
330
+ ).rejects.toThrow(InternalError);
331
331
  }, 30000);
332
332
  });
333
333