@vellumai/assistant 0.8.1 → 0.8.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (506) hide show
  1. package/ARCHITECTURE.md +2 -7
  2. package/Dockerfile +75 -1
  3. package/bun.lock +11 -1
  4. package/docker-entrypoint.sh +5 -0
  5. package/docker-init-apt-root.sh +94 -0
  6. package/docker-kata-apt-env.sh +39 -0
  7. package/docs/plugins.md +88 -47
  8. package/docs/skills.md +9 -7
  9. package/examples/plugins/echo/README.md +27 -27
  10. package/examples/plugins/echo/package.json +3 -0
  11. package/examples/plugins/echo/register.ts +31 -31
  12. package/node_modules/@vellumai/slack-text/src/index.test.ts +114 -14
  13. package/node_modules/@vellumai/slack-text/src/index.ts +82 -18
  14. package/openapi.yaml +325 -3
  15. package/package.json +3 -1
  16. package/scripts/generate-openapi.ts +83 -10
  17. package/scripts/sync-llm-catalog.ts +2 -2
  18. package/scripts/sync-web-search-catalog.ts +47 -25
  19. package/src/__tests__/agent-image-optimize.test.ts +11 -3
  20. package/src/__tests__/agent-wake-disk-pressure-callsite.test.ts +131 -0
  21. package/src/__tests__/anthropic-provider.test.ts +45 -0
  22. package/src/__tests__/app-builder-tool-scripts.test.ts +9 -3
  23. package/src/__tests__/app-executors.test.ts +220 -4
  24. package/src/__tests__/auto-analysis-end-to-end.test.ts +35 -0
  25. package/src/__tests__/bundled-asset.test.ts +6 -6
  26. package/src/__tests__/channel-availability-routes.test.ts +206 -0
  27. package/src/__tests__/channel-delivery-store.test.ts +289 -1
  28. package/src/__tests__/circuit-breaker-pipeline.test.ts +0 -1
  29. package/src/__tests__/clawhub.test.ts +75 -16
  30. package/src/__tests__/compactor-tail-resolution.test.ts +41 -0
  31. package/src/__tests__/config-schema.test.ts +21 -0
  32. package/src/__tests__/config-set-route.test.ts +80 -0
  33. package/src/__tests__/config-sounds-sync.test.ts +97 -0
  34. package/src/__tests__/config-watcher-skill-reseed.test.ts +453 -0
  35. package/src/__tests__/context-search-conversations-source.test.ts +117 -2
  36. package/src/__tests__/context-search-memory-v2-source.test.ts +0 -1
  37. package/src/__tests__/context-search-workspace-source.test.ts +7 -0
  38. package/src/__tests__/context-token-estimator.test.ts +1 -0
  39. package/src/__tests__/conversation-abort-tool-results.test.ts +4 -1
  40. package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +1 -0
  41. package/src/__tests__/conversation-agent-loop-overflow.test.ts +92 -92
  42. package/src/__tests__/conversation-agent-loop.test.ts +2 -0
  43. package/src/__tests__/conversation-error.test.ts +42 -3
  44. package/src/__tests__/conversation-fork-crud.test.ts +82 -0
  45. package/src/__tests__/conversation-inference-profile-route.test.ts +40 -4
  46. package/src/__tests__/conversation-lifecycle.test.ts +173 -0
  47. package/src/__tests__/conversation-message-sync-tags.test.ts +97 -0
  48. package/src/__tests__/conversation-pairing.test.ts +54 -0
  49. package/src/__tests__/conversation-process-callsite.test.ts +4 -1
  50. package/src/__tests__/conversation-provider-retry-repair.test.ts +5 -1
  51. package/src/__tests__/conversation-queue.test.ts +4 -1
  52. package/src/__tests__/conversation-runtime-assembly.test.ts +76 -9
  53. package/src/__tests__/conversation-slash-queue.test.ts +59 -1
  54. package/src/__tests__/conversation-slash-unknown.test.ts +4 -1
  55. package/src/__tests__/conversation-surfaces-table-action.test.ts +360 -0
  56. package/src/__tests__/conversation-sync-tags.test.ts +235 -0
  57. package/src/__tests__/conversation-workspace-injection.test.ts +5 -1
  58. package/src/__tests__/conversation-workspace-tool-tracking.test.ts +5 -1
  59. package/src/__tests__/credential-security-invariants.test.ts +3 -2
  60. package/src/__tests__/db-slack-external-content-normalization.test.ts +301 -0
  61. package/src/__tests__/delete-managed-skill-tool.test.ts +55 -13
  62. package/src/__tests__/disk-pressure-tools.test.ts +1 -0
  63. package/src/__tests__/dm-backfill.test.ts +121 -10
  64. package/src/__tests__/document-tool-security.test.ts +258 -0
  65. package/src/__tests__/dynamic-skill-workflow-prompt.test.ts +0 -1
  66. package/src/__tests__/edit-propagation.test.ts +33 -0
  67. package/src/__tests__/empty-response-pipeline.test.ts +0 -4
  68. package/src/__tests__/external-plugin-loader.test.ts +60 -36
  69. package/src/__tests__/filing-service.test.ts +140 -0
  70. package/src/__tests__/get-skill-detail-audit.test.ts +0 -4
  71. package/src/__tests__/handlers-skills-memory-v2-reseed.test.ts +43 -62
  72. package/src/__tests__/helpers/tar-fixtures.ts +39 -0
  73. package/src/__tests__/helpers/wait-for.ts +21 -0
  74. package/src/__tests__/history-repair-pipeline.test.ts +0 -3
  75. package/src/__tests__/history-repair.test.ts +73 -0
  76. package/src/__tests__/host-app-control-proxy.test.ts +266 -10
  77. package/src/__tests__/image-credentials.test.ts +1 -1
  78. package/src/__tests__/inbound-slack-persistence.test.ts +2 -0
  79. package/src/__tests__/inference-no-mode-boot-e2e.test.ts +1 -1
  80. package/src/__tests__/inference-profile-reaper.test.ts +4 -2
  81. package/src/__tests__/inference-profile-session-handler.test.ts +18 -6
  82. package/src/__tests__/inference-profile-session-ipc.test.ts +17 -5
  83. package/src/__tests__/injector-chain.test.ts +10 -8
  84. package/src/__tests__/install-skill-routing.test.ts +155 -37
  85. package/src/__tests__/lifecycle-memory-v2-seed.test.ts +92 -3
  86. package/src/__tests__/list-messages-page-latest.test.ts +55 -0
  87. package/src/__tests__/llm-call-pipeline.test.ts +0 -3
  88. package/src/__tests__/llm-catalog-parity.test.ts +55 -13
  89. package/src/__tests__/llm-request-log-source-clickhouse.test.ts +34 -0
  90. package/src/__tests__/llm-request-log-source-factory.test.ts +29 -53
  91. package/src/__tests__/llm-usage-store.test.ts +114 -0
  92. package/src/__tests__/managed-profile-guard.test.ts +31 -29
  93. package/src/__tests__/managed-skill-lifecycle.test.ts +109 -18
  94. package/src/__tests__/managed-store.test.ts +84 -192
  95. package/src/__tests__/media-generate-image.test.ts +1 -1
  96. package/src/__tests__/memory-retrieval-pipeline.test.ts +0 -2
  97. package/src/__tests__/messages-after-tiebreaker.test.ts +122 -0
  98. package/src/__tests__/oauth-commands-routes.test.ts +168 -16
  99. package/src/__tests__/oauth-provider-profiles.test.ts +9 -0
  100. package/src/__tests__/openai-provider.test.ts +24 -0
  101. package/src/__tests__/openai-responses-cutover-guard.test.ts +17 -9
  102. package/src/__tests__/overflow-reduce-pipeline.test.ts +0 -2
  103. package/src/__tests__/persistence-pipeline.test.ts +0 -2
  104. package/src/__tests__/{managed-proxy-context.test.ts → platform-proxy-context.test.ts} +1 -1
  105. package/src/__tests__/platform.test.ts +2 -0
  106. package/src/__tests__/plugin-api-shim.test.ts +125 -0
  107. package/src/__tests__/plugin-bootstrap.test.ts +10 -36
  108. package/src/__tests__/plugin-external-api.test.ts +68 -0
  109. package/src/__tests__/plugin-registry.test.ts +0 -77
  110. package/src/__tests__/plugin-route-contribution.test.ts +0 -1
  111. package/src/__tests__/plugin-skill-contribution.test.ts +0 -2
  112. package/src/__tests__/plugin-tool-contribution.test.ts +16 -15
  113. package/src/__tests__/plugin-types.test.ts +3 -13
  114. package/src/__tests__/process-message-background-slack.test.ts +8 -1
  115. package/src/__tests__/process-message-display-content.test.ts +421 -0
  116. package/src/__tests__/provider-catalog-visibility.test.ts +142 -0
  117. package/src/__tests__/provider-error-scenarios.test.ts +111 -0
  118. package/src/__tests__/{provider-managed-proxy-integration.test.ts → provider-platform-proxy-integration.test.ts} +8 -8
  119. package/src/__tests__/scaffold-managed-skill-tool.test.ts +65 -13
  120. package/src/__tests__/schedule-routes.test.ts +50 -3
  121. package/src/__tests__/schedule-store.test.ts +94 -0
  122. package/src/__tests__/scheduler-reuse-conversation.test.ts +54 -7
  123. package/src/__tests__/schema-transforms.test.ts +20 -0
  124. package/src/__tests__/search-skills-unified.test.ts +0 -5
  125. package/src/__tests__/server-history-render.test.ts +43 -0
  126. package/src/__tests__/skill-load-feature-flag.test.ts +0 -12
  127. package/src/__tests__/skill-load-tool.test.ts +27 -89
  128. package/src/__tests__/skill-memory.test.ts +23 -3
  129. package/src/__tests__/skills-file-content-endpoint.test.ts +9 -38
  130. package/src/__tests__/skills-files-catalog-fallback.test.ts +0 -3
  131. package/src/__tests__/skills-install-extract.test.ts +49 -38
  132. package/src/__tests__/skills-install-staging.test.ts +159 -0
  133. package/src/__tests__/skills-uninstall.test.ts +9 -41
  134. package/src/__tests__/skills.test.ts +51 -58
  135. package/src/__tests__/slack-channel-config.test.ts +9 -0
  136. package/src/__tests__/subagent-tool-filtering.test.ts +50 -0
  137. package/src/__tests__/system-prompt.test.ts +737 -63
  138. package/src/__tests__/terminal-tools.test.ts +28 -1
  139. package/src/__tests__/thread-backfill.test.ts +557 -27
  140. package/src/__tests__/title-generate-pipeline.test.ts +0 -13
  141. package/src/__tests__/token-estimate-pipeline.test.ts +0 -3
  142. package/src/__tests__/tool-error-pipeline.test.ts +0 -3
  143. package/src/__tests__/tool-execute-pipeline.test.ts +0 -5
  144. package/src/__tests__/tool-executor-lifecycle-events.test.ts +1 -1
  145. package/src/__tests__/tool-executor.test.ts +16 -4
  146. package/src/__tests__/tool-result-truncate-pipeline.test.ts +0 -12
  147. package/src/__tests__/turn-events-store.test.ts +256 -0
  148. package/src/__tests__/twilio-routes.test.ts +4 -0
  149. package/src/__tests__/user-plugin-loader.test.ts +0 -7
  150. package/src/__tests__/voice-session-bridge.test.ts +198 -0
  151. package/src/__tests__/web-search-catalog-parity.test.ts +32 -10
  152. package/src/__tests__/workspace-migration-057-repair-stale-gemini-model-ids.test.ts +115 -3
  153. package/src/__tests__/workspace-migration-072-seed-reply-suggestion-callsite.test.ts +50 -0
  154. package/src/__tests__/workspace-migration-073-repair-recall-callsite-empty-profile.test.ts +153 -0
  155. package/src/__tests__/workspace-migration-085-memory-v2-bm25-b-reembed-disabled-v2-pages.test.ts +220 -0
  156. package/src/__tests__/workspace-migration-086-revert-stale-gemini-mis-rewrites.test.ts +269 -0
  157. package/src/__tests__/workspace-migration-remove-legacy-skills-index.test.ts +309 -0
  158. package/src/__tests__/workspace-migrations-runner.test.ts +111 -3
  159. package/src/acp/resolve-agent.ts +1 -1
  160. package/src/agent/image-optimize.ts +13 -5
  161. package/src/calls/voice-session-bridge.ts +61 -42
  162. package/src/channels/types.ts +108 -0
  163. package/src/cli/__tests__/unknown-command.test.ts +24 -0
  164. package/src/cli/commands/__tests__/changelog.test.ts +304 -319
  165. package/src/cli/commands/__tests__/schedules.test.ts +491 -0
  166. package/src/cli/commands/changelog.ts +106 -42
  167. package/src/cli/commands/conversations.ts +102 -17
  168. package/src/cli/commands/default-action.ts +10 -53
  169. package/src/cli/commands/notifications.ts +329 -317
  170. package/src/cli/commands/plugins.ts +185 -0
  171. package/src/cli/commands/schedules.ts +391 -0
  172. package/src/cli/commands/telemetry.ts +40 -0
  173. package/src/cli/lib/__tests__/cli-colors.test.ts +48 -0
  174. package/src/cli/lib/__tests__/confirm-prompt.test.ts +159 -0
  175. package/src/cli/lib/__tests__/install-from-github.test.ts +355 -0
  176. package/src/cli/lib/__tests__/list-installed-plugins.test.ts +154 -0
  177. package/src/cli/lib/__tests__/uninstall-plugin.test.ts +124 -0
  178. package/src/cli/lib/__tests__/unknown-command.test.ts +106 -0
  179. package/src/cli/lib/cli-colors.ts +12 -0
  180. package/src/cli/lib/confirm-prompt.ts +79 -0
  181. package/src/cli/lib/install-from-github.ts +304 -0
  182. package/src/cli/lib/list-installed-plugins.ts +137 -0
  183. package/src/cli/lib/uninstall-plugin.ts +82 -0
  184. package/src/cli/lib/unknown-command.ts +111 -0
  185. package/src/cli/program.ts +38 -2
  186. package/src/config/bundled-skills/app-builder/SKILL.md +23 -21
  187. package/src/config/bundled-skills/app-builder/TOOLS.json +7 -0
  188. package/src/config/bundled-skills/computer-use/TOOLS.json +15 -52
  189. package/src/config/bundled-skills/document/SKILL.md +23 -3
  190. package/src/config/bundled-skills/document/TOOLS.json +53 -0
  191. package/src/config/bundled-skills/document/tools/document-delete.ts +12 -0
  192. package/src/config/bundled-skills/document/tools/document-list.ts +12 -0
  193. package/src/config/bundled-skills/document/tools/document-read.ts +12 -0
  194. package/src/config/bundled-skills/skill-management/SKILL.md +2 -2
  195. package/src/config/bundled-skills/skill-management/TOOLS.json +7 -7
  196. package/src/config/bundled-tool-registry.ts +6 -0
  197. package/src/config/feature-flag-registry.json +41 -1
  198. package/src/config/loader.ts +64 -38
  199. package/src/config/schema.ts +7 -10
  200. package/src/config/schemas/__tests__/llm-request-logs.test.ts +36 -0
  201. package/src/config/schemas/channels.ts +8 -0
  202. package/src/config/schemas/compaction.ts +28 -0
  203. package/src/config/schemas/heartbeat.ts +9 -0
  204. package/src/config/schemas/llm-request-logs.ts +31 -7
  205. package/src/config/schemas/llm.ts +3 -0
  206. package/src/config/schemas/memory-retrieval.ts +18 -0
  207. package/src/config/schemas/tools.ts +14 -0
  208. package/src/config/skills.ts +3 -96
  209. package/src/context/compactor.ts +1047 -0
  210. package/src/context/token-estimator.ts +2 -2
  211. package/src/context/window-manager.ts +197 -1520
  212. package/src/credential-execution/managed-catalog.ts +37 -0
  213. package/src/credential-health/credential-health-service.ts +280 -19
  214. package/src/daemon/__tests__/conversation-lifecycle-auto-analyze.test.ts +34 -0
  215. package/src/daemon/__tests__/conversation-tool-setup-exclude.test.ts +138 -0
  216. package/src/daemon/__tests__/conversation-tool-setup.test.ts +74 -0
  217. package/src/daemon/approval-generators.ts +8 -6
  218. package/src/daemon/config-watcher.ts +94 -31
  219. package/src/daemon/conversation-agent-loop.ts +169 -9
  220. package/src/daemon/conversation-error.ts +171 -37
  221. package/src/daemon/conversation-lifecycle.ts +53 -40
  222. package/src/daemon/conversation-messaging.ts +25 -6
  223. package/src/daemon/conversation-process.ts +49 -12
  224. package/src/daemon/conversation-runtime-assembly.ts +16 -1
  225. package/src/daemon/conversation-slash.ts +12 -5
  226. package/src/daemon/conversation-store.ts +11 -4
  227. package/src/daemon/conversation-tool-setup.ts +39 -7
  228. package/src/daemon/conversation.ts +33 -1
  229. package/src/daemon/external-plugins-bootstrap.ts +217 -181
  230. package/src/daemon/first-greeting.ts +22 -2
  231. package/src/daemon/handlers/config-model.ts +6 -5
  232. package/src/daemon/handlers/config-slack-channel.ts +15 -3
  233. package/src/daemon/handlers/shared.ts +14 -5
  234. package/src/daemon/handlers/skills.ts +111 -108
  235. package/src/daemon/history-repair.ts +28 -1
  236. package/src/daemon/host-app-control-proxy.ts +98 -23
  237. package/src/daemon/lifecycle.ts +45 -35
  238. package/src/daemon/meet-host-supervisor.ts +5 -4
  239. package/src/daemon/memory-v2-startup.ts +49 -0
  240. package/src/daemon/message-protocol.ts +1 -0
  241. package/src/daemon/message-types/conversations.ts +25 -0
  242. package/src/daemon/message-types/messages.ts +61 -0
  243. package/src/daemon/message-types/subagents.ts +1 -0
  244. package/src/daemon/message-types/sync.ts +1 -0
  245. package/src/daemon/pkb-reminder-builder.test.ts +1 -1
  246. package/src/daemon/pkb-reminder-builder.ts +1 -1
  247. package/src/daemon/plugin-source-watcher.ts +146 -0
  248. package/src/daemon/process-message.ts +21 -3
  249. package/src/daemon/server.ts +11 -2
  250. package/src/daemon/skill-memory-refresh.ts +29 -0
  251. package/src/documents/document-store.ts +221 -3
  252. package/src/embedded/plugin-api.ts +40 -0
  253. package/src/filing/filing-service.ts +39 -0
  254. package/src/heartbeat/__tests__/heartbeat-service.test.ts +91 -6
  255. package/src/heartbeat/heartbeat-run-store.ts +2 -1
  256. package/src/heartbeat/heartbeat-service.ts +41 -0
  257. package/src/home/__tests__/feed-types.test.ts +40 -0
  258. package/src/home/feed-types.ts +22 -0
  259. package/src/home/post-connect-feed.ts +1 -0
  260. package/src/index.ts +18 -1
  261. package/src/live-voice/__tests__/live-voice-stt.test.ts +57 -0
  262. package/src/mcp/client.ts +20 -4
  263. package/src/media/image-credentials.ts +3 -3
  264. package/src/memory/__tests__/bookmark-crud.test.ts +33 -27
  265. package/src/memory/__tests__/conversation-queries.test.ts +263 -0
  266. package/src/memory/__tests__/jobs-worker-v2-graph-trigger-embed.test.ts +113 -0
  267. package/src/memory/__tests__/memory-retrospective-startup-cleanup.test.ts +119 -14
  268. package/src/memory/__tests__/message-content.test.ts +35 -0
  269. package/src/memory/bookmark-crud.ts +42 -10
  270. package/src/memory/context-search/sources/conversations.ts +62 -2
  271. package/src/memory/context-search/sources/workspace.ts +4 -0
  272. package/src/memory/conversation-crud.ts +63 -19
  273. package/src/memory/conversation-queries.ts +110 -10
  274. package/src/memory/db-init.ts +6 -0
  275. package/src/memory/delivery-crud.ts +152 -5
  276. package/src/memory/embedding-backend.ts +4 -4
  277. package/src/memory/external-conversation-store.ts +66 -5
  278. package/src/memory/graph/__tests__/conversation-graph-memory-v2-routing.test.ts +66 -9
  279. package/src/memory/graph/conversation-graph-memory.ts +31 -15
  280. package/src/memory/graph/tools.ts +3 -3
  281. package/src/memory/indexer.ts +34 -29
  282. package/src/memory/jobs/__tests__/embed-concept-page.test.ts +73 -0
  283. package/src/memory/jobs/embed-concept-page.ts +20 -11
  284. package/src/memory/jobs-worker.ts +6 -1
  285. package/src/memory/llm-request-log-source-clickhouse.ts +17 -10
  286. package/src/memory/llm-request-log-source.ts +19 -52
  287. package/src/memory/llm-usage-store.ts +125 -5
  288. package/src/memory/memory-retrospective-startup-cleanup.ts +72 -5
  289. package/src/memory/message-content.ts +1 -1
  290. package/src/memory/migrations/109-external-conversation-bindings.ts +15 -4
  291. package/src/memory/migrations/229-delete-private-conversations.test.ts +38 -1
  292. package/src/memory/migrations/229-delete-private-conversations.ts +7 -0
  293. package/src/memory/migrations/247-external-conversation-binding-thread-id.ts +78 -0
  294. package/src/memory/migrations/248-create-onboarding-events.ts +21 -0
  295. package/src/memory/migrations/249-normalize-slack-external-content.ts +240 -0
  296. package/src/memory/migrations/index.ts +6 -0
  297. package/src/memory/migrations/registry.ts +8 -0
  298. package/src/memory/onboarding-events-store.ts +106 -0
  299. package/src/memory/schema/bookmarks.ts +0 -2
  300. package/src/memory/schema/calls.ts +1 -0
  301. package/src/memory/schema/inference.ts +1 -3
  302. package/src/memory/schema/infrastructure.ts +12 -0
  303. package/src/memory/turn-events-store.ts +127 -2
  304. package/src/memory/v2/__tests__/activation.test.ts +0 -8
  305. package/src/memory/v2/__tests__/injection.test.ts +98 -8
  306. package/src/memory/v2/__tests__/migration.test.ts +87 -0
  307. package/src/memory/v2/__tests__/page-index.test.ts +83 -0
  308. package/src/memory/v2/__tests__/prompts-router.test.ts +58 -6
  309. package/src/memory/v2/__tests__/qdrant.test.ts +66 -3
  310. package/src/memory/v2/__tests__/router.test.ts +15 -0
  311. package/src/memory/v2/__tests__/skill-store.test.ts +387 -8
  312. package/src/memory/v2/injection.ts +32 -6
  313. package/src/memory/v2/migration.ts +49 -19
  314. package/src/memory/v2/page-index.ts +35 -5
  315. package/src/memory/v2/prompts/router.ts +11 -8
  316. package/src/memory/v2/prompts/sweep.ts +2 -2
  317. package/src/memory/v2/qdrant.ts +135 -7
  318. package/src/memory/v2/router.ts +9 -8
  319. package/src/memory/v2/skill-store.ts +120 -35
  320. package/src/messaging/providers/slack/__tests__/adapter-token-routing.test.ts +45 -5
  321. package/src/messaging/providers/slack/__tests__/download.test.ts +231 -0
  322. package/src/messaging/providers/slack/adapter.ts +43 -5
  323. package/src/messaging/providers/slack/client.ts +27 -0
  324. package/src/messaging/providers/slack/deep-link.ts +65 -0
  325. package/src/messaging/providers/slack/download.ts +104 -0
  326. package/src/messaging/providers/slack/message-metadata.test.ts +32 -0
  327. package/src/messaging/providers/slack/message-metadata.ts +27 -0
  328. package/src/messaging/providers/slack/render-transcript.test.ts +134 -0
  329. package/src/messaging/providers/slack/render-transcript.ts +69 -5
  330. package/src/messaging/providers/slack/types.ts +20 -1
  331. package/src/notifications/conversation-pairing.ts +2 -1
  332. package/src/notifications/decision-engine.ts +2 -1
  333. package/src/notifications/emit-signal.ts +20 -1
  334. package/src/notifications/home-feed-side-effect.ts +54 -0
  335. package/src/notifications/signal.ts +3 -1
  336. package/src/oauth/connection-resolver.ts +8 -4
  337. package/src/oauth/platform-connection.ts +6 -2
  338. package/src/oauth/seed-providers.ts +10 -1
  339. package/src/permissions/checker.ts +2 -0
  340. package/src/permissions/ipc-risk-types.ts +1 -0
  341. package/src/permissions/question-prompter.test.ts +416 -0
  342. package/src/permissions/question-prompter.ts +294 -0
  343. package/src/platform/client.test.ts +1 -1
  344. package/src/platform/client.ts +1 -1
  345. package/src/plugin-api/constants.ts +26 -0
  346. package/src/plugin-api/index.ts +34 -1
  347. package/src/plugin-api/types.ts +104 -22
  348. package/src/plugins/defaults/circuit-breaker.ts +0 -5
  349. package/src/plugins/defaults/compaction.ts +0 -4
  350. package/src/plugins/defaults/empty-response.ts +0 -2
  351. package/src/plugins/defaults/history-repair.ts +0 -2
  352. package/src/plugins/defaults/injectors.ts +36 -3
  353. package/src/plugins/defaults/llm-call.ts +0 -2
  354. package/src/plugins/defaults/memory-retrieval.ts +0 -1
  355. package/src/plugins/defaults/overflow-reduce.ts +0 -1
  356. package/src/plugins/defaults/persistence.ts +0 -2
  357. package/src/plugins/defaults/title-generate.ts +0 -5
  358. package/src/plugins/defaults/token-estimate.ts +0 -2
  359. package/src/plugins/defaults/tool-error.ts +0 -7
  360. package/src/plugins/defaults/tool-execute.ts +0 -2
  361. package/src/plugins/defaults/tool-result-truncate.ts +0 -4
  362. package/src/plugins/ensure-plugin-api-shim.ts +96 -0
  363. package/src/plugins/external-api.ts +104 -0
  364. package/src/plugins/external-plugin-loader.ts +105 -32
  365. package/src/plugins/feature-gate.ts +22 -0
  366. package/src/plugins/pipeline.ts +37 -0
  367. package/src/plugins/registry.ts +48 -80
  368. package/src/plugins/types.ts +31 -26
  369. package/src/plugins/user-loader.ts +21 -2
  370. package/src/proactive-artifact/aux-message-injector.ts +11 -0
  371. package/src/proactive-artifact/job.test.ts +37 -5
  372. package/src/prompts/__tests__/system-prompt.test.ts +12 -0
  373. package/src/prompts/__tests__/task-progress-hint-section.test.ts +99 -0
  374. package/src/prompts/normalize-onboarding.ts +27 -0
  375. package/src/prompts/sections.ts +302 -0
  376. package/src/prompts/system-prompt.ts +63 -166
  377. package/src/prompts/templates/BOOTSTRAP.md +17 -1
  378. package/src/prompts/templates/system-sections.ts +173 -0
  379. package/src/providers/__tests__/inference.test.ts +22 -7
  380. package/src/providers/anthropic/client.ts +28 -28
  381. package/src/providers/connection-resolution.ts +7 -0
  382. package/src/providers/inference/adapter-factory.ts +41 -4
  383. package/src/providers/inference/connections.ts +74 -29
  384. package/src/providers/inference/resolve-auth.ts +12 -4
  385. package/src/providers/model-catalog.ts +294 -12
  386. package/src/providers/openai/chat-completions-provider.ts +10 -2
  387. package/src/providers/openrouter/client.ts +7 -0
  388. package/src/providers/{managed-proxy → platform-proxy}/constants.ts +4 -1
  389. package/src/providers/{managed-proxy → platform-proxy}/context.ts +3 -3
  390. package/src/providers/provider-availability.ts +17 -2
  391. package/src/providers/provider-catalog-visibility.ts +36 -0
  392. package/src/providers/registry.ts +22 -14
  393. package/src/providers/retry.ts +47 -1
  394. package/src/runtime/__tests__/agent-wake.test.ts +152 -0
  395. package/src/runtime/agent-wake.ts +42 -14
  396. package/src/runtime/auth/route-policy.ts +8 -1
  397. package/src/runtime/btw-sidechain.ts +2 -0
  398. package/src/runtime/http-types.ts +19 -0
  399. package/src/runtime/migrations/origin-mode.ts +1 -1
  400. package/src/runtime/pending-interactions.ts +1 -0
  401. package/src/runtime/routes/__tests__/bookmark-routes.test.ts +17 -0
  402. package/src/runtime/routes/__tests__/conversation-management-routes.test.ts +5 -1
  403. package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +107 -20
  404. package/src/runtime/routes/__tests__/question-routes.test.ts +395 -0
  405. package/src/runtime/routes/__tests__/tts-routes.test.ts +64 -1
  406. package/src/runtime/routes/acp-routes-list.test.ts +143 -0
  407. package/src/runtime/routes/acp-routes.ts +5 -3
  408. package/src/runtime/routes/auth-routes.ts +1 -1
  409. package/src/runtime/routes/bookmark-routes.ts +5 -3
  410. package/src/runtime/routes/btw-routes.ts +5 -1
  411. package/src/runtime/routes/channel-availability-routes.ts +121 -0
  412. package/src/runtime/routes/conversation-cli-routes.ts +44 -3
  413. package/src/runtime/routes/conversation-list-routes.ts +3 -20
  414. package/src/runtime/routes/conversation-management-routes.ts +17 -42
  415. package/src/runtime/routes/conversation-query-routes.ts +40 -35
  416. package/src/runtime/routes/conversation-routes.ts +90 -11
  417. package/src/runtime/routes/documents-routes.ts +25 -86
  418. package/src/runtime/routes/group-routes.ts +5 -0
  419. package/src/runtime/routes/inbound-conversation.ts +28 -8
  420. package/src/runtime/routes/inbound-message-handler.ts +236 -41
  421. package/src/runtime/routes/inbound-stages/background-dispatch.test.ts +111 -0
  422. package/src/runtime/routes/inbound-stages/background-dispatch.ts +32 -1
  423. package/src/runtime/routes/inbound-stages/edit-intercept.ts +17 -4
  424. package/src/runtime/routes/index.ts +6 -0
  425. package/src/runtime/routes/inference-profile-session-handler.ts +17 -44
  426. package/src/runtime/routes/inference-profile-session-reaper.ts +7 -21
  427. package/src/runtime/routes/inference-provider-connection-routes.ts +65 -21
  428. package/src/runtime/routes/integrations/slack/share.ts +4 -52
  429. package/src/runtime/routes/integrations/slack/token.ts +43 -0
  430. package/src/runtime/routes/integrations/twilio.ts +6 -13
  431. package/src/runtime/routes/notification-routes.ts +1 -1
  432. package/src/runtime/routes/oauth-commands-routes.ts +105 -15
  433. package/src/runtime/routes/oauth-lifecycle-routes.ts +43 -0
  434. package/src/runtime/routes/question-routes.ts +259 -0
  435. package/src/runtime/routes/rename-conversation-routes.ts +2 -33
  436. package/src/runtime/routes/schedule-routes.ts +4 -7
  437. package/src/runtime/routes/subagents-routes.ts +57 -18
  438. package/src/runtime/routes/telemetry-routes.ts +27 -0
  439. package/src/runtime/routes/tts-routes.ts +27 -2
  440. package/src/runtime/routes/workspace-routes.test.ts +43 -0
  441. package/src/runtime/routes/workspace-routes.ts +28 -0
  442. package/src/runtime/services/conversation-serializer.ts +39 -7
  443. package/src/runtime/sync/resource-sync-events.ts +93 -1
  444. package/src/schedule/schedule-store.ts +27 -2
  445. package/src/schedule/scheduler.ts +9 -1
  446. package/src/security/__tests__/untrusted-content.test.ts +86 -0
  447. package/src/security/untrusted-content.ts +93 -8
  448. package/src/skills/catalog-files.ts +1 -1
  449. package/src/skills/catalog-install.ts +233 -116
  450. package/src/skills/clawhub.ts +70 -13
  451. package/src/skills/managed-store.ts +4 -119
  452. package/src/skills/skillssh-registry.ts +27 -48
  453. package/src/subagent/manager.ts +15 -7
  454. package/src/telemetry/types.ts +113 -1
  455. package/src/telemetry/usage-telemetry-reporter.test.ts +312 -5
  456. package/src/telemetry/usage-telemetry-reporter.ts +113 -7
  457. package/src/tools/apps/executors.ts +58 -7
  458. package/src/tools/ask-question/ask-question-tool.test.ts +509 -0
  459. package/src/tools/ask-question/ask-question-tool.ts +304 -0
  460. package/src/tools/browser/browser-execution.ts +15 -11
  461. package/src/tools/computer-use/definitions.ts +3 -3
  462. package/src/tools/credentials/vault.ts +1 -1
  463. package/src/tools/document/document-tool.ts +124 -1
  464. package/src/tools/filesystem/edit.ts +1 -1
  465. package/src/tools/filesystem/list.ts +1 -1
  466. package/src/tools/filesystem/read.ts +1 -1
  467. package/src/tools/filesystem/write.ts +5 -2
  468. package/src/tools/host-filesystem/transfer.ts +1 -1
  469. package/src/tools/host-terminal/host-shell.ts +1 -1
  470. package/src/tools/permission-checker.ts +1 -1
  471. package/src/tools/registry.ts +17 -7
  472. package/src/tools/schedule/create.ts +2 -2
  473. package/src/tools/schema-transforms.ts +7 -2
  474. package/src/tools/side-effects.ts +1 -0
  475. package/src/tools/skills/delete-managed.ts +4 -4
  476. package/src/tools/skills/execute.ts +1 -1
  477. package/src/tools/skills/scaffold-managed.ts +3 -2
  478. package/src/tools/subagent/notify-parent.ts +1 -1
  479. package/src/tools/system/request-permission.ts +2 -2
  480. package/src/tools/terminal/safe-env.ts +60 -1
  481. package/src/tools/tool-manifest.ts +2 -0
  482. package/src/tools/types.ts +72 -21
  483. package/src/tools/ui-surface/definitions.ts +6 -5
  484. package/src/tts/__tests__/provider-adapters.test.ts +76 -2
  485. package/src/tts/providers/elevenlabs-provider.ts +75 -1
  486. package/src/types/onboarding-context.ts +2 -0
  487. package/src/util/errors.ts +17 -0
  488. package/src/util/platform.ts +10 -0
  489. package/src/watcher/__tests__/engine.test.ts +22 -0
  490. package/src/watcher/engine.ts +6 -2
  491. package/src/workspace/migrations/057-repair-stale-gemini-model-ids.ts +80 -15
  492. package/src/workspace/migrations/072-seed-reply-suggestion-callsite.ts +35 -22
  493. package/src/workspace/migrations/073-repair-recall-callsite-empty-profile.ts +3 -1
  494. package/src/workspace/migrations/083-system-prompt-prefix-to-file.ts +191 -0
  495. package/src/workspace/migrations/084-remove-legacy-skills-index.ts +276 -0
  496. package/src/workspace/migrations/085-memory-v2-bm25-b-reembed-disabled-v2-pages.ts +137 -0
  497. package/src/workspace/migrations/086-revert-stale-gemini-mis-rewrites.ts +198 -0
  498. package/src/workspace/migrations/registry.ts +8 -0
  499. package/src/workspace/migrations/runner.ts +39 -9
  500. package/src/workspace/migrations/types.ts +4 -0
  501. package/examples/plugins/echo/bun.lock +0 -25
  502. package/src/__tests__/context-window-manager.test.ts +0 -2481
  503. package/src/context/__tests__/compact-prompt.test.ts +0 -63
  504. package/src/context/prompts/compact.md +0 -26
  505. package/src/prompts/__tests__/build-cli-reference-section.test.ts +0 -37
  506. /package/src/__tests__/{secret-routes-managed-proxy.test.ts → secret-routes-platform-proxy.test.ts} +0 -0
