@vellumai/assistant 0.10.9 → 0.10.10-dev.202607162206.d08e98e

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 (456) hide show
  1. package/ARCHITECTURE.md +1 -1
  2. package/Dockerfile +8 -0
  3. package/docs/activation-funnel-telemetry.md +13 -7
  4. package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/package.json +1 -0
  5. package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/__tests__/redacted-credential.test.ts +200 -0
  6. package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/redacted-credential.ts +226 -0
  7. package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/package.json +1 -0
  8. package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/__tests__/redacted-credential.test.ts +200 -0
  9. package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/redacted-credential.ts +226 -0
  10. package/node_modules/@vellumai/service-contracts/package.json +1 -0
  11. package/node_modules/@vellumai/service-contracts/src/__tests__/redacted-credential.test.ts +200 -0
  12. package/node_modules/@vellumai/service-contracts/src/redacted-credential.ts +226 -0
  13. package/openapi.yaml +530 -107
  14. package/package.json +1 -1
  15. package/scripts/generate-openapi.ts +8 -0
  16. package/src/__tests__/activation-early-marking.test.ts +6 -5
  17. package/src/__tests__/agent-loop-override-profile.test.ts +22 -25
  18. package/src/__tests__/agent-wake-override-profile.test.ts +21 -48
  19. package/src/__tests__/app-builder-tool-scripts.test.ts +0 -1
  20. package/src/__tests__/app-bundler.test.ts +4 -12
  21. package/src/__tests__/app-executors.test.ts +2 -49
  22. package/src/__tests__/app-routes-csp.test.ts +114 -146
  23. package/src/__tests__/auth-fallback-events-store.test.ts +8 -1
  24. package/src/__tests__/build-persisted-content.test.ts +96 -0
  25. package/src/__tests__/bundle-scanner.test.ts +27 -1
  26. package/src/__tests__/call-controller.test.ts +291 -0
  27. package/src/__tests__/call-site-routing-connection-auto-resolve.test.ts +165 -0
  28. package/src/__tests__/chat-credential-redaction.test.ts +1395 -0
  29. package/src/__tests__/chat-reveal-guard-priming.test.ts +791 -0
  30. package/src/__tests__/compaction.benchmark.test.ts +2 -1
  31. package/src/__tests__/compactor-image-manifest-trust.test.ts +50 -0
  32. package/src/__tests__/config-loader-backfill.test.ts +9 -4
  33. package/src/__tests__/config-schema-cmd.test.ts +10 -11
  34. package/src/__tests__/config-schema.test.ts +190 -257
  35. package/src/__tests__/conversation-agent-loop-fatal-cleanup.test.ts +208 -0
  36. package/src/__tests__/conversation-agent-loop-overflow.test.ts +5 -19
  37. package/src/__tests__/conversation-agent-loop.test.ts +22 -9
  38. package/src/__tests__/conversation-error.test.ts +31 -0
  39. package/src/__tests__/conversation-load-history-repair.test.ts +110 -0
  40. package/src/__tests__/conversation-process-callsite.test.ts +12 -19
  41. package/src/__tests__/conversation-routes-slash-commands.test.ts +7 -21
  42. package/src/__tests__/conversation-summarize-route.test.ts +36 -44
  43. package/src/__tests__/conversation-surfaces-action-delivery.test.ts +1 -0
  44. package/src/__tests__/conversation-surfaces-activation-emit.test.ts +7 -7
  45. package/src/__tests__/conversation-tool-setup-app-refresh.test.ts +0 -1
  46. package/src/__tests__/conversation-tool-setup-attribution.test.ts +0 -1
  47. package/src/__tests__/conversation-usage.test.ts +4 -12
  48. package/src/__tests__/credential-routes.test.ts +250 -0
  49. package/src/__tests__/credential-security-invariants.test.ts +3 -0
  50. package/src/__tests__/db-migration-rollback.test.ts +22 -0
  51. package/src/__tests__/empty-response-hook.test.ts +186 -1
  52. package/src/__tests__/external-plugin-loader.test.ts +11 -5
  53. package/src/__tests__/heartbeat-service.test.ts +0 -28
  54. package/src/__tests__/host-shell-tool.test.ts +2 -0
  55. package/src/__tests__/inactive-tool-error-messages.test.ts +2 -2
  56. package/src/__tests__/inference-no-mode-boot-e2e.test.ts +52 -8
  57. package/src/__tests__/internal-telemetry-routes.test.ts +23 -4
  58. package/src/__tests__/invite-routes-http.test.ts +12 -16
  59. package/src/__tests__/list-all-apps.test.ts +0 -4
  60. package/src/__tests__/llm-context-resolution.test.ts +32 -56
  61. package/src/__tests__/llm-request-log-turn-query.test.ts +109 -0
  62. package/src/__tests__/llm-resolver-override-or-default.test.ts +3 -52
  63. package/src/__tests__/llm-resolver.test.ts +342 -602
  64. package/src/__tests__/llm-schema.test.ts +79 -37
  65. package/src/__tests__/max-tokens-continue-hook.test.ts +19 -0
  66. package/src/__tests__/media-stream-output.test.ts +259 -3
  67. package/src/__tests__/media-stream-server-integration.test.ts +22 -1
  68. package/src/__tests__/media-stream-stt-session.test.ts +47 -0
  69. package/src/__tests__/memory-jobs-worker-cleanup-cadence.test.ts +33 -0
  70. package/src/__tests__/memory-recall-log-store.test.ts +47 -13
  71. package/src/__tests__/mock-gateway-ipc.ts +46 -1
  72. package/src/__tests__/mtime-cache.test.ts +61 -0
  73. package/src/__tests__/navigate-settings-tab.test.ts +2 -0
  74. package/src/__tests__/normalize-onboarding.test.ts +33 -0
  75. package/src/__tests__/onboarding-persona-write.test.ts +26 -0
  76. package/src/__tests__/plugin-api-resolve-credential.test.ts +140 -0
  77. package/src/__tests__/plugin-app-serve-routes.test.ts +166 -14
  78. package/src/__tests__/plugin-import-boundary-reverse-guard.test.ts +2 -0
  79. package/src/__tests__/post-turn-tool-result-truncation.test.ts +38 -0
  80. package/src/__tests__/provider-commit-message-generator.test.ts +27 -20
  81. package/src/__tests__/provider-connections-backfill.test.ts +138 -0
  82. package/src/__tests__/provider-platform-proxy-integration.test.ts +10 -31
  83. package/src/__tests__/provider-registry-ollama.test.ts +8 -18
  84. package/src/__tests__/provider-send-message-override-profile.test.ts +23 -23
  85. package/src/__tests__/provider-usage-tracking.test.ts +9 -19
  86. package/src/__tests__/prune-old-conversations-job.test.ts +12 -0
  87. package/src/__tests__/published-app-updater.test.ts +22 -16
  88. package/src/__tests__/registry.test.ts +5 -16
  89. package/src/__tests__/retry-openrouter-only-normalization.test.ts +22 -23
  90. package/src/__tests__/retry-thinking-adaptive-only.test.ts +43 -48
  91. package/src/__tests__/retry-thinking-tool-choice.test.ts +57 -66
  92. package/src/__tests__/retry-verbosity-normalization.test.ts +24 -23
  93. package/src/__tests__/reveal-success-registry.test.ts +123 -0
  94. package/src/__tests__/run-conversation-turn-persistence.test.ts +130 -0
  95. package/src/__tests__/secret-fixtures.ts +9 -0
  96. package/src/__tests__/server-history-render.test.ts +28 -0
  97. package/src/__tests__/skills.test.ts +9 -4
  98. package/src/__tests__/slack-share-routes.test.ts +0 -1
  99. package/src/__tests__/stt-stream-session.test.ts +6 -5
  100. package/src/__tests__/subagent-call-site-routing.test.ts +69 -95
  101. package/src/__tests__/subagent-disposal.test.ts +2 -0
  102. package/src/__tests__/subagent-fork-notifications.test.ts +2 -0
  103. package/src/__tests__/subagent-fork-spawn.test.ts +2 -0
  104. package/src/__tests__/subagent-manager-notify.test.ts +2 -0
  105. package/src/__tests__/subagent-role-registry.test.ts +37 -0
  106. package/src/__tests__/subagent-spawn-and-await.test.ts +1 -0
  107. package/src/__tests__/subagent-terminal-message.test.ts +50 -0
  108. package/src/__tests__/subagent-tool-gate-mode.test.ts +78 -5
  109. package/src/__tests__/surface-completion-nudge-hook.test.ts +19 -0
  110. package/src/__tests__/telemetry-routes.test.ts +99 -19
  111. package/src/__tests__/tool-audit.test.ts +34 -4
  112. package/src/__tests__/tool-executor-lifecycle-events.test.ts +1 -1
  113. package/src/__tests__/tool-profiler.test.ts +72 -1
  114. package/src/__tests__/tool-result-spool.test.ts +49 -4
  115. package/src/__tests__/tool-side-effects-slack-dm.test.ts +0 -1
  116. package/src/__tests__/ui-channel-variants.test.ts +108 -0
  117. package/src/__tests__/ui-shape-teaching.test.ts +255 -0
  118. package/src/__tests__/usage-attribution.test.ts +18 -41
  119. package/src/__tests__/user-plugin-loader.test.ts +4 -4
  120. package/src/__tests__/voice-config-update.test.ts +46 -0
  121. package/src/__tests__/voice-session-bridge.test.ts +216 -84
  122. package/src/__tests__/workspace-migration-131-drop-web-fetch-mode.test.ts +120 -0
  123. package/src/agent/loop.ts +4 -3
  124. package/src/api/events/open-conversation.test.ts +64 -0
  125. package/src/api/events/open-conversation.ts +33 -0
  126. package/src/api/index.ts +6 -0
  127. package/src/api/responses/conversation-message.ts +5 -0
  128. package/src/apps/app-store.ts +25 -35
  129. package/src/bundler/app-bundler.ts +34 -48
  130. package/src/bundler/app-compiler.ts +39 -4
  131. package/src/bundler/bundle-scanner.ts +13 -0
  132. package/src/bundler/manifest.ts +1 -1
  133. package/src/calls/__tests__/voice-session-bridge.test.ts +47 -0
  134. package/src/calls/call-constants.ts +5 -0
  135. package/src/calls/call-controller.ts +100 -32
  136. package/src/calls/call-transport.ts +9 -0
  137. package/src/calls/media-stream-output.ts +107 -4
  138. package/src/calls/media-stream-server.ts +29 -9
  139. package/src/calls/media-stream-stt-session.ts +7 -1
  140. package/src/calls/media-turn-detector.ts +11 -1
  141. package/src/calls/voice-session-bridge.ts +84 -63
  142. package/src/cli/commands/__tests__/inference-providers.test.ts +270 -33
  143. package/src/cli/commands/__tests__/notifications.test.ts +24 -3
  144. package/src/cli/commands/config.help.ts +6 -6
  145. package/src/cli/commands/credentials.help.ts +13 -0
  146. package/src/cli/commands/credentials.ts +6 -1
  147. package/src/cli/commands/email.help.ts +7 -0
  148. package/src/cli/commands/email.ts +35 -1
  149. package/src/cli/commands/inference-providers.ts +168 -107
  150. package/src/cli/commands/inference.help.ts +118 -46
  151. package/src/cli/commands/memory/index.help.ts +23 -0
  152. package/src/cli/commands/memory/nodes.ts +146 -0
  153. package/src/cli/commands/notifications.help.ts +10 -10
  154. package/src/cli/commands/oauth/connect-surface-guidance.test.ts +40 -0
  155. package/src/cli/commands/oauth/connect-surface-guidance.ts +54 -0
  156. package/src/cli/commands/oauth/connect.test.ts +126 -0
  157. package/src/cli/commands/oauth/connect.ts +29 -6
  158. package/src/cli/commands/oauth/index.help.ts +7 -1
  159. package/src/cli/commands/oauth/status.test.ts +69 -3
  160. package/src/cli/commands/oauth/status.ts +50 -12
  161. package/src/cli/commands/plugins.help.ts +13 -2
  162. package/src/cli/commands/plugins.ts +61 -7
  163. package/src/cli/commands/telemetry.help.ts +13 -0
  164. package/src/cli/commands/telemetry.ts +45 -5
  165. package/src/cli/lib/__tests__/plugin-catalog-local.test.ts +8 -2
  166. package/src/cli/lib/__tests__/upgrade-plugin.test.ts +213 -9
  167. package/src/cli/lib/bundled-marketplace.json +93 -36
  168. package/src/cli/lib/inspect-plugin.ts +5 -1
  169. package/src/cli/lib/install-from-github.ts +68 -4
  170. package/src/cli/lib/plugin-catalog-local.ts +6 -2
  171. package/src/cli/lib/plugin-fingerprint.ts +3 -3
  172. package/src/cli/lib/upgrade-plugin.ts +236 -35
  173. package/src/config/__tests__/default-profile-catalog.test.ts +8 -12
  174. package/src/config/__tests__/plugin-resident-skill-discovery.test.ts +137 -0
  175. package/src/config/__tests__/profile-materialization.test.ts +1 -88
  176. package/src/config/bundled-skills/AGENTS.md +3 -30
  177. package/src/config/bundled-skills/app-builder/SKILL.md +5 -3
  178. package/src/config/bundled-skills/app-builder/TOOLS.json +23 -0
  179. package/src/config/bundled-skills/app-builder/tools/app-open.ts +32 -0
  180. package/src/config/bundled-skills/messaging/tools/messaging-send.ts +1 -1
  181. package/src/config/bundled-skills/phone-calls/references/CONFIG.md +7 -7
  182. package/src/config/bundled-skills/schedule/SKILL.md +1 -1
  183. package/src/config/bundled-skills/schedule/TOOLS.json +1 -1
  184. package/src/config/bundled-skills/settings/TOOLS.json +3 -1
  185. package/src/config/bundled-skills/settings/tools/navigate-settings-tab.ts +2 -0
  186. package/src/config/bundled-skills/settings/tools/voice-config-update.ts +18 -10
  187. package/src/config/bundled-skills/subagent/SKILL.md +2 -0
  188. package/src/config/bundled-skills/subagent/TOOLS.json +1 -1
  189. package/src/config/call-site-defaults.ts +3 -3
  190. package/src/config/feature-flag-registry.json +24 -39
  191. package/src/config/llm-resolver.ts +88 -475
  192. package/src/config/profile-materialization.ts +11 -11
  193. package/src/config/schema.ts +93 -127
  194. package/src/config/schemas/__tests__/live-voice.test.ts +24 -6
  195. package/src/config/schemas/__tests__/stt.test.ts +31 -3
  196. package/src/config/schemas/live-voice.ts +10 -4
  197. package/src/config/schemas/llm.ts +52 -15
  198. package/src/config/schemas/memory-lifecycle.ts +1 -1
  199. package/src/config/schemas/memory-retrospective.ts +8 -0
  200. package/src/config/schemas/memory-v2.ts +2 -2
  201. package/src/config/schemas/memory-v3.ts +1 -1
  202. package/src/config/schemas/services.ts +6 -3
  203. package/src/config/schemas/stt.ts +38 -21
  204. package/src/config/schemas/tts.ts +13 -18
  205. package/src/config/skills.ts +31 -21
  206. package/src/context/compactor.ts +44 -7
  207. package/src/context/post-turn-tool-result-truncation.ts +4 -2
  208. package/src/context/tool-result-spool.ts +26 -5
  209. package/src/conversations/__tests__/message-consolidation.test.ts +48 -0
  210. package/src/conversations/message-consolidation.ts +22 -2
  211. package/src/daemon/__tests__/conversation-tool-setup.test.ts +5 -12
  212. package/src/daemon/app-source-watcher.ts +17 -23
  213. package/src/daemon/chat-credential-redaction.ts +1365 -0
  214. package/src/daemon/conversation-agent-loop-handlers.ts +627 -34
  215. package/src/daemon/conversation-agent-loop.ts +71 -31
  216. package/src/daemon/conversation-error.ts +36 -14
  217. package/src/daemon/conversation-process.ts +22 -0
  218. package/src/daemon/conversation-store.ts +35 -0
  219. package/src/daemon/conversation-surfaces.ts +25 -25
  220. package/src/daemon/conversation-tool-setup.ts +33 -7
  221. package/src/daemon/conversation.ts +55 -5
  222. package/src/daemon/handlers/shared.ts +33 -2
  223. package/src/daemon/lifecycle.ts +4 -4
  224. package/src/daemon/message-types/conversations.ts +6 -17
  225. package/src/daemon/providers-setup.ts +8 -0
  226. package/src/daemon/tool-setup-types.ts +7 -1
  227. package/src/daemon/wake-conversation-ops.ts +10 -1
  228. package/src/hooks/types.ts +9 -0
  229. package/src/ipc/__tests__/email-ipc.test.ts +90 -0
  230. package/src/ipc/gateway-client.test.ts +59 -0
  231. package/src/ipc/gateway-client.ts +70 -27
  232. package/src/live-voice/__tests__/live-voice-events.test.ts +14 -2
  233. package/src/live-voice/__tests__/live-voice-integration.test.ts +116 -2
  234. package/src/live-voice/__tests__/live-voice-vad.test.ts +804 -13
  235. package/src/live-voice/__tests__/protocol.test.ts +122 -0
  236. package/src/live-voice/live-voice-session.ts +443 -40
  237. package/src/live-voice/protocol.ts +143 -1
  238. package/src/monitoring/__tests__/plugin-source-watch.test.ts +3 -1
  239. package/src/monitoring/plugin-source-watch.ts +3 -62
  240. package/src/notifications/README.md +1 -1
  241. package/src/permissions/checker.ts +8 -4
  242. package/src/persistence/__tests__/db-init-migrations-ok.test.ts +26 -0
  243. package/src/persistence/conversation-crud.ts +104 -2
  244. package/src/persistence/db-init.ts +14 -3
  245. package/src/persistence/job-handlers/cleanup.ts +15 -7
  246. package/src/persistence/llm-request-log-store.ts +88 -52
  247. package/src/persistence/migrations/298-move-memory-jobs-to-memory-db.ts +7 -31
  248. package/src/persistence/migrations/305-drop-contact-acl-columns.ts +3 -2
  249. package/src/persistence/migrations/326-move-injection-events-to-memory-db.ts +8 -34
  250. package/src/persistence/migrations/336-move-memory-v2-activation-logs-to-memory-db.ts +90 -0
  251. package/src/persistence/migrations/337-move-memory-recall-logs-to-memory-db.ts +114 -0
  252. package/src/persistence/migrations/338-move-memory-v3-selections-to-memory-db.ts +84 -0
  253. package/src/persistence/migrations/339-move-activation-sessions-to-memory-db.ts +52 -0
  254. package/src/persistence/migrations/__tests__/run-migrations.test.ts +155 -0
  255. package/src/persistence/migrations/helpers/relocation.ts +44 -1
  256. package/src/persistence/migrations/run-migrations.ts +25 -1
  257. package/src/persistence/schema/infrastructure.ts +9 -0
  258. package/src/persistence/schema/memory-core.ts +3 -0
  259. package/src/persistence/schema/memory-injection.ts +2 -0
  260. package/src/persistence/steps.ts +36 -0
  261. package/src/platform/client.test.ts +1 -44
  262. package/src/platform/client.ts +10 -20
  263. package/src/platform/consent-cache.test.ts +89 -23
  264. package/src/platform/consent-cache.ts +71 -33
  265. package/src/plugin-api/constants.ts +12 -0
  266. package/src/plugin-api/conversation-turn.ts +37 -14
  267. package/src/plugin-api/index.ts +11 -1
  268. package/src/plugin-api/resolve-credential.ts +75 -0
  269. package/src/plugin-api/vision-support.test.ts +8 -19
  270. package/src/plugin-api/vision-support.ts +26 -17
  271. package/src/plugins/collect-source-versions.ts +77 -0
  272. package/src/plugins/defaults/compaction/compact.ts +6 -0
  273. package/src/plugins/defaults/compaction/window-manager.ts +9 -0
  274. package/src/plugins/defaults/empty-response/hooks/post-model-call.ts +18 -24
  275. package/src/plugins/defaults/empty-response/hooks/user-prompt-submit.ts +38 -0
  276. package/src/plugins/defaults/empty-response/refusal-quarantine.ts +99 -0
  277. package/src/plugins/defaults/image-fallback/__tests__/caption-cache-persistence.test.ts +7 -3
  278. package/src/plugins/defaults/index.ts +8 -1
  279. package/src/plugins/defaults/max-tokens-continue/hooks/post-model-call.ts +4 -1
  280. package/src/plugins/defaults/memory/__tests__/activation-session-store.test.ts +48 -6
  281. package/src/plugins/defaults/memory/__tests__/db-memory-attach.test.ts +28 -22
  282. package/src/plugins/defaults/memory/__tests__/memory-log-stores-degraded.test.ts +148 -0
  283. package/src/plugins/defaults/memory/__tests__/memory-retrospective-job.test.ts +80 -8
  284. package/src/plugins/defaults/memory/__tests__/memory-retrospective-prompt.test.ts +202 -0
  285. package/src/plugins/defaults/memory/__tests__/memory-v2-activation-log-store.test.ts +64 -25
  286. package/src/plugins/defaults/memory/__tests__/memory-v2-concept-frequency.test.ts +24 -11
  287. package/src/plugins/defaults/memory/__tests__/prompt-override.test.ts +70 -6
  288. package/src/plugins/defaults/memory/__tests__/table-relocation.test.ts +278 -0
  289. package/src/plugins/defaults/memory/activation-session-store.ts +27 -20
  290. package/src/plugins/defaults/memory/context-search/sources/memory-v2.ts +2 -9
  291. package/src/plugins/defaults/memory/context-search/sources/workspace.ts +1 -8
  292. package/src/plugins/defaults/memory/graph/retriever.test.ts +6 -6
  293. package/src/plugins/defaults/memory/graph/store.ts +114 -0
  294. package/src/plugins/defaults/memory/graph/tool-handlers.ts +36 -1
  295. package/src/plugins/defaults/memory/graph/tools.ts +47 -13
  296. package/src/plugins/defaults/memory/graph-topology/build-memory-graph.ts +16 -35
  297. package/src/plugins/defaults/memory/jobs-worker.ts +10 -1
  298. package/src/plugins/defaults/memory/memory-db.ts +3 -2
  299. package/src/plugins/defaults/memory/memory-recall-log-store.ts +129 -66
  300. package/src/plugins/defaults/memory/memory-retrospective-constants.ts +8 -0
  301. package/src/plugins/defaults/memory/memory-retrospective-job.ts +11 -122
  302. package/src/plugins/defaults/memory/memory-retrospective-prompt.ts +216 -0
  303. package/src/plugins/defaults/memory/memory-v2-activation-log-store.ts +84 -46
  304. package/src/plugins/defaults/memory/memory-v2-concept-frequency.ts +42 -32
  305. package/src/plugins/defaults/memory/path-containment.ts +21 -0
  306. package/src/plugins/defaults/memory/prompt-override.ts +51 -13
  307. package/src/plugins/defaults/memory/tools.test.ts +34 -0
  308. package/src/plugins/defaults/memory/tools.ts +12 -2
  309. package/src/plugins/defaults/memory/v2/__tests__/harness-compare.test.ts +19 -15
  310. package/src/plugins/defaults/memory/v2/__tests__/harness-oracle.test.ts +24 -19
  311. package/src/plugins/defaults/memory/v2/__tests__/harness-replay-input.test.ts +19 -15
  312. package/src/plugins/defaults/memory/v2/__tests__/injection.test.ts +22 -2
  313. package/src/plugins/defaults/memory/v2/__tests__/prompts-consolidation.test.ts +5 -3
  314. package/src/plugins/defaults/memory/v2/harness/oracle.ts +59 -41
  315. package/src/plugins/defaults/memory/v2/harness/replay-input.ts +29 -25
  316. package/src/plugins/defaults/memory/v2/migration.ts +46 -16
  317. package/src/plugins/defaults/memory/v2/prompts/consolidation.ts +4 -0
  318. package/src/plugins/defaults/memory/v3/__tests__/carry-integration.test.ts +17 -15
  319. package/src/plugins/defaults/memory/v3/__tests__/gate.test.ts +24 -22
  320. package/src/plugins/defaults/memory/v3/__tests__/injection.test.ts +9 -5
  321. package/src/plugins/defaults/memory/v3/__tests__/orchestrate.test.ts +277 -10
  322. package/src/plugins/defaults/memory/v3/__tests__/selection-log-store.test.ts +57 -5
  323. package/src/plugins/defaults/memory/v3/__tests__/shadow-integration.test.ts +9 -7
  324. package/src/plugins/defaults/memory/v3/__tests__/shadow-plugin.test.ts +38 -7
  325. package/src/plugins/defaults/memory/v3/hot-set.test.ts +36 -18
  326. package/src/plugins/defaults/memory/v3/hot-set.ts +12 -14
  327. package/src/plugins/defaults/memory/v3/learned-edges.test.ts +47 -27
  328. package/src/plugins/defaults/memory/v3/learned-edges.ts +12 -14
  329. package/src/plugins/defaults/memory/v3/orchestrate.ts +139 -22
  330. package/src/plugins/defaults/memory/v3/prune.test.ts +10 -3
  331. package/src/plugins/defaults/memory/v3/prune.ts +17 -11
  332. package/src/plugins/defaults/memory/v3/selection-log-store.ts +23 -11
  333. package/src/plugins/defaults/memory/v3/shadow-plugin.ts +70 -53
  334. package/src/plugins/defaults/surface-completion-nudge/hooks/post-model-call.ts +9 -6
  335. package/src/plugins/external-plugin-loader.ts +15 -15
  336. package/src/plugins/mtime-cache.ts +47 -0
  337. package/src/plugins/pipeline.ts +9 -1
  338. package/src/plugins/plugin-execution-context.ts +44 -0
  339. package/src/plugins/plugin-tree-walk.ts +31 -24
  340. package/src/plugins/source-fingerprint.ts +3 -4
  341. package/src/prompts/normalize-onboarding.ts +12 -0
  342. package/src/prompts/persona-resolver.ts +8 -0
  343. package/src/prompts/templates/BOOTSTRAP-ACTIVATION-RAIL.md +1 -1
  344. package/src/providers/__tests__/dispatch-connection-routing.test.ts +6 -9
  345. package/src/providers/__tests__/registry-native-web-search.test.ts +4 -10
  346. package/src/providers/__tests__/retry-callsite.test.ts +235 -215
  347. package/src/providers/__tests__/satellite-connection-routing.test.ts +8 -16
  348. package/src/providers/atlascloud/client.ts +10 -49
  349. package/src/providers/baseten/client.ts +43 -0
  350. package/src/providers/call-site-routing.ts +26 -13
  351. package/src/providers/connection-resolution.ts +9 -11
  352. package/src/providers/fetch-provider-catalog.ts +4 -2
  353. package/src/providers/inference/__tests__/adapter-factory-openai-compatible.test.ts +29 -4
  354. package/src/providers/inference/__tests__/connection-availability-keyless.test.ts +78 -0
  355. package/src/providers/inference/adapter-factory.ts +27 -6
  356. package/src/providers/inference/auth.ts +29 -1
  357. package/src/providers/inference/backfill.ts +61 -10
  358. package/src/providers/inference/connection-availability.ts +3 -2
  359. package/src/providers/inference/resolve-auth.ts +9 -1
  360. package/src/providers/model-catalog.ts +37 -0
  361. package/src/providers/openai/__tests__/api-error-normalization.test.ts +24 -2
  362. package/src/providers/openai/api-key-validation.ts +70 -0
  363. package/src/providers/retry.ts +2 -0
  364. package/src/providers/types.ts +11 -4
  365. package/src/providers/vellum-model-routing.test.ts +26 -0
  366. package/src/providers/vellum-model-routing.ts +30 -0
  367. package/src/providers/voice-error-copy.ts +47 -0
  368. package/src/runtime/agent-wake.ts +40 -24
  369. package/src/runtime/for-chat-mint-registry.ts +118 -0
  370. package/src/runtime/reveal-nonce.ts +49 -0
  371. package/src/runtime/reveal-success-registry.ts +306 -0
  372. package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +88 -3
  373. package/src/runtime/routes/__tests__/inference-provider-connection-routes.test.ts +161 -30
  374. package/src/runtime/routes/__tests__/migration-vellum-metadata-reconcile.test.ts +7 -0
  375. package/src/runtime/routes/__tests__/schedule-worker-routes.test.ts +15 -0
  376. package/src/runtime/routes/app-management-routes.ts +152 -45
  377. package/src/runtime/routes/app-routes.ts +16 -77
  378. package/src/runtime/routes/canned-message-complete.ts +16 -16
  379. package/src/runtime/routes/conversation-management-routes.ts +17 -14
  380. package/src/runtime/routes/conversation-query-routes.ts +54 -6
  381. package/src/runtime/routes/conversation-routes.ts +48 -2
  382. package/src/runtime/routes/credential-routes.ts +116 -3
  383. package/src/runtime/routes/email-routes.ts +17 -1
  384. package/src/runtime/routes/inbound-stages/transcribe-audio.test.ts +33 -5
  385. package/src/runtime/routes/inbound-stages/transcribe-audio.ts +6 -5
  386. package/src/runtime/routes/inference-provider-connection-routes.ts +77 -28
  387. package/src/runtime/routes/inference-send-routes.ts +1 -1
  388. package/src/runtime/routes/internal-telemetry-routes.ts +7 -6
  389. package/src/runtime/routes/plugins-routes.ts +37 -6
  390. package/src/runtime/routes/publish-routes.ts +15 -18
  391. package/src/runtime/routes/schedule-worker-routes.ts +9 -0
  392. package/src/runtime/routes/secret-routes.ts +10 -0
  393. package/src/runtime/routes/telemetry-routes.ts +149 -45
  394. package/src/schedule/__tests__/schedule-timezone.test.ts +101 -0
  395. package/src/schedule/__tests__/worker-watchdog.test.ts +209 -0
  396. package/src/schedule/schedule-store.ts +12 -1
  397. package/src/schedule/schedule-timezone.ts +63 -0
  398. package/src/schedule/scheduler.ts +100 -6
  399. package/src/schedule/worker-control.ts +19 -0
  400. package/src/security/auth-fallback-events-store.ts +7 -6
  401. package/src/security/secret-scanner.ts +26 -1
  402. package/src/services/published-app-updater.ts +6 -11
  403. package/src/stt/stt-stream-session.ts +36 -16
  404. package/src/subagent/manager.ts +34 -8
  405. package/src/telemetry/AGENTS.md +30 -1
  406. package/src/telemetry/__tests__/config-setting-snapshot.test.ts +18 -0
  407. package/src/telemetry/__tests__/outbox-test-harness.ts +5 -3
  408. package/src/telemetry/config-setting-snapshot.ts +44 -10
  409. package/src/telemetry/telemetry-event-sources.test.ts +124 -30
  410. package/src/telemetry/telemetry-event-sources.ts +137 -83
  411. package/src/telemetry/telemetry-events-outbox.test.ts +46 -1
  412. package/src/telemetry/telemetry-events-outbox.ts +60 -9
  413. package/src/telemetry/telemetry-wire-source.json +1 -1
  414. package/src/telemetry/telemetry-wire-validation.ts +39 -2
  415. package/src/telemetry/telemetry-wire.generated.ts +8 -0
  416. package/src/telemetry/tool-audit.ts +15 -9
  417. package/src/telemetry/tool-executed-events-store.test.ts +1 -1
  418. package/src/telemetry/turn-events-store.ts +20 -0
  419. package/src/telemetry/types.ts +45 -12
  420. package/src/telemetry/usage-telemetry-reporter.test.ts +294 -25
  421. package/src/telemetry/usage-telemetry-reporter.ts +117 -18
  422. package/src/telemetry/watchdog-direct-emit.test.ts +11 -3
  423. package/src/telemetry/watchdog-direct-emit.ts +11 -6
  424. package/src/tools/apps/executors.ts +11 -36
  425. package/src/tools/executor.ts +14 -1
  426. package/src/tools/host-terminal/host-shell.ts +6 -2
  427. package/src/tools/network/__tests__/web-fetch-firecrawl.test.ts +1 -1
  428. package/src/tools/network/__tests__/web-fetch-metadata.test.ts +25 -0
  429. package/src/tools/network/web-fetch.ts +6 -2
  430. package/src/tools/skills/sandbox-runner.ts +5 -2
  431. package/src/tools/subagent/spawn.ts +7 -11
  432. package/src/tools/terminal/shell.ts +5 -2
  433. package/src/tools/tool-manifest.ts +0 -2
  434. package/src/tools/tool-profiler.ts +37 -6
  435. package/src/tools/ui-surface/channel-variants.ts +101 -0
  436. package/src/tools/ui-surface/definitions.ts +23 -67
  437. package/src/tools/ui-surface/surface-shape-docs.ts +229 -0
  438. package/src/tts/__tests__/provider-adapters.test.ts +18 -0
  439. package/src/tts/provider-catalog.ts +2 -4
  440. package/src/tts/providers/deepgram-provider.ts +10 -1
  441. package/src/types/onboarding-context.ts +8 -0
  442. package/src/usage/attribution.ts +18 -112
  443. package/src/{config/bundled-skills/messaging/tools/gmail-mime-helpers.ts → util/mime-type.ts} +4 -1
  444. package/src/util/provider-error-patterns.ts +7 -1
  445. package/src/util/worker-process.ts +105 -0
  446. package/src/watcher/__tests__/telemetry.test.ts +17 -4
  447. package/src/watcher/telemetry.ts +5 -5
  448. package/src/workspace/migrations/131-drop-web-fetch-mode.ts +61 -0
  449. package/src/workspace/migrations/registry.ts +2 -0
  450. package/src/workspace/provider-commit-message-generator.ts +6 -4
  451. package/src/__tests__/app-open-proxy.test.ts +0 -67
  452. package/src/onboarding/onboarding-research-events-store.test.ts +0 -230
  453. package/src/onboarding/onboarding-research-events-store.ts +0 -104
  454. package/src/runtime/routes/assets/vellum-design-system.css +0 -2236
  455. package/src/tools/apps/definitions.ts +0 -73
  456. package/src/tools/apps/open-proxy.ts +0 -43
