@alumbwe/anvil 1.0.20 → 1.0.21

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 (328) hide show
  1. package/package.json +6 -2
  2. package/src/agents/bundled-agents.generated.ts +4 -4
  3. package/src/chat.tsx +1703 -1698
  4. package/src/cli-args.ts +134 -134
  5. package/src/components/status-bar.tsx +331 -313
  6. package/src/hooks/helpers/send-message.ts +663 -654
  7. package/src/hooks/use-send-message.ts +806 -773
  8. package/src/index.tsx +473 -473
  9. package/src/state/chat-store.ts +553 -544
  10. package/src/utils/create-run-config.ts +121 -121
  11. package/src/utils/format-compact-number.ts +37 -0
  12. package/vendor/@anvil/common/package.json +27 -0
  13. package/vendor/@anvil/common/src/__tests__/agent-validation.test.ts +846 -0
  14. package/vendor/@anvil/common/src/__tests__/anvil-model-availability.test.ts +101 -0
  15. package/vendor/@anvil/common/src/__tests__/anvil-models.test.ts +1408 -0
  16. package/vendor/@anvil/common/src/__tests__/anvil-public-data-use-copy.test.ts +258 -0
  17. package/vendor/@anvil/common/src/__tests__/anvil-referral-tiers.test.ts +48 -0
  18. package/vendor/@anvil/common/src/__tests__/anvil-spend-ceilings.test.ts +282 -0
  19. package/vendor/@anvil/common/src/__tests__/anvil-trust.test.ts +526 -0
  20. package/vendor/@anvil/common/src/__tests__/deepseek-direct.test.ts +68 -0
  21. package/vendor/@anvil/common/src/__tests__/disposable-email.test.ts +132 -0
  22. package/vendor/@anvil/common/src/__tests__/dynamic-agent-template-schema.test.ts +422 -0
  23. package/vendor/@anvil/common/src/__tests__/env-ci.test.ts +167 -0
  24. package/vendor/@anvil/common/src/__tests__/env-process.test.ts +145 -0
  25. package/vendor/@anvil/common/src/__tests__/foreign-client-shipped-agents.test.ts +234 -0
  26. package/vendor/@anvil/common/src/__tests__/foreign-client-signals.test.ts +379 -0
  27. package/vendor/@anvil/common/src/__tests__/free-agents.test.ts +807 -0
  28. package/vendor/@anvil/common/src/__tests__/gravity-capi.test.ts +154 -0
  29. package/vendor/@anvil/common/src/__tests__/handlesteps-parsing.test.ts +246 -0
  30. package/vendor/@anvil/common/src/__tests__/kimi-k3-god-only.test.ts +77 -0
  31. package/vendor/@anvil/common/src/__tests__/model-config.test.ts +89 -0
  32. package/vendor/@anvil/common/src/__tests__/project-file-tree.test.ts +298 -0
  33. package/vendor/@anvil/common/src/__tests__/provisioned-model-tiers.test.ts +116 -0
  34. package/vendor/@anvil/common/src/__tests__/reasoning-effort.test.ts +167 -0
  35. package/vendor/@anvil/common/src/__tests__/reddit-capi.test.ts +132 -0
  36. package/vendor/@anvil/common/src/__tests__/response-ad-positions.test.ts +72 -0
  37. package/vendor/@anvil/common/src/__tests__/user-state.test.ts +30 -0
  38. package/vendor/@anvil/common/src/actions.ts +215 -0
  39. package/vendor/@anvil/common/src/analytics-core.ts +69 -0
  40. package/vendor/@anvil/common/src/analytics.ts +93 -0
  41. package/vendor/@anvil/common/src/api-keys/constants.ts +26 -0
  42. package/vendor/@anvil/common/src/browser-actions.ts +413 -0
  43. package/vendor/@anvil/common/src/constants/__tests__/anvil-onboarding-gate.test.ts +63 -0
  44. package/vendor/@anvil/common/src/constants/__tests__/anvil-onboarding.test.ts +246 -0
  45. package/vendor/@anvil/common/src/constants/__tests__/cf-worker-signals.test.ts +106 -0
  46. package/vendor/@anvil/common/src/constants/agents.ts +99 -0
  47. package/vendor/@anvil/common/src/constants/analytics-events.ts +435 -0
  48. package/vendor/@anvil/common/src/constants/anthropic.ts +73 -0
  49. package/vendor/@anvil/common/src/constants/anvil-data-use.ts +81 -0
  50. package/vendor/@anvil/common/src/constants/anvil-errors.ts +7 -0
  51. package/vendor/@anvil/common/src/constants/anvil-gemini-thinker.ts +21 -0
  52. package/vendor/@anvil/common/src/constants/anvil-model-ids.ts +11 -0
  53. package/vendor/@anvil/common/src/constants/anvil-models.ts +2130 -0
  54. package/vendor/@anvil/common/src/constants/anvil-onboarding-gate.ts +55 -0
  55. package/vendor/@anvil/common/src/constants/anvil-onboarding.ts +307 -0
  56. package/vendor/@anvil/common/src/constants/anvil-referral-tiers.ts +87 -0
  57. package/vendor/@anvil/common/src/constants/anvil-signup-block.ts +68 -0
  58. package/vendor/@anvil/common/src/constants/anvil-spend-ceilings.ts +450 -0
  59. package/vendor/@anvil/common/src/constants/anvil-trust.ts +976 -0
  60. package/vendor/@anvil/common/src/constants/auth.ts +18 -0
  61. package/vendor/@anvil/common/src/constants/byok.ts +2 -0
  62. package/vendor/@anvil/common/src/constants/cf-worker-signals.ts +132 -0
  63. package/vendor/@anvil/common/src/constants/composio.ts +34 -0
  64. package/vendor/@anvil/common/src/constants/deepseek-direct.ts +55 -0
  65. package/vendor/@anvil/common/src/constants/feedback.ts +13 -0
  66. package/vendor/@anvil/common/src/constants/foreign-client-signals.ts +272 -0
  67. package/vendor/@anvil/common/src/constants/free-agents.ts +777 -0
  68. package/vendor/@anvil/common/src/constants/gemini.ts +15 -0
  69. package/vendor/@anvil/common/src/constants/grant-priorities.ts +13 -0
  70. package/vendor/@anvil/common/src/constants/hosts.ts +6 -0
  71. package/vendor/@anvil/common/src/constants/images.ts +51 -0
  72. package/vendor/@anvil/common/src/constants/index.ts +7 -0
  73. package/vendor/@anvil/common/src/constants/knowledge.ts +35 -0
  74. package/vendor/@anvil/common/src/constants/limits.ts +23 -0
  75. package/vendor/@anvil/common/src/constants/model-config.ts +277 -0
  76. package/vendor/@anvil/common/src/constants/openrouter-attribution.ts +8 -0
  77. package/vendor/@anvil/common/src/constants/paths.ts +69 -0
  78. package/vendor/@anvil/common/src/constants/provider-routes.ts +317 -0
  79. package/vendor/@anvil/common/src/constants/reasoning-effort.ts +79 -0
  80. package/vendor/@anvil/common/src/constants/skills.ts +60 -0
  81. package/vendor/@anvil/common/src/constants/subscription-plans.ts +49 -0
  82. package/vendor/@anvil/common/src/constants/ui.ts +25 -0
  83. package/vendor/@anvil/common/src/env-ci.ts +36 -0
  84. package/vendor/@anvil/common/src/env-process.ts +95 -0
  85. package/vendor/@anvil/common/src/env-schema.ts +95 -0
  86. package/vendor/@anvil/common/src/env.ts +24 -0
  87. package/vendor/@anvil/common/src/gravity-capi.ts +207 -0
  88. package/vendor/@anvil/common/src/mcp/client.ts +233 -0
  89. package/vendor/@anvil/common/src/old-constants.ts +10 -0
  90. package/vendor/@anvil/common/src/project-file-tree.ts +355 -0
  91. package/vendor/@anvil/common/src/reddit-capi.ts +254 -0
  92. package/vendor/@anvil/common/src/schemas/feedback.ts +52 -0
  93. package/vendor/@anvil/common/src/schemas/logs.ts +66 -0
  94. package/vendor/@anvil/common/src/templates/agent-validation.ts +392 -0
  95. package/vendor/@anvil/common/src/templates/initial-agents-dir/LICENSE +202 -0
  96. package/vendor/@anvil/common/src/templates/initial-agents-dir/README.md +294 -0
  97. package/vendor/@anvil/common/src/templates/initial-agents-dir/examples/01-basic-diff-reviewer.ts +17 -0
  98. package/vendor/@anvil/common/src/templates/initial-agents-dir/examples/02-intermediate-git-committer.ts +78 -0
  99. package/vendor/@anvil/common/src/templates/initial-agents-dir/examples/03-advanced-file-explorer.ts +73 -0
  100. package/vendor/@anvil/common/src/templates/initial-agents-dir/my-custom-agent.ts +40 -0
  101. package/vendor/@anvil/common/src/templates/initial-agents-dir/package.json +6 -0
  102. package/vendor/@anvil/common/src/templates/initial-agents-dir/skills/README.md +65 -0
  103. package/vendor/@anvil/common/src/templates/initial-agents-dir/skills/example-skill/SKILL.md +29 -0
  104. package/vendor/@anvil/common/src/templates/initial-agents-dir/types/agent-definition.ts +497 -0
  105. package/vendor/@anvil/common/src/templates/initial-agents-dir/types/tools.ts +444 -0
  106. package/vendor/@anvil/common/src/templates/initial-agents-dir/types/util-types.ts +178 -0
  107. package/vendor/@anvil/common/src/testing/TESTING_PATTERNS.md +351 -0
  108. package/vendor/@anvil/common/src/testing/anvil-offer-invariants.ts +169 -0
  109. package/vendor/@anvil/common/src/testing/errors.ts +33 -0
  110. package/vendor/@anvil/common/src/testing/fixtures/agent-runtime.ts +334 -0
  111. package/vendor/@anvil/common/src/testing/impl/agent-runtime.ts +6 -0
  112. package/vendor/@anvil/common/src/testing/index.ts +84 -0
  113. package/vendor/@anvil/common/src/testing/mock-modules.ts +53 -0
  114. package/vendor/@anvil/common/src/testing/mock-types.ts +123 -0
  115. package/vendor/@anvil/common/src/testing/mocks/analytics.ts +261 -0
  116. package/vendor/@anvil/common/src/testing/mocks/child-process.ts +93 -0
  117. package/vendor/@anvil/common/src/testing/mocks/crypto.ts +218 -0
  118. package/vendor/@anvil/common/src/testing/mocks/database.ts +337 -0
  119. package/vendor/@anvil/common/src/testing/mocks/fetch.ts +219 -0
  120. package/vendor/@anvil/common/src/testing/mocks/filesystem.ts +166 -0
  121. package/vendor/@anvil/common/src/testing/mocks/index.ts +101 -0
  122. package/vendor/@anvil/common/src/testing/mocks/logger.ts +135 -0
  123. package/vendor/@anvil/common/src/testing/mocks/stream.ts +313 -0
  124. package/vendor/@anvil/common/src/testing/mocks/timers.ts +132 -0
  125. package/vendor/@anvil/common/src/testing/mocks/tree-sitter.ts +127 -0
  126. package/vendor/@anvil/common/src/testing/setup.ts +282 -0
  127. package/vendor/@anvil/common/src/testing-env-ci.ts +15 -0
  128. package/vendor/@anvil/common/src/testing-env-process.ts +78 -0
  129. package/vendor/@anvil/common/src/tools/__tests__/compile-tool-definitions.test.ts +34 -0
  130. package/vendor/@anvil/common/src/tools/compile-tool-definitions.ts +157 -0
  131. package/vendor/@anvil/common/src/tools/constants.ts +117 -0
  132. package/vendor/@anvil/common/src/tools/list.ts +190 -0
  133. package/vendor/@anvil/common/src/tools/params/__tests__/coerce-to-array.test.ts +213 -0
  134. package/vendor/@anvil/common/src/tools/params/__tests__/read-docs.test.ts +47 -0
  135. package/vendor/@anvil/common/src/tools/params/__tests__/render-ui.test.ts +65 -0
  136. package/vendor/@anvil/common/src/tools/params/tool/__tests__/run-terminal-command-timeout.test.ts +49 -0
  137. package/vendor/@anvil/common/src/tools/params/tool/add-message.ts +39 -0
  138. package/vendor/@anvil/common/src/tools/params/tool/add-subgoal.ts +57 -0
  139. package/vendor/@anvil/common/src/tools/params/tool/apply-patch.ts +110 -0
  140. package/vendor/@anvil/common/src/tools/params/tool/ask-user.ts +181 -0
  141. package/vendor/@anvil/common/src/tools/params/tool/browser-logs.ts +85 -0
  142. package/vendor/@anvil/common/src/tools/params/tool/cloud-plan-ready.ts +94 -0
  143. package/vendor/@anvil/common/src/tools/params/tool/code-search.ts +159 -0
  144. package/vendor/@anvil/common/src/tools/params/tool/composio.ts +131 -0
  145. package/vendor/@anvil/common/src/tools/params/tool/create-plan.ts +80 -0
  146. package/vendor/@anvil/common/src/tools/params/tool/end-turn.ts +57 -0
  147. package/vendor/@anvil/common/src/tools/params/tool/find-files.ts +60 -0
  148. package/vendor/@anvil/common/src/tools/params/tool/glob.ts +80 -0
  149. package/vendor/@anvil/common/src/tools/params/tool/gravity-index.ts +93 -0
  150. package/vendor/@anvil/common/src/tools/params/tool/list-directory.ts +58 -0
  151. package/vendor/@anvil/common/src/tools/params/tool/lookup-agent-info.ts +37 -0
  152. package/vendor/@anvil/common/src/tools/params/tool/propose-str-replace.ts +103 -0
  153. package/vendor/@anvil/common/src/tools/params/tool/propose-write-file.ts +71 -0
  154. package/vendor/@anvil/common/src/tools/params/tool/read-docs.ts +90 -0
  155. package/vendor/@anvil/common/src/tools/params/tool/read-files.ts +109 -0
  156. package/vendor/@anvil/common/src/tools/params/tool/read-subtree.ts +79 -0
  157. package/vendor/@anvil/common/src/tools/params/tool/read-url.ts +81 -0
  158. package/vendor/@anvil/common/src/tools/params/tool/render-ui.ts +168 -0
  159. package/vendor/@anvil/common/src/tools/params/tool/run-file-change-hooks.ts +57 -0
  160. package/vendor/@anvil/common/src/tools/params/tool/run-terminal-command.ts +211 -0
  161. package/vendor/@anvil/common/src/tools/params/tool/screenshot.ts +97 -0
  162. package/vendor/@anvil/common/src/tools/params/tool/set-messages.ts +44 -0
  163. package/vendor/@anvil/common/src/tools/params/tool/set-output.ts +61 -0
  164. package/vendor/@anvil/common/src/tools/params/tool/skill.ts +59 -0
  165. package/vendor/@anvil/common/src/tools/params/tool/spawn-agent-inline.ts +56 -0
  166. package/vendor/@anvil/common/src/tools/params/tool/spawn-agents.ts +154 -0
  167. package/vendor/@anvil/common/src/tools/params/tool/str-replace.ts +107 -0
  168. package/vendor/@anvil/common/src/tools/params/tool/suggest-followups.ts +97 -0
  169. package/vendor/@anvil/common/src/tools/params/tool/task-completed.ts +61 -0
  170. package/vendor/@anvil/common/src/tools/params/tool/think-deeply.ts +56 -0
  171. package/vendor/@anvil/common/src/tools/params/tool/update-subgoal.ts +89 -0
  172. package/vendor/@anvil/common/src/tools/params/tool/vision-analyze.ts +82 -0
  173. package/vendor/@anvil/common/src/tools/params/tool/web-search.ts +73 -0
  174. package/vendor/@anvil/common/src/tools/params/tool/write-file.ts +71 -0
  175. package/vendor/@anvil/common/src/tools/params/tool/write-todos.ts +67 -0
  176. package/vendor/@anvil/common/src/tools/params/utils.ts +150 -0
  177. package/vendor/@anvil/common/src/tools/utils.ts +24 -0
  178. package/vendor/@anvil/common/src/types/__tests__/dynamic-agent-template.test.ts +20 -0
  179. package/vendor/@anvil/common/src/types/agent-template.ts +215 -0
  180. package/vendor/@anvil/common/src/types/anvil-session.ts +535 -0
  181. package/vendor/@anvil/common/src/types/anvil-streak.ts +6 -0
  182. package/vendor/@anvil/common/src/types/anvil-usage.ts +64 -0
  183. package/vendor/@anvil/common/src/types/api/agents/publish.ts +61 -0
  184. package/vendor/@anvil/common/src/types/bun-test.d.ts +5 -0
  185. package/vendor/@anvil/common/src/types/contracts/agent-runtime.ts +75 -0
  186. package/vendor/@anvil/common/src/types/contracts/analytics.ts +9 -0
  187. package/vendor/@anvil/common/src/types/contracts/bigquery.ts +55 -0
  188. package/vendor/@anvil/common/src/types/contracts/billing.ts +46 -0
  189. package/vendor/@anvil/common/src/types/contracts/client.ts +53 -0
  190. package/vendor/@anvil/common/src/types/contracts/database.ts +112 -0
  191. package/vendor/@anvil/common/src/types/contracts/env.ts +203 -0
  192. package/vendor/@anvil/common/src/types/contracts/llm.ts +191 -0
  193. package/vendor/@anvil/common/src/types/contracts/logger.ts +14 -0
  194. package/vendor/@anvil/common/src/types/contracts/logs.ts +34 -0
  195. package/vendor/@anvil/common/src/types/contracts/trace.ts +21 -0
  196. package/vendor/@anvil/common/src/types/dynamic-agent-template.ts +328 -0
  197. package/vendor/@anvil/common/src/types/filesystem.ts +10 -0
  198. package/vendor/@anvil/common/src/types/function-params.ts +33 -0
  199. package/vendor/@anvil/common/src/types/grant.ts +20 -0
  200. package/vendor/@anvil/common/src/types/gravity-index.ts +170 -0
  201. package/vendor/@anvil/common/src/types/json.ts +29 -0
  202. package/vendor/@anvil/common/src/types/mcp.ts +24 -0
  203. package/vendor/@anvil/common/src/types/messages/anvil-message.ts +58 -0
  204. package/vendor/@anvil/common/src/types/messages/content-part.ts +59 -0
  205. package/vendor/@anvil/common/src/types/messages/data-content.ts +14 -0
  206. package/vendor/@anvil/common/src/types/messages/provider-metadata.ts +13 -0
  207. package/vendor/@anvil/common/src/types/organization.ts +118 -0
  208. package/vendor/@anvil/common/src/types/print-mode.ts +121 -0
  209. package/vendor/@anvil/common/src/types/publisher.ts +67 -0
  210. package/vendor/@anvil/common/src/types/session-state.ts +149 -0
  211. package/vendor/@anvil/common/src/types/skill.ts +86 -0
  212. package/vendor/@anvil/common/src/types/source.ts +11 -0
  213. package/vendor/@anvil/common/src/types/spawn.ts +13 -0
  214. package/vendor/@anvil/common/src/types/subscription.ts +67 -0
  215. package/vendor/@anvil/common/src/types/usage.ts +16 -0
  216. package/vendor/@anvil/common/src/types/util.ts +3 -0
  217. package/vendor/@anvil/common/src/util/__tests__/ad-user-agent.test.ts +26 -0
  218. package/vendor/@anvil/common/src/util/__tests__/analytics-dispatcher.test.ts +122 -0
  219. package/vendor/@anvil/common/src/util/__tests__/analytics-log.test.ts +102 -0
  220. package/vendor/@anvil/common/src/util/__tests__/analytics-sampling.test.ts +143 -0
  221. package/vendor/@anvil/common/src/util/__tests__/anvil-streak.test.ts +202 -0
  222. package/vendor/@anvil/common/src/util/__tests__/anvil-usage-summary.test.ts +193 -0
  223. package/vendor/@anvil/common/src/util/__tests__/axiom-only-log.test.ts +140 -0
  224. package/vendor/@anvil/common/src/util/__tests__/client-user-agent.test.ts +148 -0
  225. package/vendor/@anvil/common/src/util/__tests__/engagement-tracker.test.ts +115 -0
  226. package/vendor/@anvil/common/src/util/__tests__/env-file-path.test.ts +57 -0
  227. package/vendor/@anvil/common/src/util/__tests__/error-abort.test.ts +774 -0
  228. package/vendor/@anvil/common/src/util/__tests__/error-api-details.test.ts +206 -0
  229. package/vendor/@anvil/common/src/util/__tests__/file-read-limits.test.ts +173 -0
  230. package/vendor/@anvil/common/src/util/__tests__/format-code-search.test.ts +60 -0
  231. package/vendor/@anvil/common/src/util/__tests__/log-mirror.test.ts +31 -0
  232. package/vendor/@anvil/common/src/util/__tests__/messages.test.ts +1252 -0
  233. package/vendor/@anvil/common/src/util/__tests__/partial-json-delta.test.ts +505 -0
  234. package/vendor/@anvil/common/src/util/__tests__/path.test.ts +22 -0
  235. package/vendor/@anvil/common/src/util/__tests__/project-ignore.test.ts +90 -0
  236. package/vendor/@anvil/common/src/util/__tests__/promise.test.ts +325 -0
  237. package/vendor/@anvil/common/src/util/__tests__/rate-limit.test.ts +28 -0
  238. package/vendor/@anvil/common/src/util/__tests__/reddit-anvil-retention.test.ts +130 -0
  239. package/vendor/@anvil/common/src/util/__tests__/saxy.test.ts +1008 -0
  240. package/vendor/@anvil/common/src/util/__tests__/split-data.test.ts +289 -0
  241. package/vendor/@anvil/common/src/util/__tests__/string.test.ts +239 -0
  242. package/vendor/@anvil/common/src/util/__tests__/thread-title.test.ts +70 -0
  243. package/vendor/@anvil/common/src/util/__tests__/tool-result-media-order.test.ts +108 -0
  244. package/vendor/@anvil/common/src/util/__tests__/ttft-histogram.test.ts +180 -0
  245. package/vendor/@anvil/common/src/util/__tests__/with-timeout.test.ts +24 -0
  246. package/vendor/@anvil/common/src/util/__tests__/zoned-time.test.ts +88 -0
  247. package/vendor/@anvil/common/src/util/ad-user-agent.ts +25 -0
  248. package/vendor/@anvil/common/src/util/agent-file-utils.ts +110 -0
  249. package/vendor/@anvil/common/src/util/agent-id-parsing.ts +136 -0
  250. package/vendor/@anvil/common/src/util/agent-name-normalization.ts +38 -0
  251. package/vendor/@anvil/common/src/util/agent-name-resolver.ts +86 -0
  252. package/vendor/@anvil/common/src/util/analytics-dispatcher.ts +82 -0
  253. package/vendor/@anvil/common/src/util/analytics-log.ts +78 -0
  254. package/vendor/@anvil/common/src/util/analytics-sampling.ts +255 -0
  255. package/vendor/@anvil/common/src/util/anvil-model-availability.ts +102 -0
  256. package/vendor/@anvil/common/src/util/anvil-privacy.ts +82 -0
  257. package/vendor/@anvil/common/src/util/anvil-streak-line.ts +103 -0
  258. package/vendor/@anvil/common/src/util/anvil-streak.ts +156 -0
  259. package/vendor/@anvil/common/src/util/anvil-usage-summary.ts +125 -0
  260. package/vendor/@anvil/common/src/util/array.ts +31 -0
  261. package/vendor/@anvil/common/src/util/axiom-only-log.ts +124 -0
  262. package/vendor/@anvil/common/src/util/cache-debug.ts +171 -0
  263. package/vendor/@anvil/common/src/util/client-user-agent.ts +114 -0
  264. package/vendor/@anvil/common/src/util/credentials.ts +26 -0
  265. package/vendor/@anvil/common/src/util/currency.ts +25 -0
  266. package/vendor/@anvil/common/src/util/dates.ts +81 -0
  267. package/vendor/@anvil/common/src/util/disposable-email.ts +277 -0
  268. package/vendor/@anvil/common/src/util/engagement-tracker.ts +129 -0
  269. package/vendor/@anvil/common/src/util/env-file-path.ts +40 -0
  270. package/vendor/@anvil/common/src/util/error.ts +562 -0
  271. package/vendor/@anvil/common/src/util/file-read-limits.ts +236 -0
  272. package/vendor/@anvil/common/src/util/file.ts +339 -0
  273. package/vendor/@anvil/common/src/util/format-code-search.ts +115 -0
  274. package/vendor/@anvil/common/src/util/lazy-response-ads.ts +92 -0
  275. package/vendor/@anvil/common/src/util/log-data.ts +57 -0
  276. package/vendor/@anvil/common/src/util/log-ingest.ts +58 -0
  277. package/vendor/@anvil/common/src/util/log-mirror.ts +34 -0
  278. package/vendor/@anvil/common/src/util/lru-cache.ts +67 -0
  279. package/vendor/@anvil/common/src/util/messages.ts +685 -0
  280. package/vendor/@anvil/common/src/util/min-heap.ts +87 -0
  281. package/vendor/@anvil/common/src/util/model-utils.ts +4 -0
  282. package/vendor/@anvil/common/src/util/object.ts +128 -0
  283. package/vendor/@anvil/common/src/util/partial-json-delta.ts +89 -0
  284. package/vendor/@anvil/common/src/util/path.ts +15 -0
  285. package/vendor/@anvil/common/src/util/project-ignore.ts +119 -0
  286. package/vendor/@anvil/common/src/util/promise.ts +73 -0
  287. package/vendor/@anvil/common/src/util/random.ts +15 -0
  288. package/vendor/@anvil/common/src/util/rate-limit.ts +56 -0
  289. package/vendor/@anvil/common/src/util/reddit-anvil-retention.ts +60 -0
  290. package/vendor/@anvil/common/src/util/reddit-capi-events.ts +36 -0
  291. package/vendor/@anvil/common/src/util/response-ad-positions.ts +54 -0
  292. package/vendor/@anvil/common/src/util/saxy.ts +741 -0
  293. package/vendor/@anvil/common/src/util/skills.test.ts +44 -0
  294. package/vendor/@anvil/common/src/util/skills.ts +36 -0
  295. package/vendor/@anvil/common/src/util/split-data.ts +259 -0
  296. package/vendor/@anvil/common/src/util/stop-sequence.ts +61 -0
  297. package/vendor/@anvil/common/src/util/string.ts +426 -0
  298. package/vendor/@anvil/common/src/util/system-info.ts +53 -0
  299. package/vendor/@anvil/common/src/util/thread-title.ts +46 -0
  300. package/vendor/@anvil/common/src/util/ttft-histogram.ts +73 -0
  301. package/vendor/@anvil/common/src/util/xml-parser.ts +27 -0
  302. package/vendor/@anvil/common/src/util/xml.ts +17 -0
  303. package/vendor/@anvil/common/src/util/zod-schema.ts +25 -0
  304. package/vendor/@anvil/common/src/util/zoned-time.ts +136 -0
  305. package/vendor/@anvil/common/src/utils/ask-user-bridge.ts +44 -0
  306. package/vendor/@anvil/sdk/dist/index.cjs +52297 -0
  307. package/vendor/@anvil/sdk/dist/index.cjs.map +440 -0
  308. package/vendor/@anvil/sdk/dist/index.d.ts +4277 -0
  309. package/vendor/@anvil/sdk/dist/index.mjs +52290 -0
  310. package/vendor/@anvil/sdk/dist/index.mjs.map +440 -0
  311. package/vendor/@anvil/sdk/dist/vendor/ai.cjs +30886 -0
  312. package/vendor/@anvil/sdk/dist/vendor/ripgrep/arm64-darwin/rg +0 -0
  313. package/vendor/@anvil/sdk/dist/vendor/ripgrep/arm64-linux/rg +0 -0
  314. package/vendor/@anvil/sdk/dist/vendor/ripgrep/x64-darwin/rg +0 -0
  315. package/vendor/@anvil/sdk/dist/vendor/ripgrep/x64-linux/rg +0 -0
  316. package/vendor/@anvil/sdk/dist/vendor/ripgrep/x64-win32/rg.exe +0 -0
  317. package/vendor/@anvil/sdk/dist/wasm/tree-sitter-c-sharp.wasm +0 -0
  318. package/vendor/@anvil/sdk/dist/wasm/tree-sitter-cpp.wasm +0 -0
  319. package/vendor/@anvil/sdk/dist/wasm/tree-sitter-go.wasm +0 -0
  320. package/vendor/@anvil/sdk/dist/wasm/tree-sitter-java.wasm +0 -0
  321. package/vendor/@anvil/sdk/dist/wasm/tree-sitter-javascript.wasm +0 -0
  322. package/vendor/@anvil/sdk/dist/wasm/tree-sitter-python.wasm +0 -0
  323. package/vendor/@anvil/sdk/dist/wasm/tree-sitter-ruby.wasm +0 -0
  324. package/vendor/@anvil/sdk/dist/wasm/tree-sitter-rust.wasm +0 -0
  325. package/vendor/@anvil/sdk/dist/wasm/tree-sitter-tsx.wasm +0 -0
  326. package/vendor/@anvil/sdk/dist/wasm/tree-sitter-typescript.wasm +0 -0
  327. package/vendor/@anvil/sdk/dist/wasm/tree-sitter.wasm +0 -0
  328. package/vendor/@anvil/sdk/package.json +21 -0