@@ -1,17 +1,24 @@
1
1
  #!/usr/bin/env bun
2
2
  /**
3
- * Generate `meta/web-search-provider-catalog.json` from the canonical
4
- * `SEARCH_PROVIDER_CATALOG` in
3
+ * Generate the client-facing web-search-provider catalog JSON from the
4
+ * canonical `SEARCH_PROVIDER_CATALOG` in
5
5
  * `assistant/src/providers/search-provider-catalog.ts`.
6
6
  *
7
- * Companion to `sync-llm-catalog.ts`. The meta JSON is the cross-package
8
- * artifact consumed by:
7
+ * Two byte-identical copies are written:
8
+ * - `meta/web-search-provider-catalog.json` primary checked-in artifact,
9
+ * consumed by:
10
+ * - `cli/src/__tests__/search-provider-env-var-parity.test.ts`
11
+ * (drift guard for the CLI's hardcoded env-var mirror).
12
+ * - Downstream `vellum-assistant-platform/web/src/lib/generated/
13
+ * web-search-provider-catalog.json` (manually sync'd today; the
14
+ * scheduled sync workflow is a planned follow-up).
15
+ * - `clients/shared/Resources/web-search-provider-catalog.json` — SwiftPM
16
+ * resource bundled into `VellumAssistantShared`. SwiftPM cannot reach
17
+ * files outside a target's source directory, so this mirror is
18
+ * necessary; both files are produced by the same generator and
19
+ * asserted equal by the parity test, making drift impossible.
9
20
  *
10
- * - `cli/src/__tests__/search-provider-env-var-parity.test.ts`
11
- * (drift guard for the CLI's hardcoded env-var mirror).
12
- * - Downstream `vellum-assistant-platform/web/src/lib/generated/
13
- * web-search-provider-catalog.json` (manually sync'd today; scheduled
14
- * sync workflow is a planned follow-up).
21
+ * Companion to `sync-llm-catalog.ts`; same dual-write pattern.
15
22
  *
16
23
  * Usage:
17
24
  * cd assistant && bun run scripts/sync-web-search-catalog.ts
@@ -20,7 +27,7 @@
20
27
  */
