@esankhan3/anvil-agent-core 0.1.0

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 (459) hide show
  1. package/README.md +142 -0
  2. package/dist/__tests__/adapter-enrichment.test.d.ts +12 -0
  3. package/dist/__tests__/adapter-enrichment.test.d.ts.map +1 -0
  4. package/dist/__tests__/adapter-enrichment.test.js +199 -0
  5. package/dist/__tests__/adapter-enrichment.test.js.map +1 -0
  6. package/dist/__tests__/adk-anthropic-llm.test.d.ts +18 -0
  7. package/dist/__tests__/adk-anthropic-llm.test.d.ts.map +1 -0
  8. package/dist/__tests__/adk-anthropic-llm.test.js +124 -0
  9. package/dist/__tests__/adk-anthropic-llm.test.js.map +1 -0
  10. package/dist/__tests__/cost-breakdown.test.d.ts +11 -0
  11. package/dist/__tests__/cost-breakdown.test.d.ts.map +1 -0
  12. package/dist/__tests__/cost-breakdown.test.js +163 -0
  13. package/dist/__tests__/cost-breakdown.test.js.map +1 -0
  14. package/dist/__tests__/mcp.test.d.ts +13 -0
  15. package/dist/__tests__/mcp.test.d.ts.map +1 -0
  16. package/dist/__tests__/mcp.test.js +413 -0
  17. package/dist/__tests__/mcp.test.js.map +1 -0
  18. package/dist/__tests__/ollama-adapter-agentic.test.d.ts +15 -0
  19. package/dist/__tests__/ollama-adapter-agentic.test.d.ts.map +1 -0
  20. package/dist/__tests__/ollama-adapter-agentic.test.js +268 -0
  21. package/dist/__tests__/ollama-adapter-agentic.test.js.map +1 -0
  22. package/dist/__tests__/ollama-adapter-executor.test.d.ts +10 -0
  23. package/dist/__tests__/ollama-adapter-executor.test.d.ts.map +1 -0
  24. package/dist/__tests__/ollama-adapter-executor.test.js +95 -0
  25. package/dist/__tests__/ollama-adapter-executor.test.js.map +1 -0
  26. package/dist/__tests__/openai-adapter-output.test.d.ts +17 -0
  27. package/dist/__tests__/openai-adapter-output.test.d.ts.map +1 -0
  28. package/dist/__tests__/openai-adapter-output.test.js +127 -0
  29. package/dist/__tests__/openai-adapter-output.test.js.map +1 -0
  30. package/dist/__tests__/opencode-adapter.test.d.ts +7 -0
  31. package/dist/__tests__/opencode-adapter.test.d.ts.map +1 -0
  32. package/dist/__tests__/opencode-adapter.test.js +141 -0
  33. package/dist/__tests__/opencode-adapter.test.js.map +1 -0
  34. package/dist/__tests__/openrouter-adapter-agentic.test.d.ts +11 -0
  35. package/dist/__tests__/openrouter-adapter-agentic.test.d.ts.map +1 -0
  36. package/dist/__tests__/openrouter-adapter-agentic.test.js +297 -0
  37. package/dist/__tests__/openrouter-adapter-agentic.test.js.map +1 -0
  38. package/dist/__tests__/router-circuit-breaker.test.d.ts +6 -0
  39. package/dist/__tests__/router-circuit-breaker.test.d.ts.map +1 -0
  40. package/dist/__tests__/router-circuit-breaker.test.js +253 -0
  41. package/dist/__tests__/router-circuit-breaker.test.js.map +1 -0
  42. package/dist/__tests__/router-config-loader.test.d.ts +8 -0
  43. package/dist/__tests__/router-config-loader.test.d.ts.map +1 -0
  44. package/dist/__tests__/router-config-loader.test.js +172 -0
  45. package/dist/__tests__/router-config-loader.test.js.map +1 -0
  46. package/dist/__tests__/router-discovery.test.d.ts +5 -0
  47. package/dist/__tests__/router-discovery.test.d.ts.map +1 -0
  48. package/dist/__tests__/router-discovery.test.js +129 -0
  49. package/dist/__tests__/router-discovery.test.js.map +1 -0
  50. package/dist/__tests__/router-fallback.test.d.ts +9 -0
  51. package/dist/__tests__/router-fallback.test.d.ts.map +1 -0
  52. package/dist/__tests__/router-fallback.test.js +283 -0
  53. package/dist/__tests__/router-fallback.test.js.map +1 -0
  54. package/dist/__tests__/router-local-executor.test.d.ts +6 -0
  55. package/dist/__tests__/router-local-executor.test.d.ts.map +1 -0
  56. package/dist/__tests__/router-local-executor.test.js +193 -0
  57. package/dist/__tests__/router-local-executor.test.js.map +1 -0
  58. package/dist/__tests__/router-model-registry.test.d.ts +8 -0
  59. package/dist/__tests__/router-model-registry.test.d.ts.map +1 -0
  60. package/dist/__tests__/router-model-registry.test.js +389 -0
  61. package/dist/__tests__/router-model-registry.test.js.map +1 -0
  62. package/dist/__tests__/router-rate-limiter.test.d.ts +8 -0
  63. package/dist/__tests__/router-rate-limiter.test.d.ts.map +1 -0
  64. package/dist/__tests__/router-rate-limiter.test.js +128 -0
  65. package/dist/__tests__/router-rate-limiter.test.js.map +1 -0
  66. package/dist/__tests__/router-resolver.test.d.ts +7 -0
  67. package/dist/__tests__/router-resolver.test.d.ts.map +1 -0
  68. package/dist/__tests__/router-resolver.test.js +110 -0
  69. package/dist/__tests__/router-resolver.test.js.map +1 -0
  70. package/dist/__tests__/router-retry.test.d.ts +7 -0
  71. package/dist/__tests__/router-retry.test.d.ts.map +1 -0
  72. package/dist/__tests__/router-retry.test.js +302 -0
  73. package/dist/__tests__/router-retry.test.js.map +1 -0
  74. package/dist/__tests__/router-scaffold.test.d.ts +11 -0
  75. package/dist/__tests__/router-scaffold.test.d.ts.map +1 -0
  76. package/dist/__tests__/router-scaffold.test.js +103 -0
  77. package/dist/__tests__/router-scaffold.test.js.map +1 -0
  78. package/dist/__tests__/router-spend-ledger.test.d.ts +8 -0
  79. package/dist/__tests__/router-spend-ledger.test.d.ts.map +1 -0
  80. package/dist/__tests__/router-spend-ledger.test.js +227 -0
  81. package/dist/__tests__/router-spend-ledger.test.js.map +1 -0
  82. package/dist/__tests__/router-telemetry.test.d.ts +11 -0
  83. package/dist/__tests__/router-telemetry.test.d.ts.map +1 -0
  84. package/dist/__tests__/router-telemetry.test.js +173 -0
  85. package/dist/__tests__/router-telemetry.test.js.map +1 -0
  86. package/dist/__tests__/single-shot.test.d.ts +12 -0
  87. package/dist/__tests__/single-shot.test.d.ts.map +1 -0
  88. package/dist/__tests__/single-shot.test.js +204 -0
  89. package/dist/__tests__/single-shot.test.js.map +1 -0
  90. package/dist/__tests__/skills-prompt.test.d.ts +11 -0
  91. package/dist/__tests__/skills-prompt.test.d.ts.map +1 -0
  92. package/dist/__tests__/skills-prompt.test.js +208 -0
  93. package/dist/__tests__/skills-prompt.test.js.map +1 -0
  94. package/dist/__tests__/skills.test.d.ts +11 -0
  95. package/dist/__tests__/skills.test.d.ts.map +1 -0
  96. package/dist/__tests__/skills.test.js +161 -0
  97. package/dist/__tests__/skills.test.js.map +1 -0
  98. package/dist/__tests__/telemetry-integration.test.d.ts +12 -0
  99. package/dist/__tests__/telemetry-integration.test.d.ts.map +1 -0
  100. package/dist/__tests__/telemetry-integration.test.js +153 -0
  101. package/dist/__tests__/telemetry-integration.test.js.map +1 -0
  102. package/dist/__tests__/telemetry.test.d.ts +13 -0
  103. package/dist/__tests__/telemetry.test.d.ts.map +1 -0
  104. package/dist/__tests__/telemetry.test.js +161 -0
  105. package/dist/__tests__/telemetry.test.js.map +1 -0
  106. package/dist/__tests__/upstream-error.test.d.ts +8 -0
  107. package/dist/__tests__/upstream-error.test.d.ts.map +1 -0
  108. package/dist/__tests__/upstream-error.test.js +186 -0
  109. package/dist/__tests__/upstream-error.test.js.map +1 -0
  110. package/dist/adk-adapter.d.ts +45 -0
  111. package/dist/adk-adapter.d.ts.map +1 -0
  112. package/dist/adk-adapter.js +403 -0
  113. package/dist/adk-adapter.js.map +1 -0
  114. package/dist/adk-anthropic-llm.d.ts +59 -0
  115. package/dist/adk-anthropic-llm.d.ts.map +1 -0
  116. package/dist/adk-anthropic-llm.js +397 -0
  117. package/dist/adk-anthropic-llm.js.map +1 -0
  118. package/dist/agent/index.d.ts +14 -0
  119. package/dist/agent/index.d.ts.map +1 -0
  120. package/dist/agent/index.js +14 -0
  121. package/dist/agent/index.js.map +1 -0
  122. package/dist/agent/session/__tests__/bridge-tool-executor.test.d.ts +10 -0
  123. package/dist/agent/session/__tests__/bridge-tool-executor.test.d.ts.map +1 -0
  124. package/dist/agent/session/__tests__/bridge-tool-executor.test.js +152 -0
  125. package/dist/agent/session/__tests__/bridge-tool-executor.test.js.map +1 -0
  126. package/dist/agent/session/__tests__/collect-trajectory.test.d.ts +18 -0
  127. package/dist/agent/session/__tests__/collect-trajectory.test.d.ts.map +1 -0
  128. package/dist/agent/session/__tests__/collect-trajectory.test.js +208 -0
  129. package/dist/agent/session/__tests__/collect-trajectory.test.js.map +1 -0
  130. package/dist/agent/session/__tests__/run-with-agent.test.d.ts +8 -0
  131. package/dist/agent/session/__tests__/run-with-agent.test.d.ts.map +1 -0
  132. package/dist/agent/session/__tests__/run-with-agent.test.js +98 -0
  133. package/dist/agent/session/__tests__/run-with-agent.test.js.map +1 -0
  134. package/dist/agent/session/__tests__/runtime.test.d.ts +7 -0
  135. package/dist/agent/session/__tests__/runtime.test.d.ts.map +1 -0
  136. package/dist/agent/session/__tests__/runtime.test.js +361 -0
  137. package/dist/agent/session/__tests__/runtime.test.js.map +1 -0
  138. package/dist/agent/session/__tests__/skills-mcp-spawn.test.d.ts +15 -0
  139. package/dist/agent/session/__tests__/skills-mcp-spawn.test.d.ts.map +1 -0
  140. package/dist/agent/session/__tests__/skills-mcp-spawn.test.js +160 -0
  141. package/dist/agent/session/__tests__/skills-mcp-spawn.test.js.map +1 -0
  142. package/dist/agent/session/__tests__/spawn-telemetry.test.d.ts +13 -0
  143. package/dist/agent/session/__tests__/spawn-telemetry.test.d.ts.map +1 -0
  144. package/dist/agent/session/__tests__/spawn-telemetry.test.js +135 -0
  145. package/dist/agent/session/__tests__/spawn-telemetry.test.js.map +1 -0
  146. package/dist/agent/session/__tests__/types.test.d.ts +9 -0
  147. package/dist/agent/session/__tests__/types.test.d.ts.map +1 -0
  148. package/dist/agent/session/__tests__/types.test.js +220 -0
  149. package/dist/agent/session/__tests__/types.test.js.map +1 -0
  150. package/dist/agent/session/adapter.d.ts +108 -0
  151. package/dist/agent/session/adapter.d.ts.map +1 -0
  152. package/dist/agent/session/adapter.js +39 -0
  153. package/dist/agent/session/adapter.js.map +1 -0
  154. package/dist/agent/session/collect-trajectory.d.ts +36 -0
  155. package/dist/agent/session/collect-trajectory.d.ts.map +1 -0
  156. package/dist/agent/session/collect-trajectory.js +193 -0
  157. package/dist/agent/session/collect-trajectory.js.map +1 -0
  158. package/dist/agent/session/default-adapter-factory.d.ts +48 -0
  159. package/dist/agent/session/default-adapter-factory.d.ts.map +1 -0
  160. package/dist/agent/session/default-adapter-factory.js +227 -0
  161. package/dist/agent/session/default-adapter-factory.js.map +1 -0
  162. package/dist/agent/session/headless-types.d.ts +85 -0
  163. package/dist/agent/session/headless-types.d.ts.map +1 -0
  164. package/dist/agent/session/headless-types.js +16 -0
  165. package/dist/agent/session/headless-types.js.map +1 -0
  166. package/dist/agent/session/index.d.ts +18 -0
  167. package/dist/agent/session/index.d.ts.map +1 -0
  168. package/dist/agent/session/index.js +20 -0
  169. package/dist/agent/session/index.js.map +1 -0
  170. package/dist/agent/session/language-model-bridge.d.ts +84 -0
  171. package/dist/agent/session/language-model-bridge.d.ts.map +1 -0
  172. package/dist/agent/session/language-model-bridge.js +454 -0
  173. package/dist/agent/session/language-model-bridge.js.map +1 -0
  174. package/dist/agent/session/legacy-adapter-types.d.ts +63 -0
  175. package/dist/agent/session/legacy-adapter-types.d.ts.map +1 -0
  176. package/dist/agent/session/legacy-adapter-types.js +11 -0
  177. package/dist/agent/session/legacy-adapter-types.js.map +1 -0
  178. package/dist/agent/session/run-with-agent.d.ts +36 -0
  179. package/dist/agent/session/run-with-agent.d.ts.map +1 -0
  180. package/dist/agent/session/run-with-agent.js +81 -0
  181. package/dist/agent/session/run-with-agent.js.map +1 -0
  182. package/dist/agent/session/session-registry.d.ts +71 -0
  183. package/dist/agent/session/session-registry.d.ts.map +1 -0
  184. package/dist/agent/session/session-registry.js +209 -0
  185. package/dist/agent/session/session-registry.js.map +1 -0
  186. package/dist/agent/session/session.d.ts +76 -0
  187. package/dist/agent/session/session.d.ts.map +1 -0
  188. package/dist/agent/session/session.js +327 -0
  189. package/dist/agent/session/session.js.map +1 -0
  190. package/dist/agent/session/types.d.ts +230 -0
  191. package/dist/agent/session/types.d.ts.map +1 -0
  192. package/dist/agent/session/types.js +44 -0
  193. package/dist/agent/session/types.js.map +1 -0
  194. package/dist/checkpoint/__tests__/blob-store.test.d.ts +5 -0
  195. package/dist/checkpoint/__tests__/blob-store.test.d.ts.map +1 -0
  196. package/dist/checkpoint/__tests__/blob-store.test.js +82 -0
  197. package/dist/checkpoint/__tests__/blob-store.test.js.map +1 -0
  198. package/dist/checkpoint/__tests__/key.test.d.ts +7 -0
  199. package/dist/checkpoint/__tests__/key.test.d.ts.map +1 -0
  200. package/dist/checkpoint/__tests__/key.test.js +130 -0
  201. package/dist/checkpoint/__tests__/key.test.js.map +1 -0
  202. package/dist/checkpoint/__tests__/runner.test.d.ts +12 -0
  203. package/dist/checkpoint/__tests__/runner.test.d.ts.map +1 -0
  204. package/dist/checkpoint/__tests__/runner.test.js +201 -0
  205. package/dist/checkpoint/__tests__/runner.test.js.map +1 -0
  206. package/dist/checkpoint/__tests__/store.test.d.ts +6 -0
  207. package/dist/checkpoint/__tests__/store.test.d.ts.map +1 -0
  208. package/dist/checkpoint/__tests__/store.test.js +150 -0
  209. package/dist/checkpoint/__tests__/store.test.js.map +1 -0
  210. package/dist/checkpoint/blob-store.d.ts +41 -0
  211. package/dist/checkpoint/blob-store.d.ts.map +1 -0
  212. package/dist/checkpoint/blob-store.js +119 -0
  213. package/dist/checkpoint/blob-store.js.map +1 -0
  214. package/dist/checkpoint/index.d.ts +15 -0
  215. package/dist/checkpoint/index.d.ts.map +1 -0
  216. package/dist/checkpoint/index.js +14 -0
  217. package/dist/checkpoint/index.js.map +1 -0
  218. package/dist/checkpoint/key.d.ts +39 -0
  219. package/dist/checkpoint/key.d.ts.map +1 -0
  220. package/dist/checkpoint/key.js +112 -0
  221. package/dist/checkpoint/key.js.map +1 -0
  222. package/dist/checkpoint/runner.d.ts +83 -0
  223. package/dist/checkpoint/runner.d.ts.map +1 -0
  224. package/dist/checkpoint/runner.js +146 -0
  225. package/dist/checkpoint/runner.js.map +1 -0
  226. package/dist/checkpoint/store.d.ts +91 -0
  227. package/dist/checkpoint/store.d.ts.map +1 -0
  228. package/dist/checkpoint/store.js +292 -0
  229. package/dist/checkpoint/store.js.map +1 -0
  230. package/dist/checkpoint/types.d.ts +74 -0
  231. package/dist/checkpoint/types.d.ts.map +1 -0
  232. package/dist/checkpoint/types.js +16 -0
  233. package/dist/checkpoint/types.js.map +1 -0
  234. package/dist/claude-adapter.d.ts +23 -0
  235. package/dist/claude-adapter.d.ts.map +1 -0
  236. package/dist/claude-adapter.js +233 -0
  237. package/dist/claude-adapter.js.map +1 -0
  238. package/dist/cost.d.ts +67 -0
  239. package/dist/cost.d.ts.map +1 -0
  240. package/dist/cost.js +148 -0
  241. package/dist/cost.js.map +1 -0
  242. package/dist/data/model-prices.json +39681 -0
  243. package/dist/fallback-adapter.d.ts +51 -0
  244. package/dist/fallback-adapter.d.ts.map +1 -0
  245. package/dist/fallback-adapter.js +116 -0
  246. package/dist/fallback-adapter.js.map +1 -0
  247. package/dist/gemini-adapter.d.ts +22 -0
  248. package/dist/gemini-adapter.d.ts.map +1 -0
  249. package/dist/gemini-adapter.js +185 -0
  250. package/dist/gemini-adapter.js.map +1 -0
  251. package/dist/gemini-cli-adapter.d.ts +24 -0
  252. package/dist/gemini-cli-adapter.d.ts.map +1 -0
  253. package/dist/gemini-cli-adapter.js +169 -0
  254. package/dist/gemini-cli-adapter.js.map +1 -0
  255. package/dist/index.d.ts +39 -0
  256. package/dist/index.d.ts.map +1 -0
  257. package/dist/index.js +39 -0
  258. package/dist/index.js.map +1 -0
  259. package/dist/mcp/client.d.ts +32 -0
  260. package/dist/mcp/client.d.ts.map +1 -0
  261. package/dist/mcp/client.js +92 -0
  262. package/dist/mcp/client.js.map +1 -0
  263. package/dist/mcp/config-loader.d.ts +36 -0
  264. package/dist/mcp/config-loader.d.ts.map +1 -0
  265. package/dist/mcp/config-loader.js +129 -0
  266. package/dist/mcp/config-loader.js.map +1 -0
  267. package/dist/mcp/index.d.ts +12 -0
  268. package/dist/mcp/index.d.ts.map +1 -0
  269. package/dist/mcp/index.js +11 -0
  270. package/dist/mcp/index.js.map +1 -0
  271. package/dist/mcp/tool-merger.d.ts +18 -0
  272. package/dist/mcp/tool-merger.d.ts.map +1 -0
  273. package/dist/mcp/tool-merger.js +34 -0
  274. package/dist/mcp/tool-merger.js.map +1 -0
  275. package/dist/mcp/types.d.ts +35 -0
  276. package/dist/mcp/types.d.ts.map +1 -0
  277. package/dist/mcp/types.js +10 -0
  278. package/dist/mcp/types.js.map +1 -0
  279. package/dist/ollama-adapter.d.ts +95 -0
  280. package/dist/ollama-adapter.d.ts.map +1 -0
  281. package/dist/ollama-adapter.js +404 -0
  282. package/dist/ollama-adapter.js.map +1 -0
  283. package/dist/openai-adapter.d.ts +40 -0
  284. package/dist/openai-adapter.d.ts.map +1 -0
  285. package/dist/openai-adapter.js +81 -0
  286. package/dist/openai-adapter.js.map +1 -0
  287. package/dist/opencode-adapter.d.ts +39 -0
  288. package/dist/opencode-adapter.d.ts.map +1 -0
  289. package/dist/opencode-adapter.js +105 -0
  290. package/dist/opencode-adapter.js.map +1 -0
  291. package/dist/openrouter-adapter.d.ts +69 -0
  292. package/dist/openrouter-adapter.d.ts.map +1 -0
  293. package/dist/openrouter-adapter.js +481 -0
  294. package/dist/openrouter-adapter.js.map +1 -0
  295. package/dist/registry.d.ts +55 -0
  296. package/dist/registry.d.ts.map +1 -0
  297. package/dist/registry.js +191 -0
  298. package/dist/registry.js.map +1 -0
  299. package/dist/router/circuit-breaker.d.ts +57 -0
  300. package/dist/router/circuit-breaker.d.ts.map +1 -0
  301. package/dist/router/circuit-breaker.js +110 -0
  302. package/dist/router/circuit-breaker.js.map +1 -0
  303. package/dist/router/config-loader.d.ts +43 -0
  304. package/dist/router/config-loader.d.ts.map +1 -0
  305. package/dist/router/config-loader.js +155 -0
  306. package/dist/router/config-loader.js.map +1 -0
  307. package/dist/router/discovery.d.ts +37 -0
  308. package/dist/router/discovery.d.ts.map +1 -0
  309. package/dist/router/discovery.js +54 -0
  310. package/dist/router/discovery.js.map +1 -0
  311. package/dist/router/errors.d.ts +39 -0
  312. package/dist/router/errors.d.ts.map +1 -0
  313. package/dist/router/errors.js +103 -0
  314. package/dist/router/errors.js.map +1 -0
  315. package/dist/router/index.d.ts +31 -0
  316. package/dist/router/index.d.ts.map +1 -0
  317. package/dist/router/index.js +20 -0
  318. package/dist/router/index.js.map +1 -0
  319. package/dist/router/local-executor.d.ts +62 -0
  320. package/dist/router/local-executor.d.ts.map +1 -0
  321. package/dist/router/local-executor.js +161 -0
  322. package/dist/router/local-executor.js.map +1 -0
  323. package/dist/router/model-registry.d.ts +90 -0
  324. package/dist/router/model-registry.d.ts.map +1 -0
  325. package/dist/router/model-registry.js +242 -0
  326. package/dist/router/model-registry.js.map +1 -0
  327. package/dist/router/rate-limiter.d.ts +63 -0
  328. package/dist/router/rate-limiter.d.ts.map +1 -0
  329. package/dist/router/rate-limiter.js +136 -0
  330. package/dist/router/rate-limiter.js.map +1 -0
  331. package/dist/router/resolver.d.ts +45 -0
  332. package/dist/router/resolver.d.ts.map +1 -0
  333. package/dist/router/resolver.js +71 -0
  334. package/dist/router/resolver.js.map +1 -0
  335. package/dist/router/retry.d.ts +58 -0
  336. package/dist/router/retry.d.ts.map +1 -0
  337. package/dist/router/retry.js +119 -0
  338. package/dist/router/retry.js.map +1 -0
  339. package/dist/router/router.d.ts +100 -0
  340. package/dist/router/router.d.ts.map +1 -0
  341. package/dist/router/router.js +306 -0
  342. package/dist/router/router.js.map +1 -0
  343. package/dist/router/spend-ledger.d.ts +69 -0
  344. package/dist/router/spend-ledger.d.ts.map +1 -0
  345. package/dist/router/spend-ledger.js +191 -0
  346. package/dist/router/spend-ledger.js.map +1 -0
  347. package/dist/router/telemetry.d.ts +48 -0
  348. package/dist/router/telemetry.d.ts.map +1 -0
  349. package/dist/router/telemetry.js +112 -0
  350. package/dist/router/telemetry.js.map +1 -0
  351. package/dist/router/types.d.ts +95 -0
  352. package/dist/router/types.d.ts.map +1 -0
  353. package/dist/router/types.js +16 -0
  354. package/dist/router/types.js.map +1 -0
  355. package/dist/single-shot.d.ts +60 -0
  356. package/dist/single-shot.d.ts.map +1 -0
  357. package/dist/single-shot.js +385 -0
  358. package/dist/single-shot.js.map +1 -0
  359. package/dist/skills/activator.d.ts +17 -0
  360. package/dist/skills/activator.d.ts.map +1 -0
  361. package/dist/skills/activator.js +24 -0
  362. package/dist/skills/activator.js.map +1 -0
  363. package/dist/skills/compose.d.ts +43 -0
  364. package/dist/skills/compose.d.ts.map +1 -0
  365. package/dist/skills/compose.js +53 -0
  366. package/dist/skills/compose.js.map +1 -0
  367. package/dist/skills/index.d.ts +17 -0
  368. package/dist/skills/index.d.ts.map +1 -0
  369. package/dist/skills/index.js +16 -0
  370. package/dist/skills/index.js.map +1 -0
  371. package/dist/skills/loader.d.ts +9 -0
  372. package/dist/skills/loader.d.ts.map +1 -0
  373. package/dist/skills/loader.js +49 -0
  374. package/dist/skills/loader.js.map +1 -0
  375. package/dist/skills/parser.d.ts +13 -0
  376. package/dist/skills/parser.d.ts.map +1 -0
  377. package/dist/skills/parser.js +60 -0
  378. package/dist/skills/parser.js.map +1 -0
  379. package/dist/skills/render.d.ts +11 -0
  380. package/dist/skills/render.d.ts.map +1 -0
  381. package/dist/skills/render.js +21 -0
  382. package/dist/skills/render.js.map +1 -0
  383. package/dist/skills/resolve-dir.d.ts +20 -0
  384. package/dist/skills/resolve-dir.d.ts.map +1 -0
  385. package/dist/skills/resolve-dir.js +30 -0
  386. package/dist/skills/resolve-dir.js.map +1 -0
  387. package/dist/skills/tool-policy.d.ts +26 -0
  388. package/dist/skills/tool-policy.d.ts.map +1 -0
  389. package/dist/skills/tool-policy.js +33 -0
  390. package/dist/skills/tool-policy.js.map +1 -0
  391. package/dist/skills/types.d.ts +42 -0
  392. package/dist/skills/types.d.ts.map +1 -0
  393. package/dist/skills/types.js +8 -0
  394. package/dist/skills/types.js.map +1 -0
  395. package/dist/stream-format.d.ts +100 -0
  396. package/dist/stream-format.d.ts.map +1 -0
  397. package/dist/stream-format.js +90 -0
  398. package/dist/stream-format.js.map +1 -0
  399. package/dist/telemetry/attributes.d.ts +38 -0
  400. package/dist/telemetry/attributes.d.ts.map +1 -0
  401. package/dist/telemetry/attributes.js +39 -0
  402. package/dist/telemetry/attributes.js.map +1 -0
  403. package/dist/telemetry/config.d.ts +30 -0
  404. package/dist/telemetry/config.d.ts.map +1 -0
  405. package/dist/telemetry/config.js +54 -0
  406. package/dist/telemetry/config.js.map +1 -0
  407. package/dist/telemetry/exporters.d.ts +10 -0
  408. package/dist/telemetry/exporters.d.ts.map +1 -0
  409. package/dist/telemetry/exporters.js +46 -0
  410. package/dist/telemetry/exporters.js.map +1 -0
  411. package/dist/telemetry/index.d.ts +15 -0
  412. package/dist/telemetry/index.d.ts.map +1 -0
  413. package/dist/telemetry/index.js +12 -0
  414. package/dist/telemetry/index.js.map +1 -0
  415. package/dist/telemetry/instrument.d.ts +36 -0
  416. package/dist/telemetry/instrument.d.ts.map +1 -0
  417. package/dist/telemetry/instrument.js +215 -0
  418. package/dist/telemetry/instrument.js.map +1 -0
  419. package/dist/telemetry/metrics.d.ts +45 -0
  420. package/dist/telemetry/metrics.d.ts.map +1 -0
  421. package/dist/telemetry/metrics.js +186 -0
  422. package/dist/telemetry/metrics.js.map +1 -0
  423. package/dist/telemetry/tracer.d.ts +33 -0
  424. package/dist/telemetry/tracer.d.ts.map +1 -0
  425. package/dist/telemetry/tracer.js +162 -0
  426. package/dist/telemetry/tracer.js.map +1 -0
  427. package/dist/tools/__tests__/builtin.test.d.ts +7 -0
  428. package/dist/tools/__tests__/builtin.test.d.ts.map +1 -0
  429. package/dist/tools/__tests__/builtin.test.js +242 -0
  430. package/dist/tools/__tests__/builtin.test.js.map +1 -0
  431. package/dist/tools/builtin.d.ts +29 -0
  432. package/dist/tools/builtin.d.ts.map +1 -0
  433. package/dist/tools/builtin.js +367 -0
  434. package/dist/tools/builtin.js.map +1 -0
  435. package/dist/tools/index.d.ts +4 -0
  436. package/dist/tools/index.d.ts.map +1 -0
  437. package/dist/tools/index.js +3 -0
  438. package/dist/tools/index.js.map +1 -0
  439. package/dist/tools/path-guard.d.ts +23 -0
  440. package/dist/tools/path-guard.d.ts.map +1 -0
  441. package/dist/tools/path-guard.js +66 -0
  442. package/dist/tools/path-guard.js.map +1 -0
  443. package/dist/tools/types.d.ts +45 -0
  444. package/dist/tools/types.d.ts.map +1 -0
  445. package/dist/tools/types.js +11 -0
  446. package/dist/tools/types.js.map +1 -0
  447. package/dist/types.d.ts +249 -0
  448. package/dist/types.d.ts.map +1 -0
  449. package/dist/types.js +22 -0
  450. package/dist/types.js.map +1 -0
  451. package/dist/upstream-error.d.ts +64 -0
  452. package/dist/upstream-error.d.ts.map +1 -0
  453. package/dist/upstream-error.js +105 -0
  454. package/dist/upstream-error.js.map +1 -0
  455. package/dist/version.d.ts +2 -0
  456. package/dist/version.d.ts.map +1 -0
  457. package/dist/version.js +2 -0
  458. package/dist/version.js.map +1 -0
  459. package/package.json +49 -0