@@ -0,0 +1,685 @@
1
+ import { modelMessageSchema } from 'ai'
2
+ import { cloneDeep, has, isEqual } from 'lodash'
3
+
4
+ import type { Logger } from '../types/contracts/logger'
5
+ import type { JSONValue } from '../types/json'
6
+ import type {
7
+ AssistantMessage,
8
+ AuxiliaryMessageData,
9
+ Message,
10
+ SystemMessage,
11
+ ToolMessage,
12
+ UserMessage,
13
+ } from '../types/messages/anvil-message'
14
+ import type {
15
+ ToolCallPart,
16
+ ToolResultOutput,
17
+ } from '../types/messages/content-part'
18
+ import type { ProviderMetadata } from '../types/messages/provider-metadata'
19
+ import type {
20
+ AssistantModelMessage,
21
+ ModelMessage,
22
+ SystemModelMessage,
23
+ ToolModelMessage,
24
+ UserModelMessage,
25
+ } from 'ai'
26
+
27
+
28
+ export function toContentString(msg: ModelMessage): string {
29
+ const { content } = msg
30
+ if (typeof content === 'string') return content
31
+ return content
32
+ .map((item) =>
33
+ item && 'text' in item && typeof item.text === 'string' ? item.text : '',
34
+ )
35
+ .join('\n')
36
+ }
37
+
38
+ export function withCacheControl<T extends object>(
39
+ obj: T & { providerOptions?: ProviderMetadata },
40
+ ): T & { providerOptions: ProviderMetadata } {
41
+ const wrapper = cloneDeep(obj) as T & {
42
+ providerOptions: ProviderMetadata
43
+ }
44
+ if (!wrapper.providerOptions) {
45
+ wrapper.providerOptions = {}
46
+ }
47
+
48
+ /* 'anvil' provider name is not compatible with providerMetadata for
49
+ * messages, so we need to use 'openaiCompatible' instead.
50
+ * https://github.com/vercel/ai/blob/8e4fdac31b4f8c6a8d07a606a8833e74adf99470/packages/openai-compatible/src/chat/convert-to-openai-compatible-chat-messages.ts#L9
51
+ */
52
+ for (const provider of [
53
+ 'anthropic',
54
+ 'openrouter',
55
+ 'openaiCompatible',
56
+ ] as const) {
57
+ if (!wrapper.providerOptions[provider]) {
58
+ wrapper.providerOptions[provider] = {}
59
+ }
60
+ wrapper.providerOptions[provider].cache_control = { type: 'ephemeral' }
61
+ }
62
+
63
+ return wrapper
64
+ }
65
+
66
+ export function withoutCacheControl<T extends object>(
67
+ obj: T & { providerOptions?: ProviderMetadata },
68
+ ): T & { providerOptions?: ProviderMetadata } {
69
+ const wrapper = cloneDeep(obj) as T & {
70
+ providerOptions?: ProviderMetadata
71
+ }
72
+
73
+ for (const provider of [
74
+ 'anthropic',
75
+ 'openrouter',
76
+ 'openaiCompatible',
77
+ ] as const) {
78
+ if (has(wrapper.providerOptions?.[provider]?.cache_control, 'type')) {
79
+ delete wrapper.providerOptions?.[provider]?.cache_control?.type
80
+ }
81
+ if (
82
+ Object.keys(wrapper.providerOptions?.[provider]?.cache_control ?? {})
83
+ .length === 0
84
+ ) {
85
+ delete wrapper.providerOptions?.[provider]?.cache_control
86
+ }
87
+ if (Object.keys(wrapper.providerOptions?.[provider] ?? {}).length === 0) {
88
+ delete wrapper.providerOptions?.[provider]
89
+ }
90
+ }
91
+
92
+ if (Object.keys(wrapper.providerOptions ?? {}).length === 0) {
93
+ delete wrapper.providerOptions
94
+ }
95
+
96
+ return wrapper
97
+ }
98
+
99
+ type NonStringContent<T extends { content: any }> = Omit<T, 'content'> & {
100
+ content: Exclude<T['content'], string>
101
+ }
102
+ type ModelMessageWithAuxiliaryData = (
103
+ | SystemModelMessage
104
+ | NonStringContent<UserModelMessage>
105
+ | NonStringContent<AssistantModelMessage>
106
+ | ToolModelMessage
107
+ ) &
108
+ AuxiliaryMessageData
109
+
110
+ function assistantToAnvilMessage(
111
+ message: Omit<AssistantMessage, 'content'> & {
112
+ content: Exclude<AssistantMessage['content'], string>[number]
113
+ },
114
+ ): AssistantMessage {
115
+ // if (message.content.type === 'tool-call') {
116
+ // return cloneDeep({
117
+ // ...message,
118
+ // content: [
119
+ // {
120
+ // type: 'text',
121
+ // text: getToolCallString(
122
+ // message.content.toolName,
123
+ // message.content.input,
124
+ // false,
125
+ // ),
126
+ // },
127
+ // ],
128
+ // })
129
+ // }
130
+ return cloneDeep({ ...message, content: [message.content] })
131
+ }
132
+
133
+ /** A tool message answering `message`'s call, carrying `output`. */
134
+ function toolResultMessage(
135
+ message: ToolMessage,
136
+ output: Extract<ToolResultOutput, { type: 'json' }>,
137
+ ): ModelMessageWithAuxiliaryData {
138
+ return cloneDeep<ToolModelMessage>({
139
+ ...message,
140
+ role: 'tool',
141
+ content: [{ ...message, output, type: 'tool-result' }],
142
+ })
143
+ }
144
+
145
+ const EMPTY_TOOL_OUTPUT = { type: 'json', value: '' } as const
146
+
147
+ function convertToolResultMessage(
148
+ message: ToolMessage,
149
+ ): ModelMessageWithAuxiliaryData[] {
150
+ if (message.content.length === 0) {
151
+ return [toolResultMessage(message, EMPTY_TOOL_OUTPUT)]
152
+ }
153
+ // A `media` part becomes a *user* message, because providers do not accept
154
+ // images inside a tool message. Emit every tool message first regardless of
155
+ // the part order, so the results still immediately follow the assistant's
156
+ // tool_calls — otherwise a tool that returns [media, json] (which is the
157
+ // order preview_screenshot uses, so the model sees pixels before the note)
158
+ // puts a user message between the call and its result. Providers reject that
159
+ // outright ("tool call result does not follow tool call"), and the server's
160
+ // repair drops the call and the result as an unpaired pair.
161
+ const toolMessages: ModelMessageWithAuxiliaryData[] = []
162
+ const mediaMessages: ModelMessageWithAuxiliaryData[] = []
163
+ for (const c of message.content) {
164
+ if (c.type === 'json') {
165
+ toolMessages.push(toolResultMessage(message, c))
166
+ continue
167
+ }
168
+ if (c.type === 'media') {
169
+ mediaMessages.push(
170
+ cloneDeep<UserMessage>({
171
+ ...message,
172
+ role: 'user',
173
+ content: [{ type: 'file', data: c.data, mediaType: c.mediaType }],
174
+ }),
175
+ )
176
+ continue
177
+ }
178
+ c satisfies never
179
+ throw new Error(
180
+ `Invalid tool output type: ${(c as { type: unknown }).type}`,
181
+ )
182
+ }
183
+
184
+ // Media-only output (see `mediaToolResult`) would otherwise emit a user
185
+ // message and no tool message at all, leaving the call permanently
186
+ // unanswered — the same unpaired-tool-call failure, just reached a different
187
+ // way. Answer it with the empty result the no-content branch already uses.
188
+ if (toolMessages.length === 0) {
189
+ toolMessages.push(toolResultMessage(message, EMPTY_TOOL_OUTPUT))
190
+ }
191
+
192
+ return [...toolMessages, ...mediaMessages]
193
+ }
194
+
195
+ function convertToolMessage(message: Message): ModelMessageWithAuxiliaryData[] {
196
+ if (message.role === 'system') {
197
+ return [
198
+ {
199
+ ...message,
200
+ content: message.content.map(({ text }) => text).join('\n\n'),
201
+ },
202
+ ]
203
+ }
204
+ if (message.role === 'user') {
205
+ return [cloneDeep(message)]
206
+ }
207
+ if (message.role === 'assistant') {
208
+ if (typeof message.content === 'string') {
209
+ return [
210
+ cloneDeep({
211
+ ...message,
212
+ content: [{ type: 'text' as const, text: message.content }],
213
+ }),
214
+ ]
215
+ }
216
+ return message.content.map((c) => {
217
+ return assistantToAnvilMessage({
218
+ ...message,
219
+ content: c,
220
+ })
221
+ })
222
+ }
223
+ if (message.role === 'tool') {
224
+ return convertToolResultMessage(message)
225
+ }
226
+ message satisfies never
227
+ throw new Error(
228
+ `Invalid message role: ${(message as { role: unknown }).role}`,
229
+ )
230
+ }
231
+
232
+ function convertToolMessages(
233
+ messages: Message[],
234
+ ): ModelMessageWithAuxiliaryData[] {
235
+ const withoutToolMessages: ModelMessageWithAuxiliaryData[] = []
236
+ // Media parts ride *user* messages (see convertToolResultMessage), and the AI
237
+ // SDK throws at a user/system boundary when any tool call issued so far is
238
+ // still unanswered. Emitting media inline therefore splits a batch of
239
+ // parallel calls -- the sibling whose result is not reached yet is still
240
+ // pending, so validation fails client-side before the request is ever sent,
241
+ // permanently wedging the thread that replays that history. Buffer media
242
+ // until every call is answered, then flush: waiting on the answers not on
243
+ // the next non-tool message keeps the image as close to its own result as is
244
+ // safe, even when something is interleaved between the results.
245
+ // (convertToolResultMessage already does this within one tool message.)
246
+ const unanswered = new Set<string>()
247
+ let pendingMedia: ModelMessageWithAuxiliaryData[] = []
248
+ const flushMedia = () => {
249
+ if (unanswered.size > 0 || pendingMedia.length === 0) return
250
+ withoutToolMessages.push(...pendingMedia)
251
+ pendingMedia = []
252
+ }
253
+
254
+ for (const message of messages) {
255
+ const converted = convertToolMessage(message)
256
+ if (message.role !== 'tool') {
257
+ flushMedia()
258
+ withoutToolMessages.push(...converted)
259
+ for (const part of converted) {
260
+ if (part.role !== 'assistant') continue
261
+ for (const content of part.content) {
262
+ // Mirrors the SDK: a provider-executed call needs no local result, so
263
+ // tracking one would defer every later image forever.
264
+ if (content.type !== 'tool-call') continue
265
+ if (content.providerExecuted !== true) {
266
+ unanswered.add(content.toolCallId)
267
+ }
268
+ }
269
+ }
270
+ continue
271
+ }
272
+ for (const part of converted) {
273
+ if (part.role !== 'tool') {
274
+ pendingMedia.push(part)
275
+ continue
276
+ }
277
+ withoutToolMessages.push(part)
278
+ for (const content of part.content) {
279
+ if (content.type === 'tool-result') {
280
+ unanswered.delete(content.toolCallId)
281
+ }
282
+ }
283
+ }
284
+ flushMedia()
285
+ }
286
+
287
+ // Anything still buffered had no clean boundary; dropUnansweredToolCalls has
288
+ // already removed calls that never get answered, so this is just a backstop.
289
+ withoutToolMessages.push(...pendingMedia)
290
+ return withoutToolMessages
291
+ }
292
+
293
+ /**
294
+ * Recursively replace any lone (unpaired) UTF-16 surrogate with U+FFFD in every
295
+ * string reachable from `value`, mutating objects/arrays in place.
296
+ *
297
+ * Why this exists: unsafe truncation (e.g. slicing a file read or terminal
298
+ * output in the middle of an emoji / astral-plane character) can leave a lone
299
+ * surrogate in message content. JS's `JSON.stringify` is "well-formed" and emits
300
+ * it as a syntactically-valid `\uXXXX` escape, and JS's `JSON.parse` is lenient
301
+ * and accepts it, so the corruption slips through every client-side check. But
302
+ * strict server-side parsers — notably Rust's serde_json, used by
303
+ * OpenAI/OpenRouter/Anthropic — reject the whole request body with
304
+ * "unexpected end of hex escape". Once such content lands in the message
305
+ * history, EVERY subsequent provider request fails fatally and the agent stops,
306
+ * even though nothing is wrong with the current turn's tool call.
307
+ *
308
+ * Sanitizing here, at the single chokepoint where all messages are converted to
309
+ * provider format, guarantees a single bad character can never poison the
310
+ * conversation regardless of which tool produced it. It is a no-op on
311
+ * already-valid strings, so valid emoji, base64, etc. are untouched.
312
+ *
313
+ * (Equivalent to `String.prototype.toWellFormed()`, implemented as a regex so we
314
+ * don't need to widen the project's TS lib to ES2024.)
315
+ */
316
+ const LONE_SURROGATE_REGEX =
317
+ /[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?<![\uD800-\uDBFF])[\uDC00-\uDFFF]/g
318
+
319
+ function toWellFormedString(str: string): string {
320
+ return str.replace(LONE_SURROGATE_REGEX, '�')
321
+ }
322
+
323
+ function wellFormStringsInPlace(value: unknown): void {
324
+ // Arrays and plain objects are both handled here: Object.keys enumerates array
325
+ // indices too, and indexing by string key mutates the element in place.
326
+ if (!value || typeof value !== 'object') return
327
+ const obj = value as Record<string, unknown>
328
+ for (const key of Object.keys(obj)) {
329
+ const item = obj[key]
330
+ if (typeof item === 'string') {
331
+ obj[key] = toWellFormedString(item)
332
+ } else {
333
+ wellFormStringsInPlace(item)
334
+ }
335
+ }
336
+ }
337
+
338
+ export function convertCbToModelMessages({
339
+ messages,
340
+ includeCacheControl = true,
341
+ logger,
342
+ }: {
343
+ messages: Message[]
344
+ includeCacheControl?: boolean
345
+ logger?: Logger
346
+ }): ModelMessage[] {
347
+ // AI SDK validates tool-call/result pairing before making a request. Repair
348
+ // at this lowest shared boundary as well as in the runtime step builder so
349
+ // auxiliary calls and restored SDK state cannot poison a thread permanently.
350
+ const sendableMessages = dropUnansweredToolCalls(messages)
351
+ const toolMessagesConverted: ModelMessageWithAuxiliaryData[] =
352
+ convertToolMessages(sendableMessages)
353
+
354
+ const aggregated: ModelMessageWithAuxiliaryData[] = []
355
+ for (const message of toolMessagesConverted) {
356
+ if (aggregated.length === 0) {
357
+ aggregated.push(message)
358
+ continue
359
+ }
360
+
361
+ const lastMessage = aggregated[aggregated.length - 1]
362
+ if (
363
+ lastMessage.timeToLive !== message.timeToLive ||
364
+ !isEqual(lastMessage.providerOptions, message.providerOptions) ||
365
+ !isEqual(lastMessage.tags, message.tags)
366
+ ) {
367
+ aggregated.push(message)
368
+ continue
369
+ }
370
+ if (lastMessage.role === 'system' && message.role === 'system') {
371
+ lastMessage.content += '\n\n' + message.content
372
+ continue
373
+ }
374
+ if (lastMessage.role === 'user' && message.role === 'user') {
375
+ lastMessage.content.push(...message.content)
376
+ continue
377
+ }
378
+ if (lastMessage.role === 'assistant' && message.role === 'assistant') {
379
+ lastMessage.content.push(...message.content)
380
+ continue
381
+ }
382
+
383
+ aggregated.push(message)
384
+ }
385
+
386
+ // Neutralize any lone UTF-16 surrogates before the messages reach the provider.
387
+ // These are mutated in place; every aggregated message is a fresh clone (see
388
+ // convertToolMessage), so the caller's message history is unaffected.
389
+ for (const message of aggregated) {
390
+ if (typeof message.content === 'string') {
391
+ message.content = toWellFormedString(message.content)
392
+ } else {
393
+ wellFormStringsInPlace(message.content)
394
+ }
395
+ }
396
+
397
+ if (!includeCacheControl) {
398
+ return aggregated
399
+ }
400
+
401
+ // Add cache control to specific messages (max of 4 can be marked for caching!):
402
+ // - The message right before the three tagged messages
403
+ // - Last message
404
+ for (const tag of [
405
+ 'LAST_ASSISTANT_MESSAGE',
406
+ 'USER_PROMPT',
407
+ 'STEP_PROMPT',
408
+ undefined, // Last message
409
+ ] as const) {
410
+ let index =
411
+ tag === 'LAST_ASSISTANT_MESSAGE'
412
+ ? aggregated.findLastIndex((m) => m.role === 'assistant')
413
+ : tag
414
+ ? aggregated.findLastIndex((m) => m.tags?.includes(tag))
415
+ : aggregated.length
416
+ if (index <= 0) {
417
+ continue
418
+ }
419
+
420
+ // Iterate to find the last "valid" message that we can cache control
421
+ let prevMessage: (typeof aggregated)[number]
422
+ let contentBlock: (typeof prevMessage)['content']
423
+ addCacheControlLoop: while (true) {
424
+ index--
425
+
426
+ // No message found
427
+ if (index < 0) {
428
+ break
429
+ }
430
+
431
+ prevMessage = aggregated[index]
432
+ contentBlock = prevMessage.content
433
+
434
+ if (typeof contentBlock === 'string') {
435
+ // This must be a system message
436
+ aggregated[index] = withCacheControl(aggregated[index])
437
+ break
438
+ }
439
+
440
+ // Iterate to find the last valid content part (not a very short string)
441
+ let lastContentIndex = contentBlock.length
442
+ let lastContentPart: (typeof contentBlock)[number]
443
+ while (true) {
444
+ lastContentIndex--
445
+ lastContentPart = contentBlock[lastContentIndex]
446
+
447
+ if (lastContentIndex < 0) {
448
+ // Continue searching in next message
449
+ break
450
+ }
451
+
452
+ if (lastContentPart.type !== 'text') {
453
+ contentBlock[lastContentIndex] = withCacheControl(
454
+ contentBlock[lastContentIndex],
455
+ )
456
+ break addCacheControlLoop
457
+ }
458
+
459
+ prevMessage.content = [
460
+ ...contentBlock.slice(0, lastContentIndex),
461
+ withCacheControl(lastContentPart),
462
+ ...contentBlock.slice(lastContentIndex + 1),
463
+ ] as typeof contentBlock
464
+
465
+ break addCacheControlLoop
466
+ }
467
+ break
468
+ }
469
+ }
470
+
471
+ // Validate each message against the AI SDK schema
472
+ for (let i = 0; i < aggregated.length; i++) {
473
+ const message = aggregated[i]
474
+ const result = modelMessageSchema.safeParse(message)
475
+ if (!result.success) {
476
+ if (logger) {
477
+ logger.error(
478
+ { message, aggregated, error: result.error },
479
+ `convertCbToModelMessages: Message at index ${i} failed schema validation.`,
480
+ )
481
+ }
482
+ throw new Error(
483
+ `convertCbToModelMessages: Message at index ${i} failed schema validation.\n` +
484
+ `Role: ${message.role}\n` +
485
+ `Message:\n${result.error.message}`,
486
+ )
487
+ }
488
+ }
489
+
490
+ return aggregated
491
+ }
492
+
493
+ /**
494
+ * Drop local tool calls that were not answered before the next user/system
495
+ * boundary or the end of history.
496
+ *
497
+ * AI SDK validates that boundary before making a request. A result later in
498
+ * history cannot cross a user/system boundary to answer an interrupted call.
499
+ * Provider-executed calls need no local answer.
500
+ *
501
+ * Tool messages themselves are preserved: the SDK intentionally supports
502
+ * freestanding results from user-run commands between prompts. The web API's
503
+ * wire-format sanitizer handles provider-specific orphan and duplicate rules.
504
+ */
505
+ export function dropUnansweredToolCalls(messages: Message[]): Message[] {
506
+ const pending = new Map<string, ToolCallPart[]>()
507
+ const unanswered = new Set<ToolCallPart>()
508
+ const flushPending = () => {
509
+ for (const calls of pending.values()) {
510
+ for (const call of calls) unanswered.add(call)
511
+ }
512
+ pending.clear()
513
+ }
514
+
515
+ // Mirror AI SDK's validator: assistant calls add pending ids, tool messages
516
+ // resolve them, and a user/system boundary (or end of history) requires the
517
+ // pending set to be empty.
518
+ for (const message of messages) {
519
+ if (message.role === 'assistant' && Array.isArray(message.content)) {
520
+ for (const part of message.content) {
521
+ if (part.type !== 'tool-call' || part.providerExecuted === true) continue
522
+ const calls = pending.get(part.toolCallId)
523
+ if (calls) calls.push(part)
524
+ else pending.set(part.toolCallId, [part])
525
+ }
526
+ } else if (message.role === 'tool') {
527
+ pending.delete(message.toolCallId)
528
+ } else if (message.role === 'user' || message.role === 'system') {
529
+ flushPending()
530
+ }
531
+ }
532
+ flushPending()
533
+
534
+ if (unanswered.size === 0) return messages
535
+
536
+ return messages.flatMap((message) => {
537
+ if (message.role !== 'assistant' || !Array.isArray(message.content)) {
538
+ return [message]
539
+ }
540
+ const content = message.content.filter(
541
+ (part) => part.type !== 'tool-call' || !unanswered.has(part),
542
+ )
543
+ return content.length > 0 ? [{ ...message, content }] : []
544
+ })
545
+ }
546
+
547
+ // type NoContent<T> = T & { content?: never }
548
+ export type SystemContent =
549
+ | string
550
+ | SystemMessage['content'][number]
551
+ | SystemMessage['content']
552
+ export function systemContent(
553
+ content: SystemContent,
554
+ ): SystemMessage['content'] {
555
+ if (typeof content === 'string') {
556
+ return [{ type: 'text', text: content }]
557
+ }
558
+ if (Array.isArray(content)) {
559
+ return content
560
+ }
561
+ return [content]
562
+ }
563
+
564
+ export function systemMessage(
565
+ params:
566
+ | SystemContent
567
+ | ({
568
+ content: SystemContent
569
+ } & Omit<SystemMessage, 'role' | 'content'>),
570
+ ): SystemMessage {
571
+ if (typeof params === 'object' && 'content' in params) {
572
+ return {
573
+ ...params,
574
+ role: 'system',
575
+ content: systemContent(params.content),
576
+ }
577
+ }
578
+ return {
579
+ role: 'system',
580
+ content: systemContent(params),
581
+ }
582
+ }
583
+
584
+ export type UserContent =
585
+ | string
586
+ | UserMessage['content'][number]
587
+ | UserMessage['content']
588
+ export function userContent(content: UserContent): UserMessage['content'] {
589
+ if (typeof content === 'string') {
590
+ return [{ type: 'text', text: content }]
591
+ }
592
+ if (Array.isArray(content)) {
593
+ return content
594
+ }
595
+ return [content]
596
+ }
597
+
598
+ export function userMessage(
599
+ params:
600
+ | UserContent
601
+ | ({
602
+ content: UserContent
603
+ } & Omit<UserMessage, 'role' | 'content'>),
604
+ ): UserMessage {
605
+ if (typeof params === 'object' && 'content' in params) {
606
+ return {
607
+ ...params,
608
+ role: 'user',
609
+ content: userContent(params.content),
610
+ sentAt: Date.now(),
611
+ }
612
+ }
613
+ return {
614
+ role: 'user',
615
+ content: userContent(params),
616
+ sentAt: Date.now(),
617
+ }
618
+ }
619
+
620
+ export type AssistantContent =
621
+ | string
622
+ | AssistantMessage['content'][number]
623
+ | AssistantMessage['content']
624
+ export function assistantContent(
625
+ content: AssistantContent,
626
+ ): AssistantMessage['content'] {
627
+ if (typeof content === 'string') {
628
+ return [{ type: 'text', text: content }]
629
+ }
630
+ if (Array.isArray(content)) {
631
+ return content
632
+ }
633
+ return [content]
634
+ }
635
+
636
+ export function assistantMessage(
637
+ params:
638
+ | AssistantContent
639
+ | ({
640
+ content: AssistantContent
641
+ } & Omit<AssistantMessage, 'role' | 'content'>),
642
+ ): AssistantMessage {
643
+ if (typeof params === 'object' && 'content' in params) {
644
+ return {
645
+ ...params,
646
+ role: 'assistant',
647
+ content: assistantContent(params.content),
648
+ sentAt: Date.now(),
649
+ }
650
+ }
651
+ return {
652
+ role: 'assistant',
653
+ content: assistantContent(params),
654
+ sentAt: Date.now(),
655
+ }
656
+ }
657
+
658
+ export function jsonToolResult<T extends JSONValue>(
659
+ value: T,
660
+ ): [
661
+ Extract<ToolResultOutput, { type: 'json' }> & {
662
+ value: T
663
+ },
664
+ ] {
665
+ return [
666
+ {
667
+ type: 'json',
668
+ value,
669
+ },
670
+ ]
671
+ }
672
+
673
+ export function mediaToolResult(params: {
674
+ data: string
675
+ mediaType: string
676
+ }): [Extract<ToolResultOutput, { type: 'media' }>] {
677
+ const { data, mediaType } = params
678
+ return [
679
+ {
680
+ type: 'media',
681
+ data,
682
+ mediaType,
683
+ },
684
+ ]
685
+ }