21
28
 
22
29
  import { readFile, writeFile } from "node:fs/promises";
23
- import { join, resolve } from "node:path";
30
+ import { join, relative, resolve } from "node:path";
24
31
 
25
32
  import {
26
33
  SEARCH_PROVIDER_CATALOG,
@@ -28,7 +35,10 @@ import {
28
35
  } from "../src/providers/search-provider-catalog.js";
29
36
 
30
37
  const ROOT = resolve(import.meta.dir, "../..");
31
- const OUTPUT_PATH = join(ROOT, "meta/web-search-provider-catalog.json");
38
+ const OUTPUT_PATHS = [
39
+ join(ROOT, "meta/web-search-provider-catalog.json"),
40
+ join(ROOT, "clients/shared/Resources/web-search-provider-catalog.json"),
41
+ ] as const;
32
42
 
33
43
  /**
34
44
  * Bumped when the *shape* of the client catalog JSON changes in a way
@@ -84,24 +94,36 @@ async function main(): Promise<void> {
84
94
  const next = generate();
85
95
 
86
96
  if (checkMode) {
87
- let current = "";
88
- try {
89
- current = await readFile(OUTPUT_PATH, "utf8");
90
- } catch {
91
- // File doesn't exist yet — treat as stale.
92
- }
93
- if (current !== next) {
94
- console.error(
95
- `\n${OUTPUT_PATH} is out of sync with SEARCH_PROVIDER_CATALOG.\n` +
96
- `Run: cd assistant && bun run sync:web-search-catalog\n`,
97
- );
98
- process.exit(1);
97
+ let anyStale = false;
98
+ for (const path of OUTPUT_PATHS) {
99
+ const rel = relative(ROOT, path);
100
+ let existing = "";
101
+ try {
102
+ existing = await readFile(path, "utf-8");
103
+ } catch {
104
+ console.error(
105
+ `${rel} does not exist. Run: bun run sync:web-search-catalog`,
106
+ );
107
+ anyStale = true;
108
+ continue;
109
+ }
110
+ if (existing !== next) {
111
+ console.error(
112
+ `${rel} is stale. Run: bun run sync:web-search-catalog`,
113
+ );
114
+ anyStale = true;
115
+ continue;
116
+ }
117
+ console.log(`${rel} is up to date.`);
99
118
  }
119
+ if (anyStale) process.exit(1);
100
120
  return;
101
121
  }
102
122
 
103
- await writeFile(OUTPUT_PATH, next, "utf8");
104
- console.log(`Wrote ${OUTPUT_PATH}`);
123
+ for (const path of OUTPUT_PATHS) {
124
+ await writeFile(path, next, "utf-8");
125
+ console.log(`Wrote ${relative(ROOT, path)}`);
126
+ }
105
127
  }
106
128
 
107
129
  await main();
@@ -14,10 +14,18 @@ describe("shouldRescaleImage", () => {
14
14
  expect(shouldRescaleImage({ width: 1200, height: 800 }, 50_000)).toBe(
15
15
  false,
16
16
  );
17
- // Even a large file is fine as long as dimensions are within limits —
18
- // Anthropic's constraint is per-side pixels, not bytes.
17
+ });
18
+
19
+ it("rescales when raw bytes would inflate past Anthropic's 5 MB base64 cap", () => {
20
+ // Regression: an oversized image retained across compaction was rejected
21
+ // with "image.source.base64: image exceeds 5 MB maximum" even though
22
+ // dimensions were within the 1568 px cap. base64 inflates raw bytes by
23
+ // 4/3, so anything over ~3.5 MB raw risks crossing the 5 MB API limit.
19
24
  expect(shouldRescaleImage({ width: 1568, height: 1568 }, 5_000_000)).toBe(
20
- false,
25
+ true,
26
+ );
27
+ expect(shouldRescaleImage({ width: 1200, height: 800 }, 4_000_000)).toBe(
28
+ true,
21
29
  );
22
30
  });
23
31
 
@@ -0,0 +1,131 @@
1
+ /**
2
+ * Regression test: `classifyDiskPressureTurnPolicy` must receive the
3
+ * caller-supplied `callSite` from `wakeAgentForOpportunity`, not a
4
+ * hardcoded `"mainAgent"`.
5
+ *
6
+ * Today the disk-pressure classifier's `isBackgroundTurn` branches on
7
+ * `isDirectWake` before it consults `callSite`, so the hardcoded value
8
+ * did not produce a runtime regression. But the metadata recorded the
9
+ * wrong call site for any wake initiated by a background job (e.g.
10
+ * memory-v2 consolidation), and the inconsistency would bite the moment
11
+ * policy ever branches on `callSite` for wake turns. This test pins the
12
+ * forwarded value so the contract stays honest.
13
+ */
14
+
15
+ import { beforeEach, describe, expect, mock, test } from "bun:test";
16
+
17
+ import type {
18
+ DiskPressureTurnMetadata,
19
+ DiskPressureTurnPolicyDecision,
20
+ } from "../daemon/disk-pressure-policy.js";
21
+ import type { Message } from "../providers/types.js";
22
+
23
+ mock.module("../memory/conversation-crud.js", () => ({
24
+ getConversationOverrideProfile: () => undefined,
25
+ }));
26
+
27
+ mock.module("../config/loader.js", () => ({
28
+ getConfig: () => ({ llm: {} }),
29
+ }));
30
+
31
+ mock.module("../config/llm-context-resolution.js", () => ({
32
+ resolveEffectiveContextWindow: () => ({ maxInputTokens: 200_000 }),
33
+ }));
34
+
35
+ const classifyCalls: DiskPressureTurnMetadata[] = [];
36
+ mock.module("../daemon/disk-pressure-policy.js", () => ({
37
+ classifyDiskPressureTurnPolicy: (
38
+ _status: unknown,
39
+ metadata: DiskPressureTurnMetadata,
40
+ ): DiskPressureTurnPolicyDecision => {
41
+ classifyCalls.push(metadata);
42
+ return { action: "allow-normal" };
43
+ },
44
+ }));
45
+
46
+ mock.module("../daemon/disk-pressure-guard.js", () => ({
47
+ getDiskPressureStatus: () => ({
48
+ enabled: false,
49
+ state: "disabled",
50
+ locked: false,
51
+ acknowledged: false,
52
+ overrideActive: false,
53
+ effectivelyLocked: false,
54
+ lockId: null,
55
+ usagePercent: null,
56
+ thresholdPercent: 95,
57
+ path: null,
58
+ lastCheckedAt: null,
59
+ blockedCapabilities: [],
60
+ error: null,
61
+ }),
62
+ }));
63
+
64
+ import {
65
+ __resetWakeChainForTests,
66
+ wakeAgentForOpportunity,
67
+ type WakeTarget,
68
+ } from "../runtime/agent-wake.js";
69
+
70
+ function makeTarget(): WakeTarget {
71
+ const history: Message[] = [];
72
+ let processing = false;
73
+ return {
74
+ conversationId: "conv-wake-callsite",
75
+ agentLoop: {
76
+ run: (async (messages: Message[]) =>
77
+ messages) as WakeTarget["agentLoop"]["run"],
78
+ },
79
+ getMessages: () => history,
80
+ pushMessage: (msg) => {
81
+ history.push(msg);
82
+ },
83
+ emitAgentEvent: () => {},
84
+ isProcessing: () => processing,
85
+ markProcessing: (on) => {
86
+ processing = on;
87
+ },
88
+ persistTailMessage: async () => {},
89
+ };
90
+ }
91
+
92
+ beforeEach(() => {
93
+ __resetWakeChainForTests();
94
+ classifyCalls.length = 0;
95
+ });
96
+
97
+ describe("wakeAgentForOpportunity — disk-pressure callSite forwarding", () => {
98
+ test("forwards opts.callSite to classifyDiskPressureTurnPolicy", async () => {
99
+ const target = makeTarget();
100
+
101
+ await wakeAgentForOpportunity(
102
+ {
103
+ conversationId: target.conversationId,
104
+ hint: "consolidate buffer",
105
+ source: "memory_v2_consolidation",
106
+ callSite: "memoryV2Consolidation",
107
+ },
108
+ { resolveTarget: async () => target },
109
+ );
110
+
111
+ expect(classifyCalls).toHaveLength(1);
112
+ expect(classifyCalls[0]!.callSite).toBe("memoryV2Consolidation");
113
+ expect(classifyCalls[0]!.isDirectWake).toBe(true);
114
+ });
115
+
116
+ test("defaults to mainAgent when opts.callSite is omitted", async () => {
117
+ const target = makeTarget();
118
+
119
+ await wakeAgentForOpportunity(
120
+ {
121
+ conversationId: target.conversationId,
122
+ hint: "resume",
123
+ source: "scheduler",
124
+ },
125
+ { resolveTarget: async () => target },
126
+ );
127
+
128
+ expect(classifyCalls).toHaveLength(1);
129
+ expect(classifyCalls[0]!.callSite).toBe("mainAgent");
130
+ });
131
+ });
@@ -223,6 +223,22 @@ describe("AnthropicProvider — Cache-Control Characterization", () => {
223
223
  expect(system[1].cache_control).toEqual({ type: "ephemeral", ttl: "1h" });
224
224
  });
225
225
 
226
+ test("omits empty dynamic system block after cache boundary", async () => {
227
+ const staticBlock = "You are a helpful assistant.";
228
+ const prompt = staticBlock + SYSTEM_PROMPT_CACHE_BOUNDARY;
229
+
230
+ await provider.sendMessage([userMsg("Hi")], undefined, prompt);
231
+
232
+ const system = lastStreamParams!.system as Array<{
233
+ type: string;
234
+ text: string;
235
+ cache_control?: { type: string; ttl?: string };
236
+ }>;
237
+ expect(system).toHaveLength(1);
238
+ expect(system[0].text).toBe(staticBlock);
239
+ expect(system[0].cache_control).toEqual({ type: "ephemeral", ttl: "1h" });
240
+ });
241
+
226
242
  test("drops static system block cache_control when total would exceed 4", async () => {
227
243
  const staticBlock = "You are a helpful assistant.";
228
244
  const dynamicBlock = "User workspace files here.";
@@ -2346,6 +2362,35 @@ describe("OpenRouterProvider — Anthropic dispatch", () => {
2346
2362
  expect(lastStreamParams!.reasoning).toBeUndefined();
2347
2363
  });
2348
2364
 
2365
+ test("sends OpenRouter app-attribution headers on Anthropic-compatible requests", async () => {
2366
+ const { OpenRouterProvider } =
2367
+ await import("../providers/openrouter/client.js");
2368
+ const provider = new OpenRouterProvider(
2369
+ "or-key",
2370
+ "anthropic/claude-sonnet-4.6",
2371
+ );
2372
+ await provider.sendMessage([userMsg("hi")], undefined, undefined, {
2373
+ config: {
2374
+ usageAttributionHeaders: {
2375
+ "Vellum-Organization-Id": "org-123",
2376
+ },
2377
+ },
2378
+ });
2379
+
2380
+ expect(_lastStreamOptions?.headers).toEqual(
2381
+ expect.objectContaining({
2382
+ "HTTP-Referer": "https://www.vellum.ai",
2383
+ "X-OpenRouter-Title": "Vellum Assistant",
2384
+ "X-OpenRouter-Categories": "personal-agent,cli-agent",
2385
+ "Vellum-Organization-Id": "org-123",
2386
+ }),
2387
+ );
2388
+ expect(lastStreamParams).not.toHaveProperty("HTTP-Referer");
2389
+ expect(lastStreamParams).not.toHaveProperty("X-OpenRouter-Title");
2390
+ expect(lastStreamParams).not.toHaveProperty("X-OpenRouter-Categories");
2391
+ expect(lastStreamParams).not.toHaveProperty("usageAttributionHeaders");
2392
+ });
2393
+
2349
2394
  test("per-request model override routes based on the overridden model", async () => {
2350
2395
  const { OpenRouterProvider } =
2351
2396
  await import("../providers/openrouter/client.js");
@@ -22,15 +22,18 @@ function makeApp(overrides: Partial<AppDefinition> = {}): AppDefinition {
22
22
  }
23
23
 
24
24
  function makeMockStore(overrides: Partial<AppStore> = {}): AppStore {
25
+ const files = new Set<string>();
25
26
  return {
26
27
  getApp: () => makeApp(),
27
28
  listApps: () => [makeApp()],
28
- appFileExists: () => false,
29
+ appFileExists: (_appId: string, path: string) => files.has(path),
29
30
  createApp: (params) =>
30
31
  makeApp({ name: params.name, description: params.description }),
31
32
  updateApp: (id, updates) => makeApp({ id, ...updates }),
32
33
  deleteApp: () => {},
33
- writeAppFile: () => {},
34
+ writeAppFile: (_appId: string, path: string) => {
35
+ files.add(path);
36
+ },
34
37
  ...overrides,
35
38
  };
36
39
  }
@@ -107,7 +110,10 @@ describe("app-builder skill tool scripts", () => {
107
110
  isError: false,
108
111
  });
109
112
  const result = await appCreateScript.run(
110
- { name: "Auto App" },
113
+ {
114
+ name: "Auto App",
115
+ source_files: { "src/main.tsx": "// real code" },
116
+ },
111
117
  makeContext({ proxyToolResolver: proxy }),
112
118
  );
113
119
  expect(result.isError).toBe(false);
@@ -1,13 +1,31 @@
1
- import { describe, expect, mock, test } from "bun:test";
1
+ import { beforeEach, describe, expect, mock, test } from "bun:test";
2
+
3
+ type CompileResult = {
4
+ ok: boolean;
5
+ errors: string[];
6
+ warnings: string[];
7
+ durationMs: number;
8
+ };
9
+
10
+ let compileResultOverride: CompileResult = {
11
+ ok: true,
12
+ errors: [],
13
+ warnings: [],
14
+ durationMs: 0,
15
+ };
2
16
 
3
17
  mock.module("../bundler/app-compiler.js", () => ({
4
- compileApp: async () => ({
18
+ compileApp: async () => compileResultOverride,
19
+ }));
20
+
21
+ beforeEach(() => {
22
+ compileResultOverride = {
5
23
  ok: true,
6
24
  errors: [],
7
25
  warnings: [],
8
26
  durationMs: 0,
9
- }),
10
- }));
27
+ };
28
+ });
11
29
 
12
30
  import type { AppDefinition } from "../memory/app-store.js";
13
31
  import type { AppStore } from "../tools/apps/executors.js";
@@ -102,6 +120,204 @@ describe("executeAppCreate", () => {
102
120
  expect(files["src/main.tsx"]).toBeDefined();
103
121
  expect(files["src/main.tsx"]).toContain("import { render } from 'preact'");
104
122
  expect(files["src/main.tsx"]).toContain('{"Hello, New App!"}');
123
+ // next_steps directive must be present so the model keeps writing
124
+ // real source files instead of treating the scaffold as done.
125
+ const parsed = JSON.parse(result.content);
126
+ expect(parsed.next_steps).toContain("placeholder src/main.tsx");
127
+ expect(parsed.next_steps).toContain("app_refresh");
128
+ });
129
+
130
+ test("skips auto_open on scaffold even when proxy resolver is available", async () => {
131
+ const files: Record<string, string> = {};
132
+ const app = makeMultifileApp({ name: "New App" });
133
+ const store: AppStore = {
134
+ ...mockStore(app, files),
135
+ createApp: () => app,
136
+ };
137
+ let proxyCalled = false;
138
+ const proxyResolver = async () => {
139
+ proxyCalled = true;
140
+ return { content: JSON.stringify({ opened: true }), isError: false };
141
+ };
142
+
143
+ const result = await executeAppCreate(
144
+ { name: "New App" },
145
+ store,
146
+ proxyResolver,
147
+ );
148
+
149
+ expect(result.isError).toBe(false);
150
+ expect(proxyCalled).toBe(false);
151
+ const parsed = JSON.parse(result.content);
152
+ expect(parsed.auto_opened).toBeUndefined();
153
+ expect(parsed.next_steps).toContain("placeholder src/main.tsx");
154
+ expect(parsed.next_steps).toContain("app_refresh");
155
+ });
156
+
157
+ test("omits next_steps when main.tsx was pre-written before app_create", async () => {
158
+ // The agent's supported workflow is to write the real source files first
159
+ // and then call app_create. In that case the placeholder directive would
160
+ // be false and risk triggering a destructive rewrite of correct code.
161
+ const files: Record<string, string> = {
162
+ "src/main.tsx": "// real code from the agent",
163
+ };
164
+ const app = makeMultifileApp({ name: "Pre-written App" });
165
+ const store: AppStore = {
166
+ ...mockStore(app, files),
167
+ createApp: () => app,
168
+ };
169
+
170
+ const result = await executeAppCreate({ name: "Pre-written App" }, store);
171
+
172
+ expect(result.isError).toBe(false);
173
+ // The pre-written file must be preserved
174
+ expect(files["src/main.tsx"]).toBe("// real code from the agent");
175
+ const parsed = JSON.parse(result.content);
176
+ expect(parsed.next_steps).toBeUndefined();
177
+ });
178
+
179
+ test("includes next_steps when compile fails on the scaffold", async () => {
180
+ compileResultOverride = {
181
+ ok: false,
182
+ errors: ["unexpected token"],
183
+ warnings: [],
184
+ durationMs: 3,
185
+ };
186
+ const files: Record<string, string> = {};
187
+ const app = makeMultifileApp({ name: "Broken App" });
188
+ const store: AppStore = {
189
+ ...mockStore(app, files),
190
+ createApp: () => app,
191
+ };
192
+
193
+ const result = await executeAppCreate({ name: "Broken App" }, store);
194
+
195
+ expect(result.isError).toBe(false);
196
+ const parsed = JSON.parse(result.content);
197
+ expect(parsed.compile_errors).toEqual(["unexpected token"]);
198
+ expect(parsed.next_steps).toContain("placeholder src/main.tsx");
199
+ expect(parsed.next_steps).toContain("app_refresh");
200
+ });
201
+
202
+ test("omits next_steps when compile fails on pre-written files", async () => {
203
+ compileResultOverride = {
204
+ ok: false,
205
+ errors: ["unexpected token"],
206
+ warnings: [],
207
+ durationMs: 3,
208
+ };
209
+ const files: Record<string, string> = {
210
+ "src/main.tsx": "// agent's real (but broken) code",
211
+ };
212
+ const app = makeMultifileApp({ name: "Broken Pre-written App" });
213
+ const store: AppStore = {
214
+ ...mockStore(app, files),
215
+ createApp: () => app,
216
+ };
217
+
218
+ const result = await executeAppCreate(
219
+ { name: "Broken Pre-written App" },
220
+ store,
221
+ );
222
+
223
+ expect(result.isError).toBe(false);
224
+ const parsed = JSON.parse(result.content);
225
+ expect(parsed.compile_errors).toEqual(["unexpected token"]);
226
+ expect(parsed.next_steps).toBeUndefined();
227
+ });
228
+
229
+ test("suppresses auto_open when only scaffold exists", async () => {
230
+ const files: Record<string, string> = {};
231
+ const app = makeMultifileApp({ name: "New App" });
232
+ const store: AppStore = {
233
+ ...mockStore(app, files),
234
+ createApp: () => app,
235
+ };
236
+ let proxyCalledWith: Record<string, unknown> | undefined;
237
+ const proxyResolver = async (
238
+ toolName: string,
239
+ input: Record<string, unknown>,
240
+ ) => {
241
+ proxyCalledWith = { toolName, input };
242
+ return { content: JSON.stringify({ opened: true }), isError: false };
243
+ };
244
+
245
+ const result = await executeAppCreate(
246
+ { name: "New App" },
247
+ store,
248
+ proxyResolver,
249
+ );
250
+
251
+ expect(result.isError).toBe(false);
252
+ const parsed = JSON.parse(result.content);
253
+ expect(proxyCalledWith).toBeUndefined();
254
+ expect(parsed.auto_opened).toBeUndefined();
255
+ expect(parsed.next_steps).toContain("placeholder src/main.tsx");
256
+ });
257
+
258
+ test("fires auto_open when source_files includes main.tsx", async () => {
259
+ const files: Record<string, string> = {};
260
+ const app = makeMultifileApp({ name: "Real App" });
261
+ const store: AppStore = {
262
+ ...mockStore(app, files),
263
+ createApp: () => app,
264
+ };
265
+ let proxyCalledWith: Record<string, unknown> | undefined;
266
+ const proxyResolver = async (
267
+ toolName: string,
268
+ input: Record<string, unknown>,
269
+ ) => {
270
+ proxyCalledWith = { toolName, input };
271
+ return { content: JSON.stringify({ opened: true }), isError: false };
272
+ };
273
+
274
+ const result = await executeAppCreate(
275
+ {
276
+ name: "Real App",
277
+ source_files: {
278
+ "src/main.tsx":
279
+ "import { render } from 'preact';\nrender(<div>Real</div>, document.getElementById('app')!);",
280
+ "src/styles.css": "body { margin: 0; }",
281
+ },
282
+ },
283
+ store,
284
+ proxyResolver,
285
+ );
286
+
287
+ expect(result.isError).toBe(false);
288
+ const parsed = JSON.parse(result.content);
289
+ expect(proxyCalledWith).toBeDefined();
290
+ expect(parsed.auto_opened).toBe(true);
291
+ expect(parsed.next_steps).toBeUndefined();
292
+ expect(files["src/main.tsx"]).toContain("Real");
293
+ expect(files["src/styles.css"]).toContain("margin");
294
+ });
295
+
296
+ test("source_files are written and prevent scaffold", async () => {
297
+ const files: Record<string, string> = {};
298
+ const app = makeMultifileApp({ name: "Inline App" });
299
+ const store: AppStore = {
300
+ ...mockStore(app, files),
301
+ createApp: () => app,
302
+ };
303
+
304
+ const result = await executeAppCreate(
305
+ {
306
+ name: "Inline App",
307
+ source_files: {
308
+ "src/main.tsx": "// custom app code",
309
+ "src/components/App.tsx": "// App component",
310
+ },
311
+ },
312
+ store,
313
+ );
314
+
315
+ expect(result.isError).toBe(false);
316
+ expect(files["src/main.tsx"]).toBe("// custom app code");
317
+ expect(files["src/components/App.tsx"]).toBe("// App component");
318
+ expect(files["src/index.html"]).toBeDefined();
319
+ const parsed = JSON.parse(result.content);
320
+ expect(parsed.next_steps).toBeUndefined();
105
321
  });
106
322
 
107
323
  test("rejects retired html shortcut", async () => {
@@ -585,3 +585,38 @@ describe("indexer v1/v2 mutual exclusion for graph_extract", () => {
585
585
  );
586
586
  });
587
587
  });
588
+
589
+ // ─────────────────────────────────────────────────────────────────
590
+ // Indexer v1/v2 mutual exclusion: build_conversation_summary feeds
591
+ // v1-only readers (fetchRecentSummaries, semantic search). When
592
+ // memory.v2.enabled is on, the summary writes are unread, so the
593
+ // indexer must not enqueue them.
594
+ // ─────────────────────────────────────────────────────────────────
595
+
596
+ describe("indexer v1/v2 mutual exclusion for build_conversation_summary", () => {
597
+ const originalV2Enabled = TEST_CONFIG.memory.v2.enabled;
598
+
599
+ afterEach(() => {
600
+ TEST_CONFIG.memory.v2.enabled = originalV2Enabled;
601
+ });
602
+
603
+ test("v2 active (config on) → build_conversation_summary not enqueued", async () => {
604
+ TEST_CONFIG.memory.v2.enabled = true;
605
+
606
+ const source = createConversation("summary-v2-active");
607
+ await indexMessages(source.id, 2);
608
+
609
+ expect(countJobsOfType("build_conversation_summary", source.id)).toBe(0);
610
+ });
611
+
612
+ test("config gate off → build_conversation_summary enqueued", async () => {
613
+ TEST_CONFIG.memory.v2.enabled = false;
614
+
615
+ const source = createConversation("summary-v2-config-off");
616
+ await indexMessages(source.id, 2);
617
+
618
+ expect(
619
+ countJobsOfType("build_conversation_summary", source.id),
620
+ ).toBeGreaterThanOrEqual(1);
621
+ });
622
+ });
@@ -101,18 +101,18 @@ describe("resolveBundledDir", () => {
101
101
  const macosDir = join(tempDir, "Contents", "MacOS");
102
102
  const resourcesDir = join(tempDir, "Contents", "Resources");
103
103
  mkdirSync(macosDir, { recursive: true });
104
- mkdirSync(join(resourcesDir, "compact-prompts"), { recursive: true });
104
+ mkdirSync(join(resourcesDir, "templates"), { recursive: true });
105
105
  // Also create at execDir level
106
- mkdirSync(join(macosDir, "compact-prompts"), { recursive: true });
106
+ mkdirSync(join(macosDir, "templates"), { recursive: true });
107
107
 
108
108
  process.execPath = join(macosDir, "vellum-daemon");
109
109
 
110
110
  const result = resolveBundledDir(
111
- "/$bunfs/root/src/context/prompts",
112
- "..",
113
- "compact-prompts",
111
+ "/$bunfs/root/src/prompts",
112
+ "templates",
113
+ "templates",
114
114
  );
115
- expect(result).toBe(join(resourcesDir, "compact-prompts"));
115
+ expect(result).toBe(join(resourcesDir, "templates"));
116
116
  });
117
117
 
118
118
  test("works with different bundleName values", () => {