@@ -0,0 +1,105 @@
1
+ /**
2
+ * OpenCode Go adapter — agentic HTTPS proxy for OpenCode's subscription
3
+ * service (https://opencode.ai/docs/go).
4
+ *
5
+ * OpenCode Go is a $10/month subscription that exposes a curated set of
6
+ * open coding models (Qwen3.5/3.6 Plus, Kimi K2.5/K2.6, GLM-5/5.1,
7
+ * DeepSeek V4, MiniMax M2.5/M2.7, MiMo …) via an OpenAI-compatible API
8
+ * at `https://opencode.ai/zen/go/v1/`. From our perspective it's just
9
+ * another OpenAI-compatible endpoint — same SSE format, same tool-call
10
+ * delta protocol — so we inherit `OpenRouterAdapter`'s agentic loop
11
+ * and override the config.
12
+ *
13
+ * Models in the registry are addressed as `opencode/<model>` (e.g.
14
+ * `opencode/qwen3.5-plus`). The adapter strips the `opencode/` prefix
15
+ * before sending to the upstream API. No binary, no daemon, no local
16
+ * SDK — just HTTP + an API key from the user's Go subscription.
17
+ *
18
+ * Replaces Ollama as the cheap local-tier provider when the user has
19
+ * a Go subscription: VRAM cost drops from ~12 GB (Ollama hosting
20
+ * qwen3:14b) to 0 GB, RAM cost stays nominal. Inference happens on
21
+ * OpenCode's hosted infrastructure.
22
+ */
23
+ import { OpenRouterAdapter } from './openrouter-adapter.js';
24
+ const PREFIX = 'opencode/';
25
+ /**
26
+ * Pricing fallbacks for known OpenCode Go models. The proxy also
27
+ * returns `usage.cost` directly (mirrors OpenRouter's behavior); when
28
+ * present the inherited base class prefers that over this table. These
29
+ * entries are for the offline path / cost estimates before a call
30
+ * runs. Pricing reflects model-card list rates as of 2026-05.
31
+ */
32
+ const OPENCODE_GO_PRICING = {
33
+ // GLM family
34
+ 'glm-5': [0.50, 2.00],
35
+ 'glm-5.1': [0.60, 2.40],
36
+ // Kimi family
37
+ 'kimi-k2.5': [0.30, 1.20],
38
+ 'kimi-k2.6': [0.50, 2.00],
39
+ // MiMo
40
+ 'mimo-v2-pro': [0.40, 1.60],
41
+ 'mimo-v2-omni': [0.20, 0.80],
42
+ 'mimo-v2.5-pro': [0.40, 1.60],
43
+ 'mimo-v2.5': [0.20, 0.80],
44
+ // MiniMax
45
+ 'minimax-m2.5': [0.10, 0.40],
46
+ 'minimax-m2.7': [0.20, 0.80],
47
+ // Qwen
48
+ 'qwen3.5-plus': [0.05, 0.20],
49
+ 'qwen3.6-plus': [0.20, 0.80],
50
+ // DeepSeek
51
+ 'deepseek-v4-pro': [0.20, 0.80],
52
+ 'deepseek-v4-flash': [0.02, 0.08],
53
+ };
54
+ export class OpenCodeAdapter extends OpenRouterAdapter {
55
+ provider = 'opencode';
56
+ // -- Configuration overrides ----------------------------------------------
57
+ getApiKey() {
58
+ return process.env.OPENCODE_API_KEY;
59
+ }
60
+ getBaseUrl() {
61
+ // Default endpoint per https://opencode.ai/docs/go. Allow override
62
+ // via OPENCODE_BASE_URL so users on different regions or testing
63
+ // against a local opencode serve instance can redirect.
64
+ return (process.env.OPENCODE_BASE_URL ?? 'https://opencode.ai/zen/go/v1').replace(/\/+$/, '');
65
+ }
66
+ getExtraHeaders() {
67
+ return {
68
+ // X-Title shows up in OpenCode's usage console; HTTP-Referer is
69
+ // OpenRouter-specific and not required here.
70
+ 'X-Title': 'Anvil',
71
+ };
72
+ }
73
+ // -- Model id handling ----------------------------------------------------
74
+ supportsModel(modelId) {
75
+ // Registry uses the `opencode/` prefix to disambiguate from
76
+ // OpenRouter's `<provider>/<model>` slugs (which pass the same
77
+ // `includes('/')` test the parent uses).
78
+ return modelId.startsWith(PREFIX);
79
+ }
80
+ getModelPricing(modelId) {
81
+ const stripped = modelId.startsWith(PREFIX) ? modelId.slice(PREFIX.length) : modelId;
82
+ return OPENCODE_GO_PRICING[stripped] ?? null;
83
+ }
84
+ // -- Run override: strip prefix before calling upstream -------------------
85
+ async run(config, output) {
86
+ if (!config.model.startsWith(PREFIX)) {
87
+ // Safety net — caller should always send a properly-prefixed id,
88
+ // but if they don't we tolerate it rather than 500'ing.
89
+ return super.run(config, output);
90
+ }
91
+ const upstreamModel = config.model.slice(PREFIX.length);
92
+ const result = await super.run({ ...config, model: upstreamModel }, output);
93
+ // Re-stamp the result with our prefixed id so downstream telemetry
94
+ // attributes the call to the OpenCode adapter, not OpenRouter.
95
+ return { ...result, provider: this.provider, model: config.model };
96
+ }
97
+ // -- Availability ---------------------------------------------------------
98
+ async checkAvailability() {
99
+ const key = this.getApiKey();
100
+ if (!key)
101
+ return { available: false, error: 'OPENCODE_API_KEY is not set (subscribe at https://opencode.ai/zen)' };
102
+ return { available: true };
103
+ }
104
+ }
105
+ //# sourceMappingURL=opencode-adapter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"opencode-adapter.js","sourceRoot":"","sources":["../src/opencode-adapter.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,69 @@
1
+ /**
2
+ * OpenRouter adapter — agentic OpenAI-compatible client.
3
+ *
4
+ * OpenRouter aggregates many providers (Anthropic / Google / Meta / OpenAI…)
5
+ * behind one OpenAI-compatible API. Models advertise via the `provider/model`
6
+ * id format (e.g. `anthropic/claude-sonnet-4-6`).
7
+ *
8
+ * Two modes, picked at runtime by the presence of `config.toolExecutor`:
9
+ *
10
+ * 1. Single-shot chat — no executor. Drains one /v1/chat/completions
11
+ * response, emits text + result. Backwards-compatible with every
12
+ * caller that existed before agentic support landed.
13
+ *
14
+ * 2. Agentic loop — executor present. Each turn the adapter sends
15
+ * `tools: [...]` (OpenAI function-calling shape), reassembles
16
+ * `tool_calls` from streaming deltas (OpenAI streams arguments
17
+ * piecemeal, indexed), executes via the executor, appends the
18
+ * assistant + tool messages to history, and re-POSTs. Loops up
19
+ * to maxToolIterations (default 32).
20
+ *
21
+ * Capabilities flip to `tier: 'agentic'` because the adapter now drives
22
+ * a full agentic loop, same as OllamaAdapter. Stage-policy.yaml's
23
+ * build/validate/ship can route here when ANVIL_LLM_PROVIDER=openrouter
24
+ * or via per-stage models.yaml entries.
25
+ */
26
+ import type { ModelAdapter, ModelAdapterConfig, ModelAdapterResult, ProviderCapabilities, ProviderName } from './types.js';
27
+ /**
28
+ * Re-export the shared `UpstreamError` so existing imports
29
+ * (`import { UpstreamError } from '@esankhan3/anvil-agent-core/openrouter-adapter'`)
30
+ * keep working. The class itself lives in `upstream-error.ts` and is
31
+ * shared by every adapter that talks to a remote provider.
32
+ */
33
+ export { UpstreamError } from './upstream-error.js';
34
+ export declare class OpenRouterAdapter implements ModelAdapter {
35
+ readonly provider: ProviderName;
36
+ readonly capabilities: ProviderCapabilities;
37
+ /**
38
+ * In-flight AbortControllers — one per concurrent run() invocation.
39
+ * The adapter is a singleton (registered once with ProviderRegistry),
40
+ * but per-repo stages run multiple repos in parallel against it. A
41
+ * single instance-level field would let one call's `finally` block
42
+ * null out the controller while another call is still mid-fetch,
43
+ * causing "Cannot read properties of null (reading 'signal')". The
44
+ * Set lets every concurrent call own its own AbortController without
45
+ * trampling, and `kill()` aborts ALL of them (matches legacy
46
+ * "stop the run" semantics).
47
+ */
48
+ private activeControllers;
49
+ protected getApiKey(): string | undefined;
50
+ protected getBaseUrl(): string;
51
+ protected getExtraHeaders(): Record<string, string>;
52
+ supportsModel(modelId: string): boolean;
53
+ getModelPricing(modelId: string): [number, number] | null;
54
+ checkAvailability(): Promise<{
55
+ available: boolean;
56
+ version?: string;
57
+ error?: string;
58
+ }>;
59
+ kill(): void;
60
+ run(config: ModelAdapterConfig, output: NodeJS.WritableStream): Promise<ModelAdapterResult>;
61
+ private runOneTurn;
62
+ /**
63
+ * Drain the SSE stream. Reassembles `tool_calls` from streamed deltas
64
+ * (OpenAI sends arguments as multiple chunks indexed by tool-call
65
+ * `index`). Emits text deltas to the output stream as they arrive.
66
+ */
67
+ private consumeSSE;
68
+ }
69
+ //# sourceMappingURL=openrouter-adapter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"openrouter-adapter.d.ts","sourceRoot":"","sources":["../src/openrouter-adapter.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAGH,OAAO,KAAK,EACV,YAAY,EACZ,kBAAkB,EAClB,kBAAkB,EAClB,oBAAoB,EACpB,YAAY,EAIb,MAAM,YAAY,CAAC;AAKpB;;;;;GAKG;AACH,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAoHpD,qBAAa,iBAAkB,YAAW,YAAY;IACpD,QAAQ,CAAC,QAAQ,EAAE,YAAY,CAAgB;IAE/C,QAAQ,CAAC,YAAY,EAAE,oBAAoB,CAYzC;IAEF;;;;;;;;;;OAUG;IACH,OAAO,CAAC,iBAAiB,CAA8B;IAIvD,SAAS,CAAC,SAAS,IAAI,MAAM,GAAG,SAAS;IAIzC,SAAS,CAAC,UAAU,IAAI,MAAM;IAI9B,SAAS,CAAC,eAAe,IAAI,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC;IAWnD,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO;IAMvC,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,IAAI;IAInD,iBAAiB,IAAI,OAAO,CAAC;QAAE,SAAS,EAAE,OAAO,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IAM5F,IAAI,IAAI,IAAI;IAON,GAAG,CAAC,MAAM,EAAE,kBAAkB,EAAE,MAAM,EAAE,MAAM,CAAC,cAAc,GAAG,OAAO,CAAC,kBAAkB,CAAC;YA+HnF,UAAU;IAsDxB;;;;OAIG;YACW,UAAU;CAgKzB"}
@@ -0,0 +1,481 @@
1
+ /**
2
+ * OpenRouter adapter — agentic OpenAI-compatible client.
3
+ *
4
+ * OpenRouter aggregates many providers (Anthropic / Google / Meta / OpenAI…)
5
+ * behind one OpenAI-compatible API. Models advertise via the `provider/model`
6
+ * id format (e.g. `anthropic/claude-sonnet-4-6`).
7
+ *
8
+ * Two modes, picked at runtime by the presence of `config.toolExecutor`:
9
+ *
10
+ * 1. Single-shot chat — no executor. Drains one /v1/chat/completions
11
+ * response, emits text + result. Backwards-compatible with every
12
+ * caller that existed before agentic support landed.
13
+ *
14
+ * 2. Agentic loop — executor present. Each turn the adapter sends
15
+ * `tools: [...]` (OpenAI function-calling shape), reassembles
16
+ * `tool_calls` from streaming deltas (OpenAI streams arguments
17
+ * piecemeal, indexed), executes via the executor, appends the
18
+ * assistant + tool messages to history, and re-POSTs. Loops up
19
+ * to maxToolIterations (default 32).
20
+ *
21
+ * Capabilities flip to `tier: 'agentic'` because the adapter now drives
22
+ * a full agentic loop, same as OllamaAdapter. Stage-policy.yaml's
23
+ * build/validate/ship can route here when ANVIL_LLM_PROVIDER=openrouter
24
+ * or via per-stage models.yaml entries.
25
+ */
26
+ import { randomUUID } from 'node:crypto';
27
+ import { emitContent, emitResult, emitToolResult, emitToolUse } from './stream-format.js';
28
+ const DEFAULT_MAX_ITERATIONS = 32;
29
+ /**
30
+ * Re-export the shared `UpstreamError` so existing imports
31
+ * (`import { UpstreamError } from '@esankhan3/anvil-agent-core/openrouter-adapter'`)
32
+ * keep working. The class itself lives in `upstream-error.ts` and is
33
+ * shared by every adapter that talks to a remote provider.
34
+ */
35
+ export { UpstreamError } from './upstream-error.js';
36
+ import { UpstreamError as _UpstreamError } from './upstream-error.js';
37
+ // ───────────────────────────────────────────────────────────────────────
38
+ // Pricing fallbacks for well-known OpenRouter slugs. OpenRouter itself
39
+ // returns `usage.cost` in the response when available — that wins. This
40
+ // table is just a fallback for older API versions that don't report it.
41
+ // Price = [inputPer1MTokens, outputPer1MTokens]
42
+ // ───────────────────────────────────────────────────────────────────────
43
+ const OPENROUTER_PRICING = {
44
+ // OpenAI
45
+ 'openai/gpt-4o': [2.5, 10.0],
46
+ 'openai/gpt-4o-mini': [0.15, 0.60],
47
+ 'openai/gpt-4-turbo': [10.0, 30.0],
48
+ 'openai/o1': [15.0, 60.0],
49
+ 'openai/o3': [10.0, 40.0],
50
+ 'openai/o3-mini': [1.1, 4.4],
51
+ // Anthropic — current generation
52
+ 'anthropic/claude-haiku-4-5': [1.0, 5.0],
53
+ 'anthropic/claude-sonnet-4-6': [3.0, 15.0],
54
+ 'anthropic/claude-sonnet-4-7': [3.0, 15.0],
55
+ 'anthropic/claude-opus-4-7': [15.0, 75.0],
56
+ // Google
57
+ 'google/gemini-2.5-flash': [0.30, 2.50],
58
+ 'google/gemini-2.5-pro': [1.25, 10.0],
59
+ // Meta
60
+ 'meta-llama/llama-3.3-70b-instruct': [0.59, 0.79],
61
+ // DeepSeek
62
+ 'deepseek/deepseek-r1': [0.55, 2.19],
63
+ };
64
+ // ───────────────────────────────────────────────────────────────────────
65
+ // Adapter
66
+ // ───────────────────────────────────────────────────────────────────────
67
+ export class OpenRouterAdapter {
68
+ provider = 'openrouter';
69
+ capabilities = {
70
+ tier: 'agentic',
71
+ streaming: true,
72
+ toolUse: true,
73
+ fileSystem: true,
74
+ shellExecution: true,
75
+ sessionResume: false,
76
+ promptCaching: true, // OpenRouter passes through provider caching
77
+ cache: 'auto',
78
+ cacheTtlSeconds: 600,
79
+ structuredOutput: 'strict',
80
+ maxOutputTokens: true,
81
+ };
82
+ /**
83
+ * In-flight AbortControllers — one per concurrent run() invocation.
84
+ * The adapter is a singleton (registered once with ProviderRegistry),
85
+ * but per-repo stages run multiple repos in parallel against it. A
86
+ * single instance-level field would let one call's `finally` block
87
+ * null out the controller while another call is still mid-fetch,
88
+ * causing "Cannot read properties of null (reading 'signal')". The
89
+ * Set lets every concurrent call own its own AbortController without
90
+ * trampling, and `kill()` aborts ALL of them (matches legacy
91
+ * "stop the run" semantics).
92
+ */
93
+ activeControllers = new Set();
94
+ // -- Configuration --------------------------------------------------------
95
+ getApiKey() {
96
+ return process.env.OPENROUTER_API_KEY;
97
+ }
98
+ getBaseUrl() {
99
+ return (process.env.OPENROUTER_BASE_URL ?? 'https://openrouter.ai/api/v1').replace(/\/+$/, '');
100
+ }
101
+ getExtraHeaders() {
102
+ return {
103
+ // OpenRouter-recommended attribution headers — surface in their
104
+ // analytics and rankings dashboards.
105
+ 'HTTP-Referer': 'https://anvil.dev',
106
+ 'X-Title': 'Anvil',
107
+ };
108
+ }
109
+ // -- ModelAdapter interface -----------------------------------------------
110
+ supportsModel(modelId) {
111
+ // OpenRouter slugs are `provider/model`. Catches Anthropic, OpenAI,
112
+ // Google, Meta, etc. without ambiguity vs other adapters.
113
+ return modelId.includes('/');
114
+ }
115
+ getModelPricing(modelId) {
116
+ return OPENROUTER_PRICING[modelId] ?? null;
117
+ }
118
+ async checkAvailability() {
119
+ const key = this.getApiKey();
120
+ if (!key)
121
+ return { available: false, error: 'OPENROUTER_API_KEY is not set' };
122
+ return { available: true };
123
+ }
124
+ kill() {
125
+ for (const ac of this.activeControllers) {
126
+ try {
127
+ ac.abort();
128
+ }
129
+ catch { /* already aborted */ }
130
+ }
131
+ this.activeControllers.clear();
132
+ }
133
+ async run(config, output) {
134
+ const apiKey = this.getApiKey();
135
+ if (!apiKey)
136
+ throw new Error('OPENROUTER_API_KEY is not set');
137
+ const abortController = new AbortController();
138
+ this.activeControllers.add(abortController);
139
+ const startMs = Date.now();
140
+ const messages = [];
141
+ if (config.projectPrompt)
142
+ messages.push({ role: 'system', content: config.projectPrompt });
143
+ messages.push({ role: 'user', content: config.userPrompt });
144
+ const tools = config.toolExecutor ? mapSchemasToOpenAI(config.toolExecutor.listSchemas()) : undefined;
145
+ const maxIter = config.maxToolIterations ?? DEFAULT_MAX_ITERATIONS;
146
+ let aggregatedText = '';
147
+ let totalIn = 0;
148
+ let totalOut = 0;
149
+ let totalCachedReadTokens = 0;
150
+ let totalReasoningTokens = 0;
151
+ let openRouterCost = 0;
152
+ let stopReason;
153
+ let providerFinishReason;
154
+ try {
155
+ for (let iter = 0; iter < maxIter; iter++) {
156
+ const turn = await this.runOneTurn(apiKey, messages, tools, config, output, abortController.signal);
157
+ aggregatedText += turn.text;
158
+ totalIn += turn.inputTokens;
159
+ totalOut += turn.outputTokens;
160
+ totalCachedReadTokens += turn.cachedReadTokens;
161
+ totalReasoningTokens += turn.reasoningTokens;
162
+ openRouterCost += turn.cost;
163
+ if (turn.finishReason)
164
+ providerFinishReason = turn.finishReason;
165
+ if (turn.toolCalls.length === 0 || !config.toolExecutor) {
166
+ stopReason = turn.toolCalls.length === 0 ? 'end_turn' : 'tools_unsupported';
167
+ break;
168
+ }
169
+ // Append assistant turn to history so the model sees its own
170
+ // tool_calls when we re-prompt with results. Thinking models
171
+ // (DeepSeek V4, Kimi K2, GLM thinking variants…) require the
172
+ // reasoning trace echoed back — without it they 400 with
173
+ // "reasoning_content is missing in assistant tool call".
174
+ // Send both `reasoning` (string) and `reasoning_details`
175
+ // (structured) so OpenRouter can map to whichever shape the
176
+ // upstream provider expects.
177
+ messages.push({
178
+ role: 'assistant',
179
+ content: turn.text || null,
180
+ ...(turn.reasoning ? { reasoning: turn.reasoning } : {}),
181
+ ...(turn.reasoningDetails.length > 0 ? { reasoning_details: turn.reasoningDetails } : {}),
182
+ tool_calls: turn.toolCalls,
183
+ });
184
+ for (const tc of turn.toolCalls) {
185
+ const args = parseArgs(tc.function.arguments);
186
+ const call = { id: tc.id, name: tc.function.name, arguments: args };
187
+ emitToolUse(output, call.name, args, tc.id);
188
+ const result = await invokeTool(config.toolExecutor, call, config.workingDir, abortController.signal);
189
+ emitToolResult(output, { toolUseId: tc.id, content: result.content, isError: result.isError });
190
+ messages.push({
191
+ role: 'tool',
192
+ content: result.content,
193
+ tool_call_id: tc.id,
194
+ });
195
+ }
196
+ if (abortController.signal.aborted) {
197
+ stopReason = 'aborted';
198
+ break;
199
+ }
200
+ }
201
+ if (stopReason === undefined)
202
+ stopReason = 'iteration_limit';
203
+ }
204
+ finally {
205
+ this.activeControllers.delete(abortController);
206
+ }
207
+ const durationMs = Date.now() - startMs;
208
+ // Cost: prefer OpenRouter's reported cost (covers all providers it
209
+ // knows). Fall back to our pricing table when absent. When the table
210
+ // also misses, costUsd = 0 and downstream telemetry will catch the
211
+ // gap via the central cost table in cost.ts.
212
+ let costUsd = openRouterCost;
213
+ if (costUsd === 0) {
214
+ const pricing = this.getModelPricing(config.model);
215
+ if (pricing) {
216
+ costUsd = (totalIn / 1_000_000) * pricing[0] + (totalOut / 1_000_000) * pricing[1];
217
+ }
218
+ }
219
+ // Normalize length finish-reason like OpenAIAdapter does so callers
220
+ // can detect truncation provider-agnostically.
221
+ const normalizedStopReason = stopReason === 'end_turn' && providerFinishReason === 'length'
222
+ ? 'max_tokens'
223
+ : stopReason;
224
+ emitResult(output, {
225
+ text: aggregatedText,
226
+ costUsd,
227
+ inputTokens: totalIn,
228
+ outputTokens: totalOut,
229
+ durationMs,
230
+ cacheReadTokens: totalCachedReadTokens,
231
+ });
232
+ return {
233
+ output: aggregatedText,
234
+ inputTokens: totalIn,
235
+ outputTokens: totalOut,
236
+ costUsd,
237
+ durationMs,
238
+ provider: this.provider,
239
+ model: config.model,
240
+ stopReason: normalizedStopReason,
241
+ cacheReadTokens: totalCachedReadTokens || undefined,
242
+ reasoningTokens: totalReasoningTokens || undefined,
243
+ toolCallCount: countToolCalls(messages),
244
+ };
245
+ }
246
+ // -- Per-turn round-trip --------------------------------------------------
247
+ async runOneTurn(apiKey, messages, tools, config, output, signal) {
248
+ const url = `${this.getBaseUrl()}/chat/completions`;
249
+ const body = {
250
+ model: config.model,
251
+ messages,
252
+ stream: true,
253
+ stream_options: { include_usage: true },
254
+ };
255
+ if (tools && tools.length > 0) {
256
+ body.tools = tools;
257
+ body.tool_choice = 'auto';
258
+ }
259
+ if (typeof config.maxOutputTokens === 'number' && config.maxOutputTokens > 0) {
260
+ body.max_tokens = config.maxOutputTokens;
261
+ }
262
+ const response = await fetch(url, {
263
+ method: 'POST',
264
+ headers: {
265
+ 'Content-Type': 'application/json',
266
+ Authorization: `Bearer ${apiKey}`,
267
+ ...this.getExtraHeaders(),
268
+ },
269
+ body: JSON.stringify(body),
270
+ signal,
271
+ });
272
+ if (!response.ok) {
273
+ const errBody = await response.text();
274
+ throw new _UpstreamError(response.status, errBody, { provider: this.provider });
275
+ }
276
+ if (!response.body)
277
+ throw new _UpstreamError(0, `${this.provider} returned no response body`, { provider: this.provider });
278
+ return this.consumeSSE(response, output);
279
+ }
280
+ /**
281
+ * Drain the SSE stream. Reassembles `tool_calls` from streamed deltas
282
+ * (OpenAI sends arguments as multiple chunks indexed by tool-call
283
+ * `index`). Emits text deltas to the output stream as they arrive.
284
+ */
285
+ async consumeSSE(response, output) {
286
+ let text = '';
287
+ let reasoning = '';
288
+ const reasoningDetails = [];
289
+ let inputTokens = 0;
290
+ let outputTokens = 0;
291
+ let cachedReadTokens = 0;
292
+ let reasoningTokens = 0;
293
+ let cost = 0;
294
+ let finishReason;
295
+ const toolCallsByIndex = new Map();
296
+ const reader = response.body.getReader();
297
+ const decoder = new TextDecoder();
298
+ let buffer = '';
299
+ // Buffer text deltas and flush at natural breaks (newline OR every
300
+ // ~80 chars). Without this, OpenAI-compatible streams that emit
301
+ // one token per chunk produce one activity card per word in the
302
+ // dashboard — every "passes", "(", "all", ")" gets its own row.
303
+ // Flushing on `\n` or threshold gives readable line-sized chunks.
304
+ let pendingText = '';
305
+ const FLUSH_THRESHOLD = 80;
306
+ const flushPending = () => {
307
+ if (pendingText.length === 0)
308
+ return;
309
+ emitContent(output, pendingText);
310
+ pendingText = '';
311
+ };
312
+ while (true) {
313
+ const { done, value } = await reader.read();
314
+ if (done)
315
+ break;
316
+ buffer += decoder.decode(value, { stream: true });
317
+ const lines = buffer.split('\n');
318
+ buffer = lines.pop() ?? '';
319
+ for (const line of lines) {
320
+ const trimmed = line.trim();
321
+ if (!trimmed || trimmed.startsWith(':'))
322
+ continue; // empty / SSE comment
323
+ if (!trimmed.startsWith('data: '))
324
+ continue;
325
+ const payload = trimmed.slice(6);
326
+ if (payload === '[DONE]')
327
+ continue;
328
+ let chunk;
329
+ try {
330
+ chunk = JSON.parse(payload);
331
+ }
332
+ catch {
333
+ continue;
334
+ }
335
+ const choice = chunk.choices?.[0];
336
+ const delta = choice?.delta;
337
+ if (delta?.content) {
338
+ text += delta.content;
339
+ pendingText += delta.content;
340
+ // Split the accumulated buffer on newlines so the flush is
341
+ // always anchored at a natural line boundary; emit each
342
+ // complete line, retain the trailing partial line.
343
+ while (pendingText.includes('\n')) {
344
+ const nl = pendingText.indexOf('\n');
345
+ const line = pendingText.slice(0, nl + 1);
346
+ emitContent(output, line);
347
+ pendingText = pendingText.slice(nl + 1);
348
+ }
349
+ // No newline yet but buffer is large — flush so very long
350
+ // single-line outputs (e.g. one paragraph) don't sit
351
+ // pending until the very end of the stream.
352
+ if (pendingText.length >= FLUSH_THRESHOLD) {
353
+ emitContent(output, pendingText);
354
+ pendingText = '';
355
+ }
356
+ }
357
+ if (delta?.reasoning) {
358
+ // Accumulate but don't stream to user — reasoning is for the
359
+ // upstream's continuation, not for the dashboard activity log.
360
+ reasoning += delta.reasoning;
361
+ }
362
+ if (delta?.reasoning_details && Array.isArray(delta.reasoning_details)) {
363
+ for (const rd of delta.reasoning_details) {
364
+ // Merge by index — like tool_calls, reasoning_details are
365
+ // streamed piecewise and grouped by `index`.
366
+ const idx = typeof rd.index === 'number' ? rd.index : reasoningDetails.length;
367
+ const existing = reasoningDetails[idx];
368
+ if (!existing) {
369
+ reasoningDetails[idx] = { ...rd, text: rd.text ?? '' };
370
+ }
371
+ else if (rd.text) {
372
+ existing.text = (existing.text ?? '') + rd.text;
373
+ }
374
+ }
375
+ }
376
+ if (delta?.tool_calls) {
377
+ for (const tcDelta of delta.tool_calls) {
378
+ const existing = toolCallsByIndex.get(tcDelta.index);
379
+ if (!existing) {
380
+ toolCallsByIndex.set(tcDelta.index, {
381
+ id: tcDelta.id ?? randomUUID(),
382
+ type: 'function',
383
+ function: {
384
+ name: tcDelta.function?.name ?? '',
385
+ arguments: tcDelta.function?.arguments ?? '',
386
+ },
387
+ });
388
+ }
389
+ else {
390
+ if (tcDelta.id)
391
+ existing.id = tcDelta.id;
392
+ if (tcDelta.function?.name)
393
+ existing.function.name += tcDelta.function.name;
394
+ if (tcDelta.function?.arguments)
395
+ existing.function.arguments += tcDelta.function.arguments;
396
+ }
397
+ }
398
+ }
399
+ if (typeof choice?.finish_reason === 'string') {
400
+ finishReason = choice.finish_reason;
401
+ }
402
+ if (chunk.usage) {
403
+ inputTokens = chunk.usage.prompt_tokens ?? inputTokens;
404
+ outputTokens = chunk.usage.completion_tokens ?? outputTokens;
405
+ cachedReadTokens = chunk.usage.prompt_tokens_details?.cached_tokens ?? cachedReadTokens;
406
+ reasoningTokens = chunk.usage.completion_tokens_details?.reasoning_tokens ?? reasoningTokens;
407
+ if (typeof chunk.usage.cost === 'number')
408
+ cost = chunk.usage.cost;
409
+ }
410
+ }
411
+ }
412
+ // Flush any trailing content that didn't end on a newline so the
413
+ // last line of the response surfaces in the activity log.
414
+ flushPending();
415
+ // Sort tool_calls by their original index so loop dispatch order
416
+ // mirrors what the model intended.
417
+ const toolCalls = [...toolCallsByIndex.entries()]
418
+ .sort(([a], [b]) => a - b)
419
+ .map(([, v]) => v);
420
+ return {
421
+ text,
422
+ reasoning,
423
+ reasoningDetails: reasoningDetails.filter(Boolean),
424
+ toolCalls,
425
+ inputTokens,
426
+ outputTokens,
427
+ cachedReadTokens,
428
+ reasoningTokens,
429
+ cost,
430
+ finishReason,
431
+ };
432
+ }
433
+ }
434
+ // ───────────────────────────────────────────────────────────────────────
435
+ // Helpers
436
+ // ───────────────────────────────────────────────────────────────────────
437
+ function mapSchemasToOpenAI(schemas) {
438
+ return schemas.map((s) => ({
439
+ type: 'function',
440
+ function: {
441
+ name: s.name,
442
+ description: s.description,
443
+ parameters: s.inputSchema,
444
+ },
445
+ }));
446
+ }
447
+ /**
448
+ * OpenAI streams `tool_calls.arguments` as a JSON-encoded string built
449
+ * up across many deltas. Parse it, fall back to `{ _raw }` when the
450
+ * model emitted malformed JSON so the executor still gets a chance to
451
+ * react.
452
+ */
453
+ function parseArgs(raw) {
454
+ if (!raw)
455
+ return {};
456
+ try {
457
+ const parsed = JSON.parse(raw);
458
+ return typeof parsed === 'object' && parsed !== null ? parsed : { _raw: raw };
459
+ }
460
+ catch {
461
+ return { _raw: raw };
462
+ }
463
+ }
464
+ async function invokeTool(executor, call, workingDir, abortSignal) {
465
+ try {
466
+ return await executor.execute(call, { workingDir, abortSignal });
467
+ }
468
+ catch (err) {
469
+ const msg = err instanceof Error ? err.message : String(err);
470
+ return { content: `Tool execution threw: ${msg}`, isError: true };
471
+ }
472
+ }
473
+ function countToolCalls(messages) {
474
+ let n = 0;
475
+ for (const m of messages) {
476
+ if (m.role === 'assistant' && m.tool_calls)
477
+ n += m.tool_calls.length;
478
+ }
479
+ return n;
480
+ }
481
+ //# sourceMappingURL=openrouter-adapter.js.map