@vellumai/assistant 0.5.1 → 0.5.3

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 (405) hide show
  1. package/ARCHITECTURE.md +163 -54
  2. package/docs/architecture/integrations.md +62 -67
  3. package/docs/credential-execution-service.md +3 -3
  4. package/docs/skills.md +100 -0
  5. package/package.json +1 -1
  6. package/src/__tests__/agent-loop.test.ts +111 -0
  7. package/src/__tests__/always-loaded-tools-guard.test.ts +3 -4
  8. package/src/__tests__/app-builder-tool-scripts.test.ts +13 -151
  9. package/src/__tests__/app-dir-path-guard.test.ts +78 -0
  10. package/src/__tests__/app-executors.test.ts +1 -291
  11. package/src/__tests__/app-git-history.test.ts +4 -4
  12. package/src/__tests__/app-routes-csp.test.ts +1 -0
  13. package/src/__tests__/app-store-dir-names.test.ts +426 -0
  14. package/src/__tests__/attachments-store.test.ts +169 -21
  15. package/src/__tests__/attachments.test.ts +115 -1
  16. package/src/__tests__/btw-routes.test.ts +1 -0
  17. package/src/__tests__/canonical-guardian-store.test.ts +38 -0
  18. package/src/__tests__/channel-reply-delivery.test.ts +55 -0
  19. package/src/__tests__/checker.test.ts +54 -0
  20. package/src/__tests__/claude-code-skill-regression.test.ts +2 -0
  21. package/src/__tests__/claude-code-tool-profiles.test.ts +2 -0
  22. package/src/__tests__/compaction.benchmark.test.ts +2 -1
  23. package/src/__tests__/config-schema-cmd.test.ts +68 -21
  24. package/src/__tests__/config-schema.test.ts +1 -1
  25. package/src/__tests__/conversation-agent-loop-overflow.test.ts +156 -5
  26. package/src/__tests__/conversation-agent-loop.test.ts +297 -2
  27. package/src/__tests__/conversation-attachments.test.ts +17 -19
  28. package/src/__tests__/conversation-disk-view-integration.test.ts +277 -0
  29. package/src/__tests__/conversation-disk-view.test.ts +810 -0
  30. package/src/__tests__/conversation-error.test.ts +1 -1
  31. package/src/__tests__/conversation-fork-crud.test.ts +551 -0
  32. package/src/__tests__/conversation-fork-route.test.ts +386 -0
  33. package/src/__tests__/conversation-history-web-search.test.ts +1 -1
  34. package/src/__tests__/conversation-key-store-disk-view.test.ts +130 -0
  35. package/src/__tests__/conversation-media-retry.test.ts +8 -2
  36. package/src/__tests__/conversation-memory-dirty-tail.test.ts +150 -0
  37. package/src/__tests__/conversation-provider-retry-repair.test.ts +7 -0
  38. package/src/__tests__/conversation-queue.test.ts +36 -1
  39. package/src/__tests__/conversation-routes-disk-view.test.ts +439 -0
  40. package/src/__tests__/conversation-routes-guardian-reply.test.ts +2 -2
  41. package/src/__tests__/conversation-routes-slash-commands.test.ts +2 -7
  42. package/src/__tests__/conversation-runtime-assembly.test.ts +17 -2
  43. package/src/__tests__/conversation-skill-tools.test.ts +4 -9
  44. package/src/__tests__/conversation-slash-commands.test.ts +149 -0
  45. package/src/__tests__/conversation-store.test.ts +24 -21
  46. package/src/__tests__/conversation-surfaces-state-update.test.ts +246 -0
  47. package/src/__tests__/conversation-surfaces-task-progress.test.ts +1 -0
  48. package/src/__tests__/conversation-title-service.test.ts +137 -0
  49. package/src/__tests__/conversation-tool-setup-app-refresh.test.ts +25 -315
  50. package/src/__tests__/conversation-tool-setup-memory-scope.test.ts +1 -0
  51. package/src/__tests__/conversation-tool-setup-side-effect-flag.test.ts +1 -0
  52. package/src/__tests__/conversation-wipe.test.ts +226 -0
  53. package/src/__tests__/conversation-workspace-cache-state.test.ts +44 -2
  54. package/src/__tests__/conversation-workspace-injection.test.ts +11 -0
  55. package/src/__tests__/credential-security-invariants.test.ts +3 -0
  56. package/src/__tests__/credential-vault-unit.test.ts +5 -10
  57. package/src/__tests__/cu-unified-flow.test.ts +1 -0
  58. package/src/__tests__/db-conversation-fork-lineage-migration.test.ts +241 -0
  59. package/src/__tests__/db-llm-request-log-provider-migration.test.ts +214 -0
  60. package/src/__tests__/db-memory-archive-migration.test.ts +372 -0
  61. package/src/__tests__/db-memory-brief-state-migration.test.ts +213 -0
  62. package/src/__tests__/db-memory-reducer-checkpoints.test.ts +273 -0
  63. package/src/__tests__/diagnostics-export.test.ts +70 -1
  64. package/src/__tests__/first-greeting.test.ts +80 -0
  65. package/src/__tests__/gateway-only-guard.test.ts +1 -0
  66. package/src/__tests__/handlers-user-message-approval-consumption.test.ts +3 -7
  67. package/src/__tests__/history-repair.test.ts +32 -10
  68. package/src/__tests__/http-conversation-lineage.test.ts +251 -0
  69. package/src/__tests__/image-source-path-reinject.test.ts +136 -0
  70. package/src/__tests__/inline-command-runner.test.ts +311 -0
  71. package/src/__tests__/inline-skill-authoring-guard.test.ts +220 -0
  72. package/src/__tests__/inline-skill-load-permissions.test.ts +435 -0
  73. package/src/__tests__/list-messages-attachments.test.ts +96 -0
  74. package/src/__tests__/llm-context-normalization.test.ts +1116 -0
  75. package/src/__tests__/llm-context-route-provider.test.ts +217 -0
  76. package/src/__tests__/llm-request-log-turn-query.test.ts +270 -0
  77. package/src/__tests__/media-generate-image.test.ts +47 -94
  78. package/src/__tests__/memory-brief-open-loops.test.ts +530 -0
  79. package/src/__tests__/memory-brief-time.test.ts +285 -0
  80. package/src/__tests__/memory-brief-wrapper.test.ts +311 -0
  81. package/src/__tests__/memory-chunk-archive.test.ts +400 -0
  82. package/src/__tests__/memory-chunk-dual-write.test.ts +453 -0
  83. package/src/__tests__/memory-episode-archive.test.ts +370 -0
  84. package/src/__tests__/memory-episode-dual-write.test.ts +626 -0
  85. package/src/__tests__/memory-lifecycle-e2e.test.ts +3 -1
  86. package/src/__tests__/memory-observation-archive.test.ts +375 -0
  87. package/src/__tests__/memory-observation-dual-write.test.ts +318 -0
  88. package/src/__tests__/memory-recall-quality.test.ts +7 -7
  89. package/src/__tests__/memory-reducer-store.test.ts +728 -0
  90. package/src/__tests__/memory-reducer-types.test.ts +699 -0
  91. package/src/__tests__/memory-reducer.test.ts +698 -0
  92. package/src/__tests__/memory-regressions.test.ts +6 -4
  93. package/src/__tests__/memory-simplified-config.test.ts +281 -0
  94. package/src/__tests__/migration-cross-version-compatibility.test.ts +4 -1
  95. package/src/__tests__/migration-export-http.test.ts +3 -1
  96. package/src/__tests__/migration-import-commit-http.test.ts +18 -4
  97. package/src/__tests__/migration-import-preflight-http.test.ts +1 -3
  98. package/src/__tests__/mime-builder.test.ts +3 -2
  99. package/src/__tests__/non-member-access-request.test.ts +12 -1
  100. package/src/__tests__/notification-decision-identity.test.ts +52 -0
  101. package/src/__tests__/oauth-apps-routes.test.ts +103 -0
  102. package/src/__tests__/oauth-store.test.ts +115 -0
  103. package/src/__tests__/parse-identity-fields.test.ts +129 -0
  104. package/src/__tests__/provider-error-scenarios.test.ts +1 -3
  105. package/src/__tests__/provider-failover-actual-provider.test.ts +66 -0
  106. package/src/__tests__/recording-handler.test.ts +17 -0
  107. package/src/__tests__/registry.test.ts +3 -8
  108. package/src/__tests__/relay-server.test.ts +1 -1
  109. package/src/__tests__/runtime-attachment-metadata.test.ts +7 -3
  110. package/src/__tests__/schema-transforms.test.ts +165 -5
  111. package/src/__tests__/server-history-render.test.ts +2 -2
  112. package/src/__tests__/skill-load-inline-command.test.ts +598 -0
  113. package/src/__tests__/skill-load-inline-includes.test.ts +644 -0
  114. package/src/__tests__/skills-inline-command-expansions.test.ts +301 -0
  115. package/src/__tests__/skills-transitive-hash.test.ts +333 -0
  116. package/src/__tests__/slack-app-setup-skill-regression.test.ts +3 -1
  117. package/src/__tests__/slack-inbound-verification.test.ts +2 -2
  118. package/src/__tests__/starter-task-flow.test.ts +1 -0
  119. package/src/__tests__/suggestion-routes.test.ts +443 -0
  120. package/src/__tests__/swarm-conversation-integration.test.ts +1 -0
  121. package/src/__tests__/swarm-recursion.test.ts +1 -0
  122. package/src/__tests__/swarm-tool.test.ts +1 -0
  123. package/src/__tests__/tool-execution-abort-cleanup.test.ts +1 -0
  124. package/src/__tests__/tool-preview-lifecycle.test.ts +32 -5
  125. package/src/__tests__/top-level-renderer.test.ts +22 -0
  126. package/src/__tests__/turn-boundary-resolution.test.ts +243 -0
  127. package/src/__tests__/vellum-self-knowledge-inline-command.test.ts +320 -0
  128. package/src/__tests__/web-fetch.test.ts +6 -2
  129. package/src/__tests__/workspace-migration-006-services-config.test.ts +335 -0
  130. package/src/__tests__/workspace-migration-007-web-search-provider-rename.test.ts +312 -0
  131. package/src/__tests__/workspace-migration-009-backfill-conversation-disk-view.test.ts +278 -0
  132. package/src/__tests__/workspace-migration-010-app-dir-rename.test.ts +275 -0
  133. package/src/__tests__/workspace-migration-012-rename-conversation-disk-view-dirs.test.ts +77 -0
  134. package/src/__tests__/workspace-migration-013-repair-conversation-disk-view.test.ts +401 -0
  135. package/src/__tests__/workspace-migration-backfill-installation-id.test.ts +328 -0
  136. package/src/__tests__/workspace-migration-seed-device-id.test.ts +6 -10
  137. package/src/agent/attachments.ts +27 -1
  138. package/src/agent/loop.ts +29 -1
  139. package/src/avatar/traits-png-sync.ts +80 -25
  140. package/src/bundler/app-bundler.ts +4 -4
  141. package/src/calls/call-domain.ts +1 -0
  142. package/src/calls/voice-session-bridge.ts +1 -0
  143. package/src/cli/commands/auth.ts +92 -0
  144. package/src/cli/commands/avatar.ts +7 -6
  145. package/src/cli/commands/config.ts +2 -0
  146. package/src/cli/commands/oauth/providers.ts +29 -0
  147. package/src/cli/program.ts +12 -0
  148. package/src/cli.ts +15 -48
  149. package/src/config/bundled-skills/app-builder/SKILL.md +103 -28
  150. package/src/config/bundled-skills/app-builder/TOOLS.json +5 -199
  151. package/src/config/bundled-skills/app-builder/tools/{app-query.ts → app-refresh.ts} +2 -2
  152. package/src/config/bundled-skills/contacts/tools/google-contacts.ts +2 -3
  153. package/src/config/bundled-skills/gmail/tools/gmail-archive.ts +6 -9
  154. package/src/config/bundled-skills/gmail/tools/gmail-attachments.ts +4 -6
  155. package/src/config/bundled-skills/gmail/tools/gmail-draft.ts +2 -3
  156. package/src/config/bundled-skills/gmail/tools/gmail-filters.ts +2 -3
  157. package/src/config/bundled-skills/gmail/tools/gmail-follow-up.ts +2 -3
  158. package/src/config/bundled-skills/gmail/tools/gmail-forward.ts +2 -3
  159. package/src/config/bundled-skills/gmail/tools/gmail-label.ts +4 -6
  160. package/src/config/bundled-skills/gmail/tools/gmail-outreach-scan.ts +2 -3
  161. package/src/config/bundled-skills/gmail/tools/gmail-send-draft.ts +2 -3
  162. package/src/config/bundled-skills/gmail/tools/gmail-sender-digest.ts +2 -3
  163. package/src/config/bundled-skills/gmail/tools/gmail-trash.ts +2 -3
  164. package/src/config/bundled-skills/gmail/tools/gmail-unsubscribe.ts +2 -3
  165. package/src/config/bundled-skills/gmail/tools/gmail-vacation.ts +2 -3
  166. package/src/config/bundled-skills/google-calendar/tools/shared.ts +1 -1
  167. package/src/config/bundled-skills/image-studio/SKILL.md +2 -2
  168. package/src/config/bundled-skills/image-studio/TOOLS.json +2 -2
  169. package/src/config/bundled-skills/image-studio/tools/media-generate-image.ts +45 -72
  170. package/src/config/bundled-skills/media-processing/tools/extract-keyframes.ts +2 -2
  171. package/src/config/bundled-skills/messaging/tools/shared.ts +1 -1
  172. package/src/config/bundled-skills/settings/tools/voice-config-update.ts +19 -3
  173. package/src/config/bundled-skills/skill-management/SKILL.md +1 -1
  174. package/src/config/bundled-skills/skill-management/TOOLS.json +2 -2
  175. package/src/config/bundled-skills/slack/tools/shared.ts +19 -4
  176. package/src/config/bundled-skills/slack/tools/slack-scan-digest.ts +2 -3
  177. package/src/config/bundled-skills/transcribe/SKILL.md +1 -1
  178. package/src/config/bundled-skills/transcribe/TOOLS.json +2 -6
  179. package/src/config/bundled-skills/transcribe/tools/transcribe-media.ts +19 -83
  180. package/src/config/bundled-tool-registry.ts +2 -14
  181. package/src/config/feature-flag-registry.json +24 -0
  182. package/src/config/loader.ts +65 -0
  183. package/src/config/raw-config-utils.ts +58 -0
  184. package/src/config/schema-utils.ts +28 -7
  185. package/src/config/schema.ts +20 -0
  186. package/src/config/schemas/elevenlabs.ts +18 -0
  187. package/src/config/schemas/memory-lifecycle.ts +4 -2
  188. package/src/config/schemas/memory-simplified.ts +101 -0
  189. package/src/config/schemas/memory-storage.ts +1 -1
  190. package/src/config/schemas/memory.ts +4 -0
  191. package/src/config/schemas/services.ts +8 -6
  192. package/src/config/skills.ts +50 -4
  193. package/src/contacts/contact-store.ts +13 -6
  194. package/src/contacts/contacts-write.ts +0 -1
  195. package/src/context/window-manager.ts +13 -2
  196. package/src/daemon/conversation-agent-loop-handlers.ts +54 -8
  197. package/src/daemon/conversation-agent-loop.ts +127 -20
  198. package/src/daemon/conversation-attachments.ts +18 -36
  199. package/src/daemon/conversation-error.ts +2 -1
  200. package/src/daemon/conversation-history.ts +18 -4
  201. package/src/daemon/conversation-lifecycle.ts +50 -16
  202. package/src/daemon/conversation-messaging.ts +70 -26
  203. package/src/daemon/conversation-process.ts +58 -34
  204. package/src/daemon/conversation-runtime-assembly.ts +22 -38
  205. package/src/daemon/conversation-slash.ts +121 -256
  206. package/src/daemon/conversation-surfaces.ts +170 -24
  207. package/src/daemon/conversation-tool-setup.ts +0 -6
  208. package/src/daemon/conversation-workspace.ts +21 -1
  209. package/src/daemon/conversation.ts +69 -30
  210. package/src/daemon/first-greeting.ts +35 -0
  211. package/src/daemon/handlers/config-embeddings.ts +156 -0
  212. package/src/daemon/handlers/config-model.ts +62 -26
  213. package/src/daemon/handlers/conversations.ts +0 -23
  214. package/src/daemon/handlers/identity.ts +12 -1
  215. package/src/daemon/handlers/recording.ts +26 -21
  216. package/src/daemon/host-cu-proxy.ts +2 -2
  217. package/src/daemon/lifecycle.ts +115 -65
  218. package/src/daemon/message-protocol.ts +3 -0
  219. package/src/daemon/message-types/conversations.ts +18 -0
  220. package/src/daemon/message-types/messages.ts +1 -0
  221. package/src/daemon/message-types/shared.ts +2 -0
  222. package/src/daemon/message-types/surfaces.ts +2 -0
  223. package/src/daemon/message-types/upgrades.ts +23 -0
  224. package/src/daemon/server.ts +83 -12
  225. package/src/daemon/shutdown-handlers.ts +8 -5
  226. package/src/daemon/startup-error.ts +9 -0
  227. package/src/daemon/tool-side-effects.ts +11 -28
  228. package/src/events/tool-permission-telemetry-listener.ts +1 -3
  229. package/src/followups/followup-store.ts +47 -1
  230. package/src/instrument.ts +0 -4
  231. package/src/media/app-icon-generator.ts +2 -2
  232. package/src/memory/app-git-service.ts +28 -16
  233. package/src/memory/app-store.ts +230 -41
  234. package/src/memory/archive-store.ts +400 -0
  235. package/src/memory/attachments-store.ts +558 -130
  236. package/src/memory/brief-formatting.ts +33 -0
  237. package/src/memory/brief-open-loops.ts +266 -0
  238. package/src/memory/brief-time.ts +161 -0
  239. package/src/memory/brief.ts +75 -0
  240. package/src/memory/conversation-attention-store.ts +70 -0
  241. package/src/memory/conversation-crud.ts +591 -8
  242. package/src/memory/conversation-directories.ts +125 -0
  243. package/src/memory/conversation-disk-view.ts +390 -0
  244. package/src/memory/conversation-key-store.ts +17 -5
  245. package/src/memory/conversation-queries.ts +5 -1
  246. package/src/memory/conversation-title-service.ts +21 -49
  247. package/src/memory/db-init.ts +40 -0
  248. package/src/memory/embedding-backend.ts +42 -53
  249. package/src/memory/embedding-gemini.test.ts +4 -4
  250. package/src/memory/embedding-local.ts +1 -3
  251. package/src/memory/embedding-ollama.ts +1 -3
  252. package/src/memory/embedding-openai.ts +1 -3
  253. package/src/memory/indexer.ts +114 -21
  254. package/src/memory/items-extractor.ts +42 -13
  255. package/src/memory/job-handlers/conversation-starters.ts +6 -1
  256. package/src/memory/job-handlers/embedding.test.ts +2 -4
  257. package/src/memory/job-handlers/embedding.ts +83 -0
  258. package/src/memory/job-utils.ts +1 -1
  259. package/src/memory/jobs-store.ts +6 -0
  260. package/src/memory/jobs-worker.ts +12 -0
  261. package/src/memory/llm-request-log-store.ts +100 -1
  262. package/src/memory/migrations/102-alter-table-columns.ts +5 -0
  263. package/src/memory/migrations/146-schedule-oneshot-routing.ts +3 -3
  264. package/src/memory/migrations/147-migrate-reminders-to-schedules.ts +66 -70
  265. package/src/memory/migrations/148-drop-reminders-table.ts +5 -9
  266. package/src/memory/migrations/160-drop-loopback-port-column.ts +1 -3
  267. package/src/memory/migrations/174-rename-thread-starters-table.ts +0 -7
  268. package/src/memory/migrations/178-oauth-providers-managed-service-config-key.ts +15 -0
  269. package/src/memory/migrations/179-llm-request-log-message-id.ts +16 -0
  270. package/src/memory/migrations/180-backfill-inline-attachments-to-disk.ts +66 -0
  271. package/src/memory/migrations/181-rename-thread-starters-checkpoints.ts +46 -0
  272. package/src/memory/migrations/182-oauth-providers-display-metadata.ts +20 -0
  273. package/src/memory/migrations/183-add-conversation-fork-lineage.ts +22 -0
  274. package/src/memory/migrations/184-llm-request-log-provider.ts +12 -0
  275. package/src/memory/migrations/185-memory-brief-state.ts +52 -0
  276. package/src/memory/migrations/186-memory-archive.ts +109 -0
  277. package/src/memory/migrations/187-memory-reducer-checkpoints.ts +19 -0
  278. package/src/memory/migrations/index.ts +10 -0
  279. package/src/memory/migrations/registry.ts +13 -0
  280. package/src/memory/qdrant-client.ts +23 -4
  281. package/src/memory/reducer-store.ts +271 -0
  282. package/src/memory/reducer-types.ts +99 -0
  283. package/src/memory/reducer.ts +453 -0
  284. package/src/memory/retriever.test.ts +601 -2
  285. package/src/memory/retriever.ts +85 -9
  286. package/src/memory/schema/conversations.ts +9 -0
  287. package/src/memory/schema/index.ts +2 -0
  288. package/src/memory/schema/infrastructure.ts +13 -7
  289. package/src/memory/schema/memory-archive.ts +121 -0
  290. package/src/memory/schema/memory-brief.ts +55 -0
  291. package/src/memory/schema/oauth.ts +6 -0
  292. package/src/memory/search/semantic.ts +17 -4
  293. package/src/messaging/providers/gmail/mime-builder.ts +3 -1
  294. package/src/notifications/copy-composer.ts +26 -0
  295. package/src/notifications/decision-engine.ts +14 -1
  296. package/src/notifications/emit-signal.ts +1 -1
  297. package/src/notifications/signal.ts +36 -0
  298. package/src/oauth/byo-connection.test.ts +1 -45
  299. package/src/oauth/byo-connection.ts +2 -8
  300. package/src/oauth/connect-orchestrator.ts +15 -11
  301. package/src/oauth/connection-resolver.test.ts +191 -0
  302. package/src/oauth/connection-resolver.ts +66 -38
  303. package/src/oauth/connection.ts +0 -1
  304. package/src/oauth/oauth-store.ts +99 -47
  305. package/src/oauth/platform-connection.test.ts +0 -1
  306. package/src/oauth/platform-connection.ts +11 -3
  307. package/src/oauth/seed-providers.ts +78 -3
  308. package/src/oauth/token-persistence.ts +16 -10
  309. package/src/permissions/checker.ts +160 -14
  310. package/src/permissions/defaults.ts +14 -0
  311. package/src/prompts/templates/BOOTSTRAP.md +2 -0
  312. package/src/providers/anthropic/client.ts +8 -1
  313. package/src/providers/failover.ts +4 -1
  314. package/src/providers/gemini/client.ts +50 -0
  315. package/src/providers/model-catalog.ts +92 -0
  316. package/src/providers/model-intents.ts +29 -20
  317. package/src/providers/openai/client.ts +49 -0
  318. package/src/providers/types.ts +2 -0
  319. package/src/runtime/access-request-helper.ts +16 -7
  320. package/src/runtime/auth/credential-service.ts +3 -1
  321. package/src/runtime/auth/route-policy.ts +14 -1
  322. package/src/runtime/btw-sidechain.ts +101 -0
  323. package/src/runtime/channel-reply-delivery.ts +17 -1
  324. package/src/runtime/http-router.ts +3 -1
  325. package/src/runtime/http-server.ts +196 -141
  326. package/src/runtime/http-types.ts +1 -0
  327. package/src/runtime/migrations/vbundle-builder.ts +5 -1
  328. package/src/runtime/routes/access-request-decision.ts +41 -0
  329. package/src/runtime/routes/app-management-routes.ts +6 -3
  330. package/src/runtime/routes/app-routes.ts +7 -3
  331. package/src/runtime/routes/approval-routes.ts +1 -0
  332. package/src/runtime/routes/approval-strategies/guardian-callback-strategy.ts +34 -2
  333. package/src/runtime/routes/attachment-routes.ts +45 -15
  334. package/src/runtime/routes/btw-routes.ts +21 -61
  335. package/src/runtime/routes/conversation-management-routes.ts +74 -0
  336. package/src/runtime/routes/conversation-query-routes.ts +187 -10
  337. package/src/runtime/routes/conversation-routes.ts +269 -28
  338. package/src/runtime/routes/conversation-starter-routes.ts +9 -11
  339. package/src/runtime/routes/diagnostics-routes.ts +1 -0
  340. package/src/runtime/routes/identity-routes.ts +2 -35
  341. package/src/runtime/routes/inbound-stages/acl-enforcement.ts +2 -2
  342. package/src/runtime/routes/llm-context-normalization.ts +1212 -0
  343. package/src/runtime/routes/log-export-routes.ts +3 -0
  344. package/src/runtime/routes/memory-item-routes.test.ts +34 -0
  345. package/src/runtime/routes/memory-item-routes.ts +94 -5
  346. package/src/runtime/routes/migration-routes.ts +4 -1
  347. package/src/runtime/routes/oauth-apps.ts +291 -0
  348. package/src/runtime/routes/secret-routes.ts +30 -1
  349. package/src/runtime/routes/settings-routes.ts +14 -0
  350. package/src/runtime/routes/surface-action-routes.ts +68 -1
  351. package/src/runtime/routes/trace-event-routes.ts +4 -1
  352. package/src/schedule/schedule-store.ts +30 -21
  353. package/src/security/secure-keys.ts +21 -0
  354. package/src/signals/bash.ts +1 -1
  355. package/src/skills/inline-command-expansions.ts +204 -0
  356. package/src/skills/inline-command-render.ts +127 -0
  357. package/src/skills/inline-command-runner.ts +242 -0
  358. package/src/skills/transitive-version-hash.ts +88 -0
  359. package/src/swarm/backend-claude-code.ts +3 -6
  360. package/src/tasks/task-store.ts +43 -1
  361. package/src/telemetry/usage-telemetry-reporter.test.ts +3 -2
  362. package/src/telemetry/usage-telemetry-reporter.ts +3 -1
  363. package/src/tools/AGENTS.md +6 -10
  364. package/src/tools/apps/executors.ts +17 -232
  365. package/src/tools/claude-code/claude-code.ts +2 -3
  366. package/src/tools/credentials/vault.ts +7 -12
  367. package/src/tools/host-filesystem/read.ts +13 -10
  368. package/src/tools/network/__tests__/web-search.test.ts +4 -2
  369. package/src/tools/permission-checker.ts +8 -1
  370. package/src/tools/schedule/list.ts +2 -7
  371. package/src/tools/schema-transforms.ts +5 -0
  372. package/src/tools/shared/filesystem/format-diff.ts +2 -7
  373. package/src/tools/skills/execute.ts +1 -1
  374. package/src/tools/skills/load.ts +140 -6
  375. package/src/tools/tool-manifest.ts +0 -6
  376. package/src/tools/ui-surface/definitions.ts +2 -2
  377. package/src/util/device-id.ts +28 -5
  378. package/src/util/platform.ts +24 -0
  379. package/src/util/pricing.ts +1 -0
  380. package/src/util/retry.ts +1 -3
  381. package/src/workspace/migrations/003-seed-device-id.ts +3 -4
  382. package/src/workspace/migrations/006-services-config.ts +5 -0
  383. package/src/workspace/migrations/008-voice-timeout-and-max-steps.ts +12 -0
  384. package/src/workspace/migrations/009-backfill-conversation-disk-view.ts +10 -0
  385. package/src/workspace/migrations/010-app-dir-rename.ts +223 -0
  386. package/src/workspace/migrations/{002-backfill-installation-id.ts → 011-backfill-installation-id.ts} +24 -13
  387. package/src/workspace/migrations/012-rename-conversation-disk-view-dirs.ts +64 -0
  388. package/src/workspace/migrations/013-repair-conversation-disk-view.ts +11 -0
  389. package/src/workspace/migrations/rebuild-conversation-disk-view.ts +186 -0
  390. package/src/workspace/migrations/registry.ts +11 -1
  391. package/src/workspace/top-level-renderer.ts +12 -0
  392. package/src/__tests__/asset-materialize-tool.test.ts +0 -523
  393. package/src/__tests__/asset-search-tool.test.ts +0 -536
  394. package/src/__tests__/fixtures/media-reuse-fixtures.ts +0 -56
  395. package/src/__tests__/media-reuse-story.e2e.test.ts +0 -762
  396. package/src/__tests__/media-visibility-policy.test.ts +0 -190
  397. package/src/config/bundled-skills/app-builder/tools/app-file-edit.ts +0 -14
  398. package/src/config/bundled-skills/app-builder/tools/app-file-list.ts +0 -13
  399. package/src/config/bundled-skills/app-builder/tools/app-file-read.ts +0 -21
  400. package/src/config/bundled-skills/app-builder/tools/app-file-write.ts +0 -14
  401. package/src/config/bundled-skills/app-builder/tools/app-list.ts +0 -13
  402. package/src/config/bundled-skills/app-builder/tools/app-update.ts +0 -23
  403. package/src/daemon/media-visibility-policy.ts +0 -59
  404. package/src/tools/assets/materialize.ts +0 -248
  405. package/src/tools/assets/search.ts +0 -400