@@ -18,9 +18,7 @@ function pendingPayloads(): OnboardingResearchTelemetryEvent[] {
18
18
  );
19
19
  }
20
20
 
21
- const route = ROUTES.find(
22
- (r) => r.operationId === "telemetry_onboarding_research",
23
- );
21
+ const route = ROUTES.find((r) => r.operationId === "telemetry_ingest");
24
22
 
25
23
  function call(body: unknown) {
26
24
  if (!route) {
@@ -29,18 +27,28 @@ function call(body: unknown) {
29
27
  return route.handler({ body } as RouteHandlerArgs);
30
28
  }
31
29
 
32
- const VALID_BODY = {
30
+ /** Well-formed onboarding_research `fields` (base fields stamped by the daemon). */
31
+ const VALID_FIELDS = {
33
32
  conversation_id: "conv-xyz",
34
33
  status: "done",
34
+ self_reported_occupation: "engineer",
35
35
  claims: [{ claim: "Senior engineer", confidence: "confident", sources: [] }],
36
- suggestions: [
37
- { suggestion: "I'll find 3 papers", prompt: "Find me 3 papers" },
38
- ],
36
+ claim_count: 1,
37
+ claims_confident: 1,
38
+ claims_maybe: 0,
39
+ claims_guessing: 0,
40
+ suggestions: [{ suggestion: "I'll find 3 papers", prompt: "Find me 3 papers" }],
41
+ suggestion_count: 1,
39
42
  plugins: ["marketing-expert"],
40
43
  installed_plugins: ["marketing-expert", "web-research"],
41
44
  };
42
45
 
43
- describe("telemetry-routes: onboarding-research", () => {
46
+ const VALID_BODY = {
47
+ type: "onboarding_research",
48
+ fields: VALID_FIELDS,
49
+ };
50
+
51
+ describe("telemetry-routes: ingest", () => {
44
52
  beforeEach(() => {
45
53
  setShareAnalytics(true);
46
54
  setShareDiagnostics(true);
@@ -49,13 +57,13 @@ describe("telemetry-routes: onboarding-research", () => {
49
57
 
50
58
  test("route policy matches the other client-facing telemetry routes", () => {
51
59
  expect(route).toBeDefined();
52
- expect(route?.endpoint).toBe("telemetry/onboarding-research");
60
+ expect(route?.endpoint).toBe("telemetry/ingest");
53
61
  expect(route?.method).toBe("POST");
54
62
  expect(route?.policy?.allowedPrincipalTypes).toEqual(ACTOR_PRINCIPALS);
55
63
  expect(route?.policy?.requiredScopes).toEqual(["settings.write"]);
56
64
  });
57
65
 
58
- test("valid body is persisted to the outbox as a wire onboarding_research event", () => {
66
+ test("valid body is persisted to the outbox as a wire event", () => {
59
67
  const result = call(VALID_BODY);
60
68
  expect(result).toEqual({ id: expect.any(String) });
61
69
 
@@ -71,6 +79,26 @@ describe("telemetry-routes: onboarding-research", () => {
71
79
  plugins: ["marketing-expert"],
72
80
  installed_plugins: ["marketing-expert", "web-research"],
73
81
  });
82
+ // Base fields are stamped by the daemon, not the client.
83
+ expect(payloads[0]?.daemon_event_id).toEqual(expect.any(String));
84
+ expect(payloads[0]?.recorded_at).toEqual(expect.any(Number));
85
+ expect(payloads[0]?.assistant_version).toEqual(expect.any(String));
86
+ });
87
+
88
+ test("stamps a fresh daemon_event_id by default", () => {
89
+ call(VALID_BODY);
90
+ // No override → the collapse key falls back to the row id (a uuid), not the
91
+ // conversation-scoped key.
92
+ expect(pendingPayloads()[0]?.daemon_event_id).not.toBe(
93
+ "onboarding_research:conv-xyz",
94
+ );
95
+ });
96
+
97
+ test("honors a client-supplied daemon_event_id collapse key", () => {
98
+ call({ ...VALID_BODY, daemon_event_id: "onboarding_research:conv-xyz" });
99
+ expect(pendingPayloads()[0]?.daemon_event_id).toBe(
100
+ "onboarding_research:conv-xyz",
101
+ );
74
102
  });
75
103
 
76
104
  test("returns skipped and persists nothing under the analytics opt-out", () => {
@@ -79,19 +107,71 @@ describe("telemetry-routes: onboarding-research", () => {
79
107
  expect(pendingPayloads().length).toBe(0);
80
108
  });
81
109
 
82
- test("returns skipped and persists nothing under the diagnostics opt-out", () => {
83
- setShareDiagnostics(false);
84
- expect(call(VALID_BODY)).toEqual({ skipped: true });
110
+ test("persists regardless of the diagnostics opt-out (rides analytics only)", () => {
111
+ setShareDiagnostics(false, "2000-01-01");
112
+ expect(call(VALID_BODY)).toEqual({ id: expect.any(String) });
113
+ expect(pendingPayloads().length).toBe(1);
114
+ });
115
+
116
+ test("rejects a non-outbox (watermark) or unknown type", () => {
117
+ // `turn` is a real wire type but watermark-flushed, not outbox-backed, so it
118
+ // has no ingest variant and a client can't inject it.
119
+ expect(() => call({ type: "turn", fields: {} })).toThrow(RouteError);
120
+ // An unknown type is rejected the same way.
121
+ expect(() => call({ type: "not_a_type", fields: {} })).toThrow(RouteError);
85
122
  expect(pendingPayloads().length).toBe(0);
86
123
  });
87
124
 
88
- test("rejects a malformed body without persisting", () => {
89
- expect(() => call({ ...VALID_BODY, status: "not-a-status" })).toThrow(
90
- RouteError,
91
- );
92
- expect(() => call({ ...VALID_BODY, claims: [{ claim: "x" }] })).toThrow(
93
- RouteError,
125
+ test("accepts any outbox-backed type, e.g. config_setting", () => {
126
+ const result = call({
127
+ type: "config_setting",
128
+ fields: { config_key: "voice.provider", config_value: "elevenlabs" },
129
+ });
130
+ expect(result).toEqual({ id: expect.any(String) });
131
+
132
+ const payloads = pendingOutboxPayloads<{ config_key: string }>(
133
+ "config_setting",
94
134
  );
135
+ expect(payloads.length).toBe(1);
136
+ expect(payloads[0]).toMatchObject({
137
+ type: "config_setting",
138
+ config_key: "voice.provider",
139
+ config_value: "elevenlabs",
140
+ });
141
+ });
142
+
143
+ test("rejects malformed fields (missing or mistyped) without persisting", () => {
144
+ // Missing the required derived counts.
145
+ const { claim_count: _c, suggestion_count: _s, ...missingCounts } =
146
+ VALID_FIELDS;
147
+ expect(() =>
148
+ call({ type: "onboarding_research", fields: missingCounts }),
149
+ ).toThrow(RouteError);
150
+
151
+ // Wrong type for a numeric field.
152
+ expect(() =>
153
+ call({
154
+ type: "onboarding_research",
155
+ fields: { ...VALID_FIELDS, claim_count: "one" },
156
+ }),
157
+ ).toThrow(RouteError);
158
+
159
+ expect(pendingPayloads().length).toBe(0);
160
+ });
161
+
162
+ test("rejects a structurally invalid request body", () => {
163
+ expect(() => call({ type: "onboarding_research" })).toThrow(RouteError);
164
+ expect(() => call({ fields: VALID_FIELDS })).toThrow(RouteError);
95
165
  expect(pendingPayloads().length).toBe(0);
96
166
  });
167
+
168
+ test("strips unknown fields keys (typed schema, not a passthrough record)", () => {
169
+ call({
170
+ type: "onboarding_research",
171
+ fields: { ...VALID_FIELDS, bogus_extra: "nope" },
172
+ });
173
+ const payloads = pendingPayloads();
174
+ expect(payloads.length).toBe(1);
175
+ expect(payloads[0]).not.toHaveProperty("bogus_extra");
176
+ });
97
177
  });
@@ -1,11 +1,11 @@
1
1
  import { beforeEach, describe, expect, mock, test } from "bun:test";
2
2
 
3
- // Toggle for the share_analytics opt-out gate consulted when populating the
4
- // telemetry columns.
5
- let shareAnalytics = true;
3
+ // Tri-state share_analytics consent consulted when populating the telemetry
4
+ // columns: only a confirmed `false` NULLs them.
5
+ let shareAnalytics: boolean | "unknown" = true;
6
6
 
7
7
  mock.module("../platform/consent-cache.js", () => ({
8
- getCachedShareAnalytics: () => shareAnalytics,
8
+ getRawShareAnalytics: () => shareAnalytics,
9
9
  }));
10
10
 
11
11
  // Capture the audit rows the terminals would insert, and the lifecycle-event
@@ -379,6 +379,36 @@ describe("tool audit terminals", () => {
379
379
  });
380
380
  });
381
381
 
382
+ test("populates telemetry columns under unknown consent (cold cache)", () => {
383
+ // NULL columns are permanently excluded by the projection's
384
+ // arg_bytes IS NOT NULL filter, so only a CONFIRMED opt-out may NULL
385
+ // them — unknown records, and flush-time consent gating decides later.
386
+ shareAnalytics = "unknown";
387
+
388
+ recordToolExecuted({
389
+ conversationId: "conv-unknown",
390
+ toolName: "file_read",
391
+ input: { path: "/tmp/a" },
392
+ resultContent: "ok",
393
+ resultBytes: 99,
394
+ decision: "allow",
395
+ riskLevel: "low",
396
+ durationMs: 12,
397
+ attribution: ATTRIBUTION,
398
+ wasPrompted: false,
399
+ });
400
+
401
+ expect(records).toHaveLength(1);
402
+ expect(records[0]).toMatchObject({
403
+ argBytes: Buffer.byteLength(JSON.stringify({ path: "/tmp/a" }), "utf8"),
404
+ resultBytes: 99,
405
+ provider: "anthropic",
406
+ model: "model-a",
407
+ inferenceProfile: "balanced",
408
+ inferenceProfileSource: "active",
409
+ });
410
+ });
411
+
382
412
  test("maps attribution onto executed rows and leaves denied rows null", () => {
383
413
  recordToolExecuted({
384
414
  conversationId: "conv-attr",
@@ -81,7 +81,7 @@ mock.module("../telemetry/tool-audit.js", () => ({
81
81
  // Analytics consent is granted so any consent-gated telemetry path the audit
82
82
  // terminals consult sees the opted-in state.
83
83
  mock.module("../platform/consent-cache.js", () => ({
84
- getCachedShareAnalytics: () => true,
84
+ getRawShareAnalytics: () => true,
85
85
  }));
86
86
 
87
87
  mock.module("../permissions/checker.js", () => ({
@@ -1,4 +1,4 @@
1
- import { beforeEach, describe, expect, test } from "bun:test";
1
+ import { afterEach, beforeEach, describe, expect, spyOn, test } from "bun:test";
2
2
 
3
3
  import {
4
4
  disposeToolProfiler,
@@ -99,6 +99,55 @@ describe("ToolProfiler", () => {
99
99
  });
100
100
  });
101
101
 
102
+ /**
103
+ * A `process.memoryUsage` replacement that reproduces the Bun 1.3.11
104
+ * `SystemError: Failed to get memory usage, errno: 2` syscall failure. Cast to
105
+ * the real signature because an always-throwing arrow infers a `never` return.
106
+ */
107
+ const throwingMemoryUsage = (() => {
108
+ throw new Error("Failed to get memory usage, errno: 2");
109
+ }) as unknown as typeof process.memoryUsage;
110
+
111
+ describe("ToolProfiler RSS sampling failures", () => {
112
+ let profiler: ToolProfiler;
113
+
114
+ beforeEach(() => {
115
+ profiler = new ToolProfiler();
116
+ });
117
+
118
+ afterEach(() => {
119
+ // Restore the real implementation between cases.
120
+ spyOn(process, "memoryUsage").mockRestore();
121
+ });
122
+
123
+ test("startRequest tolerates a throwing memoryUsage syscall", () => {
124
+ // Simulate the Bun 1.3.11 `SystemError: Failed to get memory usage` bug.
125
+ spyOn(process, "memoryUsage").mockImplementation(throwingMemoryUsage);
126
+
127
+ expect(() => profiler.startRequest()).not.toThrow();
128
+ });
129
+
130
+ test("recordToolCompletion still records stats when memoryUsage throws", () => {
131
+ profiler.startRequest();
132
+ spyOn(process, "memoryUsage").mockImplementation(throwingMemoryUsage);
133
+
134
+ // The RSS sample fails, but the tool's timing stats are still recorded and
135
+ // no exception escapes to the caller (the agent loop).
136
+ expect(() =>
137
+ profiler.recordToolCompletion("bash", 123, false),
138
+ ).not.toThrow();
139
+
140
+ const summary = profiler.getSummary();
141
+ expect(summary.toolCount).toBe(1);
142
+ expect(summary.tools["bash"]).toEqual({
143
+ count: 1,
144
+ totalMs: 123,
145
+ maxMs: 123,
146
+ errors: 0,
147
+ });
148
+ });
149
+ });
150
+
102
151
  describe("conversation-keyed profiler registry", () => {
103
152
  test("recording before a request window is a silent no-op", () => {
104
153
  expect(() =>
@@ -125,6 +174,28 @@ describe("conversation-keyed profiler registry", () => {
125
174
  ).not.toThrow();
126
175
  });
127
176
 
177
+ test("recordToolCompletion swallows profiler failures instead of throwing", () => {
178
+ startToolProfilingRequest("conv-throwing");
179
+ const spy = spyOn(process, "memoryUsage").mockImplementation(
180
+ throwingMemoryUsage,
181
+ );
182
+
183
+ try {
184
+ // A throwing RSS syscall must never propagate to the executor/agent loop.
185
+ expect(() =>
186
+ recordToolCompletion("conv-throwing", "bash", 42, false),
187
+ ).not.toThrow();
188
+ } finally {
189
+ spy.mockRestore();
190
+ }
191
+
192
+ // The turn's timing stats survived even though the RSS sample failed.
193
+ expect(() =>
194
+ emitToolProfilingSummary("conv-throwing", "req-throwing"),
195
+ ).not.toThrow();
196
+ disposeToolProfiler("conv-throwing");
197
+ });
198
+
128
199
  test("startToolProfilingRequest resets a conversation's prior window", () => {
129
200
  startToolProfilingRequest("conv-reset");
130
201
  recordToolCompletion("conv-reset", "bash", 100, false);
@@ -110,13 +110,14 @@ describe("spoolAndStubOversizedToolResults", () => {
110
110
  expect(messages).toBe(history);
111
111
  });
112
112
 
113
- test("skips below-threshold, error, exempt, file_read, host_file_read, marked, and ax-tree results", () => {
113
+ test("skips below-threshold, error, exempt, file_read, host_file_read, web_fetch, marked, and ax-tree results", () => {
114
114
  const blocks = [
115
115
  makeToolResult(SHORT, "tu_short"),
116
116
  makeToolResult(LONG, "tu_err", true),
117
117
  makeToolResult(LONG, "tu_skill"),
118
118
  makeToolResult(LONG, "tu_read"),
119
119
  makeToolResult(LONG, "tu_host_read"),
120
+ makeToolResult(LONG, "tu_web_fetch"),
120
121
  makeToolResult(`${LONG}${TRUNCATION_MARKER}`, "tu_marked"),
121
122
  makeToolResult(`<ax-tree>${LONG}</ax-tree>`, "tu_ax"),
122
123
  { type: "text" as const, text: LONG },
@@ -126,9 +127,18 @@ describe("spoolAndStubOversizedToolResults", () => {
126
127
  const count = spoolAndStubOversizedToolResults(blocks, {
127
128
  conversationDir: convDir,
128
129
  toolNameById: (id) => {
129
- if (id === "tu_skill") return "skill_load";
130
- if (id === "tu_read") return "file_read";
131
- if (id === "tu_host_read") return "host_file_read";
130
+ if (id === "tu_skill") {
131
+ return "skill_load";
132
+ }
133
+ if (id === "tu_read") {
134
+ return "file_read";
135
+ }
136
+ if (id === "tu_host_read") {
137
+ return "host_file_read";
138
+ }
139
+ if (id === "tu_web_fetch") {
140
+ return "web_fetch";
141
+ }
132
142
  return undefined;
133
143
  },
134
144
  });
@@ -312,6 +322,41 @@ describe("AgentLoop result-time spooling", () => {
312
322
  expect(readFileSync(filePath, "utf-8")).toBe(HUGE);
313
323
  });
314
324
 
325
+ test("web_fetch result is sent to the provider in full mid-turn (explicit self-sized read)", async () => {
326
+ const webFetchTool: ToolDefinition[] = [
327
+ {
328
+ name: "web_fetch",
329
+ description: "Fetch a web page",
330
+ input_schema: { type: "object", properties: {} },
331
+ },
332
+ ];
333
+ const { provider, calls } = createMockProvider([
334
+ toolUseResponse("tu_wf", "web_fetch", {}),
335
+ textResponse("Done."),
336
+ ]);
337
+ const loop = new AgentLoop({
338
+ provider,
339
+ systemPrompt: "system",
340
+ conversationId: "test-conversation",
341
+ tools: webFetchTool,
342
+ toolExecutor: async () => ({ content: LONG, isError: false }),
343
+ resolveConversationDir: () => convDir,
344
+ });
345
+
346
+ await loop.run({
347
+ requestId: "test-request",
348
+ messages: [userMessage],
349
+ onEvent: () => {},
350
+ trust: { sourceChannel: "vellum", trustClass: "unknown" },
351
+ });
352
+
353
+ // The second provider call carries the full requested window, not a stub;
354
+ // the post-turn pass (conversation-turn-finalize) owns truncating it.
355
+ const sentResult = calls[1].messages[2].content[0] as { content: string };
356
+ expect(sentResult.content).toBe(LONG);
357
+ expect(existsSync(join(convDir, TOOL_RESULT_DIR))).toBe(false);
358
+ });
359
+
315
360
  test("without resolveConversationDir the full content is sent and post-turn handles it", async () => {
316
361
  const { provider, calls } = createMockProvider([
317
362
  toolUseResponse("tu_1", "fetch_transcript", {}),
@@ -31,7 +31,6 @@ mock.module("../apps/app-store.js", () => ({
31
31
  getApp: mock(() => null),
32
32
  getAppDirPath: mock(() => ""),
33
33
  getAppsDir: mock(() => ""),
34
- isMultifileApp: mock(() => false),
35
34
  resolveAppIdFromPath: mock(() => null),
36
35
  resolveAppIdByDirName: mock(() => null),
37
36
  resolveAppDir: mock(() => ({ dirName: "", dirPath: "" })),
@@ -0,0 +1,108 @@
1
+ import { describe, expect, test } from "bun:test";
2
+
3
+ import type { ToolDefinition } from "../providers/types.js";
4
+ import { ACTIVATION_MOMENT_PARAMS } from "../telemetry/activation-funnel.js";
5
+ import {
6
+ injectActivationMomentParam,
7
+ projectUiToolsForChannel,
8
+ } from "../tools/ui-surface/channel-variants.js";
9
+ import {
10
+ uiDismissTool,
11
+ uiShowTool,
12
+ uiUpdateTool,
13
+ } from "../tools/ui-surface/definitions.js";
14
+
15
+ const defs: ToolDefinition[] = [
16
+ uiShowTool,
17
+ uiUpdateTool,
18
+ uiDismissTool,
19
+ {
20
+ name: "bash",
21
+ description: "run a command",
22
+ input_schema: { type: "object" },
23
+ },
24
+ ];
25
+
26
+ function surfaceTypeEnum(def: ToolDefinition): string[] {
27
+ return (
28
+ def.input_schema as { properties: { surface_type: { enum: string[] } } }
29
+ ).properties.surface_type.enum;
30
+ }
31
+
32
+ describe("projectUiToolsForChannel", () => {
33
+ test("returns the same array for channels without a variant", () => {
34
+ for (const channel of [undefined, "macos", "telegram", "phone"]) {
35
+ expect(projectUiToolsForChannel(defs, channel)).toBe(defs);
36
+ }
37
+ });
38
+
39
+ test("slack gets a task_progress-only ui_show", () => {
40
+ const projected = projectUiToolsForChannel(defs, "slack");
41
+ const uiShow = projected.find((d) => d.name === "ui_show")!;
42
+
43
+ expect(surfaceTypeEnum(uiShow)).toEqual(["card"]);
44
+ expect(uiShow.description).toContain("task_progress");
45
+ expect(uiShow.description).toContain(
46
+ "the only surface this channel renders",
47
+ );
48
+ expect(uiShow.description).not.toContain("oauth_connect");
49
+ expect(uiShow.description).not.toContain("dynamic_page");
50
+ expect(uiShow.description!.length).toBeLessThan(
51
+ uiShowTool.description.length / 2,
52
+ );
53
+ });
54
+
55
+ test("slack projection keeps execute and leaves other tools untouched", () => {
56
+ const projected = projectUiToolsForChannel(defs, "slack");
57
+ const uiShow = projected.find((d) => d.name === "ui_show") as {
58
+ execute?: unknown;
59
+ };
60
+
61
+ expect(uiShow.execute).toBe(uiShowTool.execute);
62
+ expect(projected.find((d) => d.name === "ui_update")).toBe(uiUpdateTool);
63
+ expect(projected.find((d) => d.name === "ui_dismiss")).toBe(uiDismissTool);
64
+ expect(projected.find((d) => d.name === "bash")).toBe(defs[3]);
65
+ });
66
+
67
+ test("projection never mutates the shared definitions", () => {
68
+ const descriptionBefore = uiShowTool.description;
69
+ const schemaBefore = JSON.stringify(uiShowTool.input_schema);
70
+
71
+ projectUiToolsForChannel(defs, "slack");
72
+
73
+ expect(uiShowTool.description).toBe(descriptionBefore);
74
+ expect(JSON.stringify(uiShowTool.input_schema)).toBe(schemaBefore);
75
+ });
76
+ });
77
+
78
+ describe("injectActivationMomentParam", () => {
79
+ function properties(def: ToolDefinition): Record<string, unknown> {
80
+ return (def.input_schema as { properties: Record<string, unknown> })
81
+ .properties;
82
+ }
83
+
84
+ test("the standard ui_show schema does not carry activation_moment", () => {
85
+ expect(properties(uiShowTool).activation_moment).toBeUndefined();
86
+ });
87
+
88
+ test("adds the optional param to ui_show only, leaving required unchanged", () => {
89
+ const projected = injectActivationMomentParam(defs);
90
+ const uiShow = projected.find((d) => d.name === "ui_show")!;
91
+
92
+ const param = properties(uiShow).activation_moment as { enum: string[] };
93
+ expect(param).toBeDefined();
94
+ expect(param.enum).toEqual([...ACTIVATION_MOMENT_PARAMS]);
95
+ expect((uiShow.input_schema as { required: string[] }).required).toEqual([
96
+ "surface_type",
97
+ "data",
98
+ ]);
99
+ expect(projected.find((d) => d.name === "ui_update")).toBe(uiUpdateTool);
100
+ expect(projected.find((d) => d.name === "bash")).toBe(defs[3]);
101
+ });
102
+
103
+ test("injection never mutates the shared definition", () => {
104
+ const before = JSON.stringify(uiShowTool.input_schema);
105
+ injectActivationMomentParam(defs);
106
+ expect(JSON.stringify(uiShowTool.input_schema)).toBe(before);
107
+ });
108
+ });