@@ -0,0 +1,273 @@
1
+ import { mkdtempSync, rmSync } from "node:fs";
2
+ import { tmpdir } from "node:os";
3
+ import { join } from "node:path";
4
+ import { Database } from "bun:sqlite";
5
+ import {
6
+ afterAll,
7
+ afterEach,
8
+ beforeEach,
9
+ describe,
10
+ expect,
11
+ mock,
12
+ test,
13
+ } from "bun:test";
14
+
15
+ import { drizzle } from "drizzle-orm/bun-sqlite";
16
+
17
+ const testDir = mkdtempSync(join(tmpdir(), "memory-reducer-checkpoints-"));
18
+ const dbPath = join(testDir, "test.db");
19
+ const originalBunTest = process.env.BUN_TEST;
20
+
21
+ mock.module("../util/platform.js", () => ({
22
+ getDataDir: () => testDir,
23
+ isMacOS: () => process.platform === "darwin",
24
+ isLinux: () => process.platform === "linux",
25
+ isWindows: () => process.platform === "win32",
26
+ getPidPath: () => join(testDir, "test.pid"),
27
+ getDbPath: () => dbPath,
28
+ getLogPath: () => join(testDir, "test.log"),
29
+ ensureDataDir: () => {},
30
+ getConversationsDir: () => join(testDir, "conversations"),
31
+ }));
32
+
33
+ mock.module("../util/logger.js", () => ({
34
+ getLogger: () =>
35
+ new Proxy({} as Record<string, unknown>, {
36
+ get: () => () => {},
37
+ }),
38
+ }));
39
+
40
+ import { initializeDb, resetDb } from "../memory/db.js";
41
+ import { getSqliteFrom } from "../memory/db-connection.js";
42
+ import { migrateMemoryReducerCheckpoints } from "../memory/migrations/187-memory-reducer-checkpoints.js";
43
+ import * as schema from "../memory/schema.js";
44
+
45
+ function createTestDb() {
46
+ const sqlite = new Database(":memory:");
47
+ sqlite.exec("PRAGMA journal_mode=WAL");
48
+ sqlite.exec("PRAGMA foreign_keys = ON");
49
+ return drizzle(sqlite, { schema });
50
+ }
51
+
52
+ function getColumnInfo(
53
+ raw: Database,
54
+ ): Array<{ name: string; notnull: number }> {
55
+ return raw.query(`PRAGMA table_info(conversations)`).all() as Array<{
56
+ name: string;
57
+ notnull: number;
58
+ }>;
59
+ }
60
+
61
+ function bootstrapPreCheckpointConversations(raw: Database): void {
62
+ raw.exec(/*sql*/ `
63
+ CREATE TABLE conversations (
64
+ id TEXT PRIMARY KEY,
65
+ title TEXT,
66
+ created_at INTEGER NOT NULL,
67
+ updated_at INTEGER NOT NULL,
68
+ total_input_tokens INTEGER NOT NULL DEFAULT 0,
69
+ total_output_tokens INTEGER NOT NULL DEFAULT 0,
70
+ total_estimated_cost REAL NOT NULL DEFAULT 0,
71
+ context_summary TEXT,
72
+ context_compacted_message_count INTEGER NOT NULL DEFAULT 0,
73
+ context_compacted_at INTEGER,
74
+ conversation_type TEXT NOT NULL DEFAULT 'standard',
75
+ source TEXT NOT NULL DEFAULT 'user',
76
+ memory_scope_id TEXT NOT NULL DEFAULT 'default',
77
+ origin_channel TEXT,
78
+ origin_interface TEXT,
79
+ fork_parent_conversation_id TEXT,
80
+ fork_parent_message_id TEXT,
81
+ is_auto_title INTEGER NOT NULL DEFAULT 1,
82
+ schedule_job_id TEXT
83
+ )
84
+ `);
85
+ }
86
+
87
+ function removeTestDbFiles(): void {
88
+ rmSync(dbPath, { force: true });
89
+ rmSync(`${dbPath}-shm`, { force: true });
90
+ rmSync(`${dbPath}-wal`, { force: true });
91
+ }
92
+
93
+ describe("memory reducer checkpoint columns migration", () => {
94
+ beforeEach(() => {
95
+ process.env.BUN_TEST = "0";
96
+ resetDb();
97
+ removeTestDbFiles();
98
+ });
99
+
100
+ afterEach(() => {
101
+ resetDb();
102
+ removeTestDbFiles();
103
+ });
104
+
105
+ afterAll(() => {
106
+ if (originalBunTest === undefined) {
107
+ delete process.env.BUN_TEST;
108
+ } else {
109
+ process.env.BUN_TEST = originalBunTest;
110
+ }
111
+ resetDb();
112
+ removeTestDbFiles();
113
+ try {
114
+ rmSync(testDir, { recursive: true });
115
+ } catch {
116
+ /* best effort */
117
+ }
118
+ });
119
+
120
+ test("fresh DB initialization includes nullable reducer checkpoint columns", () => {
121
+ initializeDb();
122
+
123
+ const raw = new Database(dbPath);
124
+ const columns = getColumnInfo(raw);
125
+
126
+ const checkpointColumns = columns.filter(
127
+ (c) =>
128
+ c.name === "memory_reduced_through_message_id" ||
129
+ c.name === "memory_dirty_tail_since_message_id" ||
130
+ c.name === "memory_last_reduced_at",
131
+ );
132
+
133
+ expect(checkpointColumns).toHaveLength(3);
134
+ expect(checkpointColumns.every((c) => c.notnull === 0)).toBe(true);
135
+
136
+ raw.close();
137
+ });
138
+
139
+ test("migration upgrades the pre-checkpoint schema without disturbing existing rows", () => {
140
+ const db = createTestDb();
141
+ const raw = getSqliteFrom(db);
142
+ const now = Date.now();
143
+
144
+ bootstrapPreCheckpointConversations(raw);
145
+ raw.exec(/*sql*/ `
146
+ INSERT INTO conversations (
147
+ id,
148
+ title,
149
+ created_at,
150
+ updated_at,
151
+ conversation_type,
152
+ source,
153
+ memory_scope_id,
154
+ is_auto_title
155
+ ) VALUES (
156
+ 'conv-upgrade',
157
+ 'Existing conversation',
158
+ ${now},
159
+ ${now},
160
+ 'standard',
161
+ 'user',
162
+ 'default',
163
+ 1
164
+ )
165
+ `);
166
+
167
+ migrateMemoryReducerCheckpoints(db);
168
+
169
+ const columnNames = getColumnInfo(raw).map((c) => c.name);
170
+ expect(columnNames).toContain("memory_reduced_through_message_id");
171
+ expect(columnNames).toContain("memory_dirty_tail_since_message_id");
172
+ expect(columnNames).toContain("memory_last_reduced_at");
173
+
174
+ const row = raw
175
+ .query(
176
+ `SELECT id, title, memory_reduced_through_message_id, memory_dirty_tail_since_message_id, memory_last_reduced_at
177
+ FROM conversations WHERE id = 'conv-upgrade'`,
178
+ )
179
+ .get() as {
180
+ id: string;
181
+ title: string | null;
182
+ memory_reduced_through_message_id: string | null;
183
+ memory_dirty_tail_since_message_id: string | null;
184
+ memory_last_reduced_at: number | null;
185
+ } | null;
186
+
187
+ expect(row).toEqual({
188
+ id: "conv-upgrade",
189
+ title: "Existing conversation",
190
+ memory_reduced_through_message_id: null,
191
+ memory_dirty_tail_since_message_id: null,
192
+ memory_last_reduced_at: null,
193
+ });
194
+
195
+ raw.close();
196
+ });
197
+
198
+ test("re-running the migration preserves populated checkpoint values", () => {
199
+ const db = createTestDb();
200
+ const raw = getSqliteFrom(db);
201
+ const now = Date.now();
202
+
203
+ bootstrapPreCheckpointConversations(raw);
204
+ raw.exec(/*sql*/ `
205
+ INSERT INTO conversations (
206
+ id,
207
+ title,
208
+ created_at,
209
+ updated_at,
210
+ conversation_type,
211
+ source,
212
+ memory_scope_id,
213
+ is_auto_title
214
+ ) VALUES (
215
+ 'conv-rerun',
216
+ 'Reduced conversation',
217
+ ${now},
218
+ ${now},
219
+ 'standard',
220
+ 'user',
221
+ 'default',
222
+ 1
223
+ )
224
+ `);
225
+
226
+ migrateMemoryReducerCheckpoints(db);
227
+ raw.exec(/*sql*/ `
228
+ UPDATE conversations
229
+ SET memory_reduced_through_message_id = 'msg-100',
230
+ memory_dirty_tail_since_message_id = 'msg-101',
231
+ memory_last_reduced_at = ${now}
232
+ WHERE id = 'conv-rerun'
233
+ `);
234
+
235
+ expect(() => migrateMemoryReducerCheckpoints(db)).not.toThrow();
236
+
237
+ const row = raw
238
+ .query(
239
+ `SELECT memory_reduced_through_message_id, memory_dirty_tail_since_message_id, memory_last_reduced_at
240
+ FROM conversations WHERE id = 'conv-rerun'`,
241
+ )
242
+ .get() as {
243
+ memory_reduced_through_message_id: string | null;
244
+ memory_dirty_tail_since_message_id: string | null;
245
+ memory_last_reduced_at: number | null;
246
+ } | null;
247
+
248
+ expect(row).toEqual({
249
+ memory_reduced_through_message_id: "msg-100",
250
+ memory_dirty_tail_since_message_id: "msg-101",
251
+ memory_last_reduced_at: now,
252
+ });
253
+
254
+ raw.close();
255
+ });
256
+
257
+ test("getConversation exposes the new checkpoint fields as null for new rows", async () => {
258
+ initializeDb();
259
+
260
+ // Dynamic import to avoid circular module init issues — conversation-crud
261
+ // depends on getDb being initialized which happens in initializeDb above.
262
+ const { createConversation, getConversation } =
263
+ await import("../memory/conversation-crud.js");
264
+
265
+ const created = createConversation("Test conversation");
266
+ const loaded = getConversation(created.id);
267
+
268
+ expect(loaded).not.toBeNull();
269
+ expect(loaded!.memoryReducedThroughMessageId).toBeNull();
270
+ expect(loaded!.memoryDirtyTailSinceMessageId).toBeNull();
271
+ expect(loaded!.memoryLastReducedAt).toBeNull();
272
+ });
273
+ });
@@ -5,11 +5,13 @@
5
5
  * (specific ID → most recent assistant → any message → empty conversation).
6
6
  */
7
7
 
8
- import { mkdtempSync, rmSync } from "node:fs";
8
+ import { mkdtempSync, readFileSync, rmSync } from "node:fs";
9
9
  import { tmpdir } from "node:os";
10
10
  import { join } from "node:path";
11
11
  import { afterAll, beforeEach, describe, expect, mock, test } from "bun:test";
12
12
 
13
+ import JSZip from "jszip";
14
+
13
15
  const testDir = mkdtempSync(join(tmpdir(), "diagnostics-export-test-"));
14
16
 
15
17
  mock.module("../util/platform.js", () => ({
@@ -90,6 +92,27 @@ function seedMessage(
90
92
  );
91
93
  }
92
94
 
95
+ function seedLlmRequestLog(
96
+ id: string,
97
+ conversationId: string,
98
+ provider: string | null,
99
+ requestPayload: unknown,
100
+ responsePayload: unknown,
101
+ createdAt: number,
102
+ ): void {
103
+ db().run(
104
+ "INSERT INTO llm_request_logs (id, conversation_id, provider, request_payload, response_payload, created_at) VALUES (?, ?, ?, ?, ?, ?)",
105
+ [
106
+ id,
107
+ conversationId,
108
+ provider,
109
+ JSON.stringify(requestPayload),
110
+ JSON.stringify(responsePayload),
111
+ createdAt,
112
+ ],
113
+ );
114
+ }
115
+
93
116
  function seedConversationKey(
94
117
  conversationKey: string,
95
118
  conversationId: string,
@@ -102,6 +125,7 @@ function seedConversationKey(
102
125
 
103
126
  function cleanDb(): void {
104
127
  db().run("DELETE FROM messages");
128
+ db().run("DELETE FROM llm_request_logs");
105
129
  db().run("DELETE FROM conversation_keys");
106
130
  db().run("DELETE FROM conversations");
107
131
  }
@@ -216,4 +240,49 @@ describe("diagnostics export", () => {
216
240
  const json = (await res.json()) as { success: boolean };
217
241
  expect(json.success).toBe(true);
218
242
  });
243
+
244
+ test("preserves llm request provider identity in the exported JSONL", async () => {
245
+ const convId = "conv-7";
246
+ const now = Date.now();
247
+
248
+ seedConversation(convId);
249
+ seedMessage("msg-user-7", convId, "user", "hello", now - 1000);
250
+ seedMessage("msg-assistant-7", convId, "assistant", "world", now);
251
+ seedLlmRequestLog(
252
+ "log-7",
253
+ convId,
254
+ "openrouter",
255
+ { model: "openai/gpt-4.1-mini", input: "hello" },
256
+ { output: "world" },
257
+ now,
258
+ );
259
+
260
+ const res = await callExport({ conversationId: convId });
261
+ expect(res.status).toBe(200);
262
+ const json = (await res.json()) as { success: boolean; filePath: string };
263
+ expect(json.success).toBe(true);
264
+
265
+ const zip = await JSZip.loadAsync(readFileSync(json.filePath));
266
+ const llmRequests = zip.file("llm_requests.jsonl");
267
+ expect(llmRequests).not.toBeNull();
268
+
269
+ const lines = (await llmRequests!.async("string")).trim().split("\n");
270
+ expect(lines).toHaveLength(1);
271
+
272
+ const row = JSON.parse(lines[0]) as {
273
+ id: string;
274
+ conversationId: string;
275
+ provider?: string | null;
276
+ request: unknown;
277
+ response: unknown;
278
+ };
279
+
280
+ expect(row).toMatchObject({
281
+ id: "log-7",
282
+ conversationId: convId,
283
+ provider: "openrouter",
284
+ });
285
+
286
+ rmSync(json.filePath, { force: true });
287
+ });
219
288
  });
@@ -0,0 +1,80 @@
1
+ import { existsSync, mkdirSync, rmSync, writeFileSync } from "node:fs";
2
+ import { tmpdir } from "node:os";
3
+ import { join } from "node:path";
4
+ import { afterEach, beforeEach, describe, expect, it, mock } from "bun:test";
5
+
6
+ let tempDir: string;
7
+
8
+ mock.module("../util/platform.js", () => ({
9
+ getWorkspacePromptPath: mock((file: string) => join(tempDir, file)),
10
+ getWorkspaceDir: () => tempDir,
11
+ getRootDir: () => tempDir,
12
+ getDataDir: () => join(tempDir, "data"),
13
+ getPlatformName: () => "darwin",
14
+ isMacOS: () => false,
15
+ isLinux: () => false,
16
+ isWindows: () => false,
17
+ ensureDataDir: () => {},
18
+ getDbPath: () => "",
19
+ getLogPath: () => "",
20
+ getHistoryPath: () => "",
21
+ getHooksDir: () => "",
22
+ getSessionTokenPath: () => "",
23
+ getPlatformTokenPath: () => "",
24
+ getPidPath: () => "",
25
+ }));
26
+
27
+ const { isWakeUpGreeting, getCannedFirstGreeting, CANNED_FIRST_GREETING } =
28
+ await import("../daemon/first-greeting.js");
29
+
30
+ describe("first-greeting", () => {
31
+ beforeEach(() => {
32
+ tempDir = join(tmpdir(), `first-greeting-test-${Date.now()}`);
33
+ mkdirSync(tempDir, { recursive: true });
34
+ });
35
+
36
+ afterEach(() => {
37
+ rmSync(tempDir, { recursive: true, force: true });
38
+ });
39
+
40
+ describe("isWakeUpGreeting", () => {
41
+ it("returns true for wake-up greeting with 0 messages and BOOTSTRAP.md present", () => {
42
+ writeFileSync(join(tempDir, "BOOTSTRAP.md"), "bootstrap content");
43
+ expect(isWakeUpGreeting("Wake up, my friend.", 0)).toBe(true);
44
+ });
45
+
46
+ it("returns true for case variations", () => {
47
+ writeFileSync(join(tempDir, "BOOTSTRAP.md"), "bootstrap content");
48
+ expect(isWakeUpGreeting("wake up, my friend.", 0)).toBe(true);
49
+ expect(isWakeUpGreeting("WAKE UP, MY FRIEND.", 0)).toBe(true);
50
+ expect(isWakeUpGreeting("Wake Up, My Friend.", 0)).toBe(true);
51
+ });
52
+
53
+ it("returns false when content doesn't match wake-up greeting", () => {
54
+ writeFileSync(join(tempDir, "BOOTSTRAP.md"), "bootstrap content");
55
+ expect(isWakeUpGreeting("Hello", 0)).toBe(false);
56
+ expect(isWakeUpGreeting("Hey there", 0)).toBe(false);
57
+ expect(isWakeUpGreeting("Wake up", 0)).toBe(false);
58
+ });
59
+
60
+ it("returns false when conversationMessageCount > 0", () => {
61
+ writeFileSync(join(tempDir, "BOOTSTRAP.md"), "bootstrap content");
62
+ expect(isWakeUpGreeting("Wake up, my friend.", 1)).toBe(false);
63
+ expect(isWakeUpGreeting("Wake up, my friend.", 5)).toBe(false);
64
+ });
65
+
66
+ it("returns false when BOOTSTRAP.md doesn't exist", () => {
67
+ expect(existsSync(join(tempDir, "BOOTSTRAP.md"))).toBe(false);
68
+ expect(isWakeUpGreeting("Wake up, my friend.", 0)).toBe(false);
69
+ });
70
+ });
71
+
72
+ describe("getCannedFirstGreeting", () => {
73
+ it("returns the expected greeting string", () => {
74
+ const greeting = getCannedFirstGreeting();
75
+ expect(greeting).toBe(CANNED_FIRST_GREETING);
76
+ expect(greeting).toContain("brand new");
77
+ expect(greeting).toContain("no name, no memories");
78
+ });
79
+ });
80
+ });
@@ -24,6 +24,7 @@ const ALLOWLIST = new Set([
24
24
  // --- Intentional local daemon-control paths ---
25
25
  "assistant/src/cli/commands/conversations.ts", // CLI wipe talks to runtime directly
26
26
  "clients/shared/Network/DaemonClient.swift",
27
+ "clients/shared/App/Auth/PlatformOAuthService.swift", // comment explaining runtimeUrl vs platformUrl
27
28
  "clients/macos/vellum-assistant/App/AppDelegate.swift",
28
29
  "clients/macos/vellum-assistant/Features/Settings/SettingsConnectTab.swift",
29
30
  ".claude/skills/update/SKILL.md", // daemon health check script
@@ -268,13 +268,9 @@ describe("handleConfirmationResponse canonical status sync", () => {
268
268
  undefined,
269
269
  { source: "button" },
270
270
  ]);
271
- expect(resolveCanonicalGuardianRequestMock).toHaveBeenCalledWith(
272
- "req-confirm-allow",
273
- "pending",
274
- {
275
- status: "approved",
276
- },
277
- );
271
+ // Canonical status sync is now handled inside Conversation.handleConfirmationResponse,
272
+ // which this test mocks out — so the handler itself no longer calls resolveCanonicalGuardianRequest.
273
+ expect(resolveCanonicalGuardianRequestMock).not.toHaveBeenCalled();
278
274
  expect(resolveMock).toHaveBeenCalledWith("req-confirm-allow");
279
275
  });
280
276
  });
@@ -491,12 +491,17 @@ describe("repairHistory", () => {
491
491
  expect(assistantMsg.content[1]).toMatchObject({
492
492
  type: "web_search_tool_result",
493
493
  tool_use_id: "stu_1",
494
- content: { type: "web_search_tool_result_error", error_code: "unavailable" },
494
+ content: {
495
+ type: "web_search_tool_result_error",
496
+ error_code: "unavailable",
497
+ },
495
498
  });
496
499
 
497
500
  // User message has no web_search_tool_result
498
501
  const userMsg = repaired[2];
499
- expect(userMsg.content.every((b) => b.type !== "web_search_tool_result")).toBe(true);
502
+ expect(
503
+ userMsg.content.every((b) => b.type !== "web_search_tool_result"),
504
+ ).toBe(true);
500
505
  });
501
506
 
502
507
  test("migrates legacy web_search_tool_result from user message to assistant message", () => {
@@ -527,7 +532,9 @@ describe("repairHistory", () => {
527
532
  {
528
533
  type: "web_search_tool_result",
529
534
  tool_use_id: "srvtoolu_abc",
530
- content: [{ type: "web_search_result", url: "https://example.com" }],
535
+ content: [
536
+ { type: "web_search_result", url: "https://example.com" },
537
+ ],
531
538
  },
532
539
  { type: "tool_result", tool_use_id: "tu_1", content: "files" },
533
540
  ],
@@ -545,8 +552,12 @@ describe("repairHistory", () => {
545
552
 
546
553
  // The assistant message now has the server pair + client tool_use
547
554
  const assistantMsg = repaired[1];
548
- const serverToolUse = assistantMsg.content.find((b) => b.type === "server_tool_use");
549
- const webSearchResult = assistantMsg.content.find((b) => b.type === "web_search_tool_result");
555
+ const serverToolUse = assistantMsg.content.find(
556
+ (b) => b.type === "server_tool_use",
557
+ );
558
+ const webSearchResult = assistantMsg.content.find(
559
+ (b) => b.type === "web_search_tool_result",
560
+ );
550
561
  expect(serverToolUse).toBeDefined();
551
562
  expect(webSearchResult).toBeDefined();
552
563
 
@@ -554,7 +565,9 @@ describe("repairHistory", () => {
554
565
  const userMsg = repaired[2];
555
566
  expect(stats.orphanToolResultsDowngraded).toBe(1);
556
567
  expect(userMsg.content.some((b) => b.type === "tool_result")).toBe(true);
557
- expect(userMsg.content.every((b) => b.type !== "web_search_tool_result")).toBe(true);
568
+ expect(
569
+ userMsg.content.every((b) => b.type !== "web_search_tool_result"),
570
+ ).toBe(true);
558
571
  });
559
572
 
560
573
  test("trailing server_tool_use gets synthetic result in same assistant message", () => {
@@ -584,7 +597,10 @@ describe("repairHistory", () => {
584
597
  expect(repaired[1].content[1]).toMatchObject({
585
598
  type: "web_search_tool_result",
586
599
  tool_use_id: "stu_1",
587
- content: { type: "web_search_tool_result_error", error_code: "unavailable" },
600
+ content: {
601
+ type: "web_search_tool_result_error",
602
+ error_code: "unavailable",
603
+ },
588
604
  });
589
605
  });
590
606
 
@@ -696,11 +712,15 @@ describe("repairHistory", () => {
696
712
 
697
713
  // Assistant message has the server pair
698
714
  const assistantMsg = repaired[1];
699
- expect(assistantMsg.content.some((b) => b.type === "web_search_tool_result")).toBe(true);
715
+ expect(
716
+ assistantMsg.content.some((b) => b.type === "web_search_tool_result"),
717
+ ).toBe(true);
700
718
 
701
719
  // User message has no web_search_tool_result — the tool_result was downgraded to text
702
720
  const userMsg = repaired[2];
703
- expect(userMsg.content.every((b) => b.type !== "web_search_tool_result")).toBe(true);
721
+ expect(
722
+ userMsg.content.every((b) => b.type !== "web_search_tool_result"),
723
+ ).toBe(true);
704
724
  expect(userMsg.content.every((b) => b.type !== "tool_result")).toBe(true);
705
725
  });
706
726
 
@@ -720,7 +740,9 @@ describe("repairHistory", () => {
720
740
  {
721
741
  type: "web_search_tool_result",
722
742
  tool_use_id: "tu_1",
723
- content: [{ type: "web_search_result", url: "https://example.com" }],
743
+ content: [
744
+ { type: "web_search_result", url: "https://example.com" },
745
+ ],
724
746
  },
725
747
  ],
726
748
  },