@plumbus/core 0.3.2 → 0.4.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 (572) hide show
  1. package/README.md +2 -2
  2. package/bin/plumbus.js +16 -2
  3. package/dist/.tsbuildinfo +1 -1
  4. package/dist/ai/ai-service.d.ts.map +1 -1
  5. package/dist/ai/ai-service.js +8 -3
  6. package/dist/ai/ai-service.js.map +1 -1
  7. package/dist/ai/provider.d.ts.map +1 -1
  8. package/dist/ai/provider.js +1 -2
  9. package/dist/ai/provider.js.map +1 -1
  10. package/dist/ai/rag/pipeline.d.ts +4 -0
  11. package/dist/ai/rag/pipeline.d.ts.map +1 -1
  12. package/dist/ai/rag/pipeline.js +17 -2
  13. package/dist/ai/rag/pipeline.js.map +1 -1
  14. package/dist/api/route-generator.d.ts.map +1 -1
  15. package/dist/api/route-generator.js +10 -6
  16. package/dist/api/route-generator.js.map +1 -1
  17. package/dist/auth/adapter.d.ts +0 -4
  18. package/dist/auth/adapter.d.ts.map +1 -1
  19. package/dist/auth/adapter.js +4 -0
  20. package/dist/auth/adapter.js.map +1 -1
  21. package/dist/cli/cli.d.ts.map +1 -1
  22. package/dist/cli/cli.js +2 -1
  23. package/dist/cli/cli.js.map +1 -1
  24. package/dist/cli/commands/dev.d.ts +4 -1
  25. package/dist/cli/commands/dev.d.ts.map +1 -1
  26. package/dist/cli/commands/dev.js +21 -30
  27. package/dist/cli/commands/dev.js.map +1 -1
  28. package/dist/cli/commands/doctor.d.ts +6 -0
  29. package/dist/cli/commands/doctor.d.ts.map +1 -1
  30. package/dist/cli/commands/doctor.js +146 -2
  31. package/dist/cli/commands/doctor.js.map +1 -1
  32. package/dist/cli/commands/generate.d.ts.map +1 -1
  33. package/dist/cli/commands/generate.js +18 -0
  34. package/dist/cli/commands/generate.js.map +1 -1
  35. package/dist/cli/commands/index.d.ts +1 -0
  36. package/dist/cli/commands/index.d.ts.map +1 -1
  37. package/dist/cli/commands/index.js +1 -0
  38. package/dist/cli/commands/index.js.map +1 -1
  39. package/dist/cli/commands/init.d.ts +1 -1
  40. package/dist/cli/commands/init.d.ts.map +1 -1
  41. package/dist/cli/commands/init.js +113 -3
  42. package/dist/cli/commands/init.js.map +1 -1
  43. package/dist/cli/commands/mcp.d.ts +9 -0
  44. package/dist/cli/commands/mcp.d.ts.map +1 -0
  45. package/dist/cli/commands/mcp.js +125 -0
  46. package/dist/cli/commands/mcp.js.map +1 -0
  47. package/dist/cli/commands/migrate.d.ts.map +1 -1
  48. package/dist/cli/commands/migrate.js +2 -1
  49. package/dist/cli/commands/migrate.js.map +1 -1
  50. package/dist/cli/commands/start.d.ts +4 -1
  51. package/dist/cli/commands/start.d.ts.map +1 -1
  52. package/dist/cli/commands/start.js +20 -29
  53. package/dist/cli/commands/start.js.map +1 -1
  54. package/dist/cli/commands/translation.d.ts.map +1 -1
  55. package/dist/cli/commands/translation.js +22 -10
  56. package/dist/cli/commands/translation.js.map +1 -1
  57. package/dist/cli/mcp-serve-context.d.ts +13 -0
  58. package/dist/cli/mcp-serve-context.d.ts.map +1 -0
  59. package/dist/cli/mcp-serve-context.js +119 -0
  60. package/dist/cli/mcp-serve-context.js.map +1 -0
  61. package/dist/cli/migrate-snapshot-schema.d.ts +15 -0
  62. package/dist/cli/migrate-snapshot-schema.d.ts.map +1 -0
  63. package/dist/cli/migrate-snapshot-schema.js +23 -0
  64. package/dist/cli/migrate-snapshot-schema.js.map +1 -0
  65. package/dist/cli/project-root.d.ts +9 -0
  66. package/dist/cli/project-root.d.ts.map +1 -0
  67. package/dist/cli/project-root.js +41 -0
  68. package/dist/cli/project-root.js.map +1 -0
  69. package/dist/cli/templates/resources.d.ts.map +1 -1
  70. package/dist/cli/templates/resources.js +7 -1
  71. package/dist/cli/templates/resources.js.map +1 -1
  72. package/dist/config/loader.d.ts +1 -1
  73. package/dist/config/loader.d.ts.map +1 -1
  74. package/dist/config/loader.js +46 -27
  75. package/dist/config/loader.js.map +1 -1
  76. package/dist/data/connection.d.ts +16 -0
  77. package/dist/data/connection.d.ts.map +1 -0
  78. package/dist/data/connection.js +27 -0
  79. package/dist/data/connection.js.map +1 -0
  80. package/dist/data/index.d.ts +1 -0
  81. package/dist/data/index.d.ts.map +1 -1
  82. package/dist/data/index.js +1 -0
  83. package/dist/data/index.js.map +1 -1
  84. package/dist/data/migration.d.ts.map +1 -1
  85. package/dist/data/migration.js +34 -6
  86. package/dist/data/migration.js.map +1 -1
  87. package/dist/data/repository.d.ts.map +1 -1
  88. package/dist/data/repository.js +26 -9
  89. package/dist/data/repository.js.map +1 -1
  90. package/dist/define/defineCapability.d.ts +4 -2
  91. package/dist/define/defineCapability.d.ts.map +1 -1
  92. package/dist/define/defineCapability.js +68 -8
  93. package/dist/define/defineCapability.js.map +1 -1
  94. package/dist/define/defineEntity.d.ts.map +1 -1
  95. package/dist/define/defineEntity.js +8 -4
  96. package/dist/define/defineEntity.js.map +1 -1
  97. package/dist/define/defineEvent.d.ts.map +1 -1
  98. package/dist/define/defineEvent.js +7 -3
  99. package/dist/define/defineEvent.js.map +1 -1
  100. package/dist/define/defineFlow.d.ts.map +1 -1
  101. package/dist/define/defineFlow.js +11 -5
  102. package/dist/define/defineFlow.js.map +1 -1
  103. package/dist/define/definePrompt.d.ts.map +1 -1
  104. package/dist/define/definePrompt.js +11 -5
  105. package/dist/define/definePrompt.js.map +1 -1
  106. package/dist/define/defineTranslation.d.ts.map +1 -1
  107. package/dist/define/defineTranslation.js +12 -10
  108. package/dist/define/defineTranslation.js.map +1 -1
  109. package/dist/define/validation-error.d.ts +2 -0
  110. package/dist/define/validation-error.d.ts.map +1 -0
  111. package/dist/define/validation-error.js +9 -0
  112. package/dist/define/validation-error.js.map +1 -0
  113. package/dist/errors/hints.d.ts +13 -0
  114. package/dist/errors/hints.d.ts.map +1 -0
  115. package/dist/errors/hints.js +15 -0
  116. package/dist/errors/hints.js.map +1 -0
  117. package/dist/errors/hook-log.d.ts +3 -0
  118. package/dist/errors/hook-log.d.ts.map +1 -0
  119. package/dist/errors/hook-log.js +6 -0
  120. package/dist/errors/hook-log.js.map +1 -0
  121. package/dist/errors/http.d.ts +18 -0
  122. package/dist/errors/http.d.ts.map +1 -1
  123. package/dist/errors/http.js +32 -2
  124. package/dist/errors/http.js.map +1 -1
  125. package/dist/errors/safe-metadata.d.ts +5 -0
  126. package/dist/errors/safe-metadata.d.ts.map +1 -0
  127. package/dist/errors/safe-metadata.js +24 -0
  128. package/dist/errors/safe-metadata.js.map +1 -0
  129. package/dist/execution/auth-roles.d.ts +6 -0
  130. package/dist/execution/auth-roles.d.ts.map +1 -0
  131. package/dist/execution/auth-roles.js +12 -0
  132. package/dist/execution/auth-roles.js.map +1 -0
  133. package/dist/execution/authorization.d.ts.map +1 -1
  134. package/dist/execution/authorization.js +3 -5
  135. package/dist/execution/authorization.js.map +1 -1
  136. package/dist/execution/context-factory.d.ts +2 -17
  137. package/dist/execution/context-factory.d.ts.map +1 -1
  138. package/dist/execution/context-factory.js +1 -0
  139. package/dist/execution/context-factory.js.map +1 -1
  140. package/dist/flows/engine.js +1 -1
  141. package/dist/flows/engine.js.map +1 -1
  142. package/dist/flows/evaluate-condition.d.ts +19 -0
  143. package/dist/flows/evaluate-condition.d.ts.map +1 -0
  144. package/dist/flows/evaluate-condition.js +276 -0
  145. package/dist/flows/evaluate-condition.js.map +1 -0
  146. package/dist/flows/index.d.ts +1 -0
  147. package/dist/flows/index.d.ts.map +1 -1
  148. package/dist/flows/index.js +2 -0
  149. package/dist/flows/index.js.map +1 -1
  150. package/dist/flows/step-executor.d.ts.map +1 -1
  151. package/dist/flows/step-executor.js +9 -2
  152. package/dist/flows/step-executor.js.map +1 -1
  153. package/dist/governance/rules/architecture.d.ts.map +1 -1
  154. package/dist/governance/rules/architecture.js +2 -0
  155. package/dist/governance/rules/architecture.js.map +1 -1
  156. package/dist/governance/rules/index.d.ts +1 -0
  157. package/dist/governance/rules/index.d.ts.map +1 -1
  158. package/dist/governance/rules/index.js +1 -0
  159. package/dist/governance/rules/index.js.map +1 -1
  160. package/dist/governance/rules/mcp.d.ts +5 -0
  161. package/dist/governance/rules/mcp.d.ts.map +1 -0
  162. package/dist/governance/rules/mcp.js +34 -0
  163. package/dist/governance/rules/mcp.js.map +1 -0
  164. package/dist/index.d.ts +1 -0
  165. package/dist/index.d.ts.map +1 -1
  166. package/dist/index.js +1 -0
  167. package/dist/index.js.map +1 -1
  168. package/dist/mcp/exposure.d.ts +4 -0
  169. package/dist/mcp/exposure.d.ts.map +1 -0
  170. package/dist/mcp/exposure.js +5 -0
  171. package/dist/mcp/exposure.js.map +1 -0
  172. package/dist/mcp/index.d.ts +4 -0
  173. package/dist/mcp/index.d.ts.map +1 -0
  174. package/dist/mcp/index.js +6 -0
  175. package/dist/mcp/index.js.map +1 -0
  176. package/dist/mcp/manifest-generator.d.ts +19 -0
  177. package/dist/mcp/manifest-generator.d.ts.map +1 -0
  178. package/dist/mcp/manifest-generator.js +29 -0
  179. package/dist/mcp/manifest-generator.js.map +1 -0
  180. package/dist/mcp/skill-generator.d.ts +4 -0
  181. package/dist/mcp/skill-generator.d.ts.map +1 -0
  182. package/dist/mcp/skill-generator.js +59 -0
  183. package/dist/mcp/skill-generator.js.map +1 -0
  184. package/dist/schema/zod-input-to-json-schema.d.ts +4 -0
  185. package/dist/schema/zod-input-to-json-schema.d.ts.map +1 -0
  186. package/dist/schema/zod-input-to-json-schema.js +6 -0
  187. package/dist/schema/zod-input-to-json-schema.js.map +1 -0
  188. package/dist/server/bootstrap.d.ts.map +1 -1
  189. package/dist/server/bootstrap.js +5 -2
  190. package/dist/server/bootstrap.js.map +1 -1
  191. package/dist/testing/context.d.ts +2 -0
  192. package/dist/testing/context.d.ts.map +1 -1
  193. package/dist/testing/context.js +1 -0
  194. package/dist/testing/context.js.map +1 -1
  195. package/dist/testing/e2e.js +1 -1
  196. package/dist/testing/e2e.js.map +1 -1
  197. package/dist/types/capability.d.ts +13 -0
  198. package/dist/types/capability.d.ts.map +1 -1
  199. package/dist/types/config.d.ts +9 -0
  200. package/dist/types/config.d.ts.map +1 -1
  201. package/dist/types/context.d.ts +31 -0
  202. package/dist/types/context.d.ts.map +1 -1
  203. package/dist/types/deep-freeze.d.ts +6 -0
  204. package/dist/types/deep-freeze.d.ts.map +1 -0
  205. package/dist/types/deep-freeze.js +38 -0
  206. package/dist/types/deep-freeze.js.map +1 -0
  207. package/dist/types/enums.d.ts +5 -0
  208. package/dist/types/enums.d.ts.map +1 -1
  209. package/dist/types/enums.js +5 -0
  210. package/dist/types/enums.js.map +1 -1
  211. package/dist/types/index.d.ts +2 -2
  212. package/dist/types/index.d.ts.map +1 -1
  213. package/dist/types/index.js.map +1 -1
  214. package/dist/types/security.d.ts +3 -1
  215. package/dist/types/security.d.ts.map +1 -1
  216. package/instructions/capabilities.md +36 -0
  217. package/instructions/cli.md +44 -0
  218. package/instructions/framework.md +20 -4
  219. package/instructions/mcp.md +20 -0
  220. package/package.json +18 -2
  221. package/dist/ai/__tests__/ai-service.test.d.ts +0 -2
  222. package/dist/ai/__tests__/ai-service.test.d.ts.map +0 -1
  223. package/dist/ai/__tests__/ai-service.test.js +0 -1078
  224. package/dist/ai/__tests__/ai-service.test.js.map +0 -1
  225. package/dist/ai/__tests__/chunking.test.d.ts +0 -2
  226. package/dist/ai/__tests__/chunking.test.d.ts.map +0 -1
  227. package/dist/ai/__tests__/chunking.test.js +0 -78
  228. package/dist/ai/__tests__/chunking.test.js.map +0 -1
  229. package/dist/ai/__tests__/cost-tracker.test.d.ts +0 -2
  230. package/dist/ai/__tests__/cost-tracker.test.d.ts.map +0 -1
  231. package/dist/ai/__tests__/cost-tracker.test.js +0 -201
  232. package/dist/ai/__tests__/cost-tracker.test.js.map +0 -1
  233. package/dist/ai/__tests__/explainability.test.d.ts +0 -2
  234. package/dist/ai/__tests__/explainability.test.d.ts.map +0 -1
  235. package/dist/ai/__tests__/explainability.test.js +0 -100
  236. package/dist/ai/__tests__/explainability.test.js.map +0 -1
  237. package/dist/ai/__tests__/model-pricing.test.d.ts +0 -2
  238. package/dist/ai/__tests__/model-pricing.test.d.ts.map +0 -1
  239. package/dist/ai/__tests__/model-pricing.test.js +0 -156
  240. package/dist/ai/__tests__/model-pricing.test.js.map +0 -1
  241. package/dist/ai/__tests__/prompt-registry.test.d.ts +0 -2
  242. package/dist/ai/__tests__/prompt-registry.test.d.ts.map +0 -1
  243. package/dist/ai/__tests__/prompt-registry.test.js +0 -56
  244. package/dist/ai/__tests__/prompt-registry.test.js.map +0 -1
  245. package/dist/ai/__tests__/provider.test.d.ts +0 -3
  246. package/dist/ai/__tests__/provider.test.d.ts.map +0 -1
  247. package/dist/ai/__tests__/provider.test.js +0 -979
  248. package/dist/ai/__tests__/provider.test.js.map +0 -1
  249. package/dist/ai/__tests__/rag-pipeline.test.d.ts +0 -2
  250. package/dist/ai/__tests__/rag-pipeline.test.d.ts.map +0 -1
  251. package/dist/ai/__tests__/rag-pipeline.test.js +0 -174
  252. package/dist/ai/__tests__/rag-pipeline.test.js.map +0 -1
  253. package/dist/ai/__tests__/security.test.d.ts +0 -2
  254. package/dist/ai/__tests__/security.test.d.ts.map +0 -1
  255. package/dist/ai/__tests__/security.test.js +0 -89
  256. package/dist/ai/__tests__/security.test.js.map +0 -1
  257. package/dist/ai/__tests__/usage-client.test.d.ts +0 -2
  258. package/dist/ai/__tests__/usage-client.test.d.ts.map +0 -1
  259. package/dist/ai/__tests__/usage-client.test.js +0 -120
  260. package/dist/ai/__tests__/usage-client.test.js.map +0 -1
  261. package/dist/ai/__tests__/validation.test.d.ts +0 -2
  262. package/dist/ai/__tests__/validation.test.d.ts.map +0 -1
  263. package/dist/ai/__tests__/validation.test.js +0 -384
  264. package/dist/ai/__tests__/validation.test.js.map +0 -1
  265. package/dist/ai/__tests__/zod-to-provider-schema.test.d.ts +0 -2
  266. package/dist/ai/__tests__/zod-to-provider-schema.test.d.ts.map +0 -1
  267. package/dist/ai/__tests__/zod-to-provider-schema.test.js +0 -60
  268. package/dist/ai/__tests__/zod-to-provider-schema.test.js.map +0 -1
  269. package/dist/api/__tests__/route-generator.test.d.ts +0 -2
  270. package/dist/api/__tests__/route-generator.test.d.ts.map +0 -1
  271. package/dist/api/__tests__/route-generator.test.js +0 -216
  272. package/dist/api/__tests__/route-generator.test.js.map +0 -1
  273. package/dist/audit/__tests__/service.test.d.ts +0 -2
  274. package/dist/audit/__tests__/service.test.d.ts.map +0 -1
  275. package/dist/audit/__tests__/service.test.js +0 -97
  276. package/dist/audit/__tests__/service.test.js.map +0 -1
  277. package/dist/auth/__tests__/adapter.test.d.ts +0 -2
  278. package/dist/auth/__tests__/adapter.test.d.ts.map +0 -1
  279. package/dist/auth/__tests__/adapter.test.js +0 -218
  280. package/dist/auth/__tests__/adapter.test.js.map +0 -1
  281. package/dist/auth/__tests__/oidc-adapter.test.d.ts +0 -2
  282. package/dist/auth/__tests__/oidc-adapter.test.d.ts.map +0 -1
  283. package/dist/auth/__tests__/oidc-adapter.test.js +0 -232
  284. package/dist/auth/__tests__/oidc-adapter.test.js.map +0 -1
  285. package/dist/auth/__tests__/password.test.d.ts +0 -2
  286. package/dist/auth/__tests__/password.test.d.ts.map +0 -1
  287. package/dist/auth/__tests__/password.test.js +0 -30
  288. package/dist/auth/__tests__/password.test.js.map +0 -1
  289. package/dist/auth/__tests__/saml-adapter.test.d.ts +0 -2
  290. package/dist/auth/__tests__/saml-adapter.test.d.ts.map +0 -1
  291. package/dist/auth/__tests__/saml-adapter.test.js +0 -188
  292. package/dist/auth/__tests__/saml-adapter.test.js.map +0 -1
  293. package/dist/auth/__tests__/scim.test.d.ts +0 -2
  294. package/dist/auth/__tests__/scim.test.d.ts.map +0 -1
  295. package/dist/auth/__tests__/scim.test.js +0 -306
  296. package/dist/auth/__tests__/scim.test.js.map +0 -1
  297. package/dist/cli/__tests__/agent.test.d.ts +0 -2
  298. package/dist/cli/__tests__/agent.test.d.ts.map +0 -1
  299. package/dist/cli/__tests__/agent.test.js +0 -63
  300. package/dist/cli/__tests__/agent.test.js.map +0 -1
  301. package/dist/cli/__tests__/certify.test.d.ts +0 -2
  302. package/dist/cli/__tests__/certify.test.d.ts.map +0 -1
  303. package/dist/cli/__tests__/certify.test.js +0 -97
  304. package/dist/cli/__tests__/certify.test.js.map +0 -1
  305. package/dist/cli/__tests__/cli.test.d.ts +0 -2
  306. package/dist/cli/__tests__/cli.test.d.ts.map +0 -1
  307. package/dist/cli/__tests__/cli.test.js +0 -123
  308. package/dist/cli/__tests__/cli.test.js.map +0 -1
  309. package/dist/cli/__tests__/create.test.d.ts +0 -2
  310. package/dist/cli/__tests__/create.test.d.ts.map +0 -1
  311. package/dist/cli/__tests__/create.test.js +0 -183
  312. package/dist/cli/__tests__/create.test.js.map +0 -1
  313. package/dist/cli/__tests__/dev.test.d.ts +0 -2
  314. package/dist/cli/__tests__/dev.test.d.ts.map +0 -1
  315. package/dist/cli/__tests__/dev.test.js +0 -132
  316. package/dist/cli/__tests__/dev.test.js.map +0 -1
  317. package/dist/cli/__tests__/discover.test.d.ts +0 -2
  318. package/dist/cli/__tests__/discover.test.d.ts.map +0 -1
  319. package/dist/cli/__tests__/discover.test.js +0 -48
  320. package/dist/cli/__tests__/discover.test.js.map +0 -1
  321. package/dist/cli/__tests__/doctor.test.d.ts +0 -2
  322. package/dist/cli/__tests__/doctor.test.d.ts.map +0 -1
  323. package/dist/cli/__tests__/doctor.test.js +0 -172
  324. package/dist/cli/__tests__/doctor.test.js.map +0 -1
  325. package/dist/cli/__tests__/generate.test.d.ts +0 -2
  326. package/dist/cli/__tests__/generate.test.d.ts.map +0 -1
  327. package/dist/cli/__tests__/generate.test.js +0 -478
  328. package/dist/cli/__tests__/generate.test.js.map +0 -1
  329. package/dist/cli/__tests__/init.test.d.ts +0 -2
  330. package/dist/cli/__tests__/init.test.d.ts.map +0 -1
  331. package/dist/cli/__tests__/init.test.js +0 -236
  332. package/dist/cli/__tests__/init.test.js.map +0 -1
  333. package/dist/cli/__tests__/migrate.test.d.ts +0 -2
  334. package/dist/cli/__tests__/migrate.test.d.ts.map +0 -1
  335. package/dist/cli/__tests__/migrate.test.js +0 -442
  336. package/dist/cli/__tests__/migrate.test.js.map +0 -1
  337. package/dist/cli/__tests__/start.test.d.ts +0 -2
  338. package/dist/cli/__tests__/start.test.d.ts.map +0 -1
  339. package/dist/cli/__tests__/start.test.js +0 -91
  340. package/dist/cli/__tests__/start.test.js.map +0 -1
  341. package/dist/cli/__tests__/templates.test.d.ts +0 -2
  342. package/dist/cli/__tests__/templates.test.d.ts.map +0 -1
  343. package/dist/cli/__tests__/templates.test.js +0 -49
  344. package/dist/cli/__tests__/templates.test.js.map +0 -1
  345. package/dist/cli/__tests__/test.test.d.ts +0 -2
  346. package/dist/cli/__tests__/test.test.d.ts.map +0 -1
  347. package/dist/cli/__tests__/test.test.js +0 -40
  348. package/dist/cli/__tests__/test.test.js.map +0 -1
  349. package/dist/cli/__tests__/ui.test.d.ts +0 -2
  350. package/dist/cli/__tests__/ui.test.d.ts.map +0 -1
  351. package/dist/cli/__tests__/ui.test.js +0 -76
  352. package/dist/cli/__tests__/ui.test.js.map +0 -1
  353. package/dist/cli/__tests__/upgrade.test.d.ts +0 -2
  354. package/dist/cli/__tests__/upgrade.test.d.ts.map +0 -1
  355. package/dist/cli/__tests__/upgrade.test.js +0 -20
  356. package/dist/cli/__tests__/upgrade.test.js.map +0 -1
  357. package/dist/cli/__tests__/utils.test.d.ts +0 -2
  358. package/dist/cli/__tests__/utils.test.d.ts.map +0 -1
  359. package/dist/cli/__tests__/utils.test.js +0 -255
  360. package/dist/cli/__tests__/utils.test.js.map +0 -1
  361. package/dist/cli/__tests__/verify.test.d.ts +0 -2
  362. package/dist/cli/__tests__/verify.test.d.ts.map +0 -1
  363. package/dist/cli/__tests__/verify.test.js +0 -138
  364. package/dist/cli/__tests__/verify.test.js.map +0 -1
  365. package/dist/cli/commands/__tests__/commands.test.d.ts +0 -2
  366. package/dist/cli/commands/__tests__/commands.test.d.ts.map +0 -1
  367. package/dist/cli/commands/__tests__/commands.test.js +0 -185
  368. package/dist/cli/commands/__tests__/commands.test.js.map +0 -1
  369. package/dist/config/__tests__/loader.test.d.ts +0 -2
  370. package/dist/config/__tests__/loader.test.d.ts.map +0 -1
  371. package/dist/config/__tests__/loader.test.js +0 -462
  372. package/dist/config/__tests__/loader.test.js.map +0 -1
  373. package/dist/data/__tests__/drift-inspector.test.d.ts +0 -2
  374. package/dist/data/__tests__/drift-inspector.test.d.ts.map +0 -1
  375. package/dist/data/__tests__/drift-inspector.test.js +0 -170
  376. package/dist/data/__tests__/drift-inspector.test.js.map +0 -1
  377. package/dist/data/__tests__/framework-migration-rewriter.test.d.ts +0 -2
  378. package/dist/data/__tests__/framework-migration-rewriter.test.d.ts.map +0 -1
  379. package/dist/data/__tests__/framework-migration-rewriter.test.js +0 -329
  380. package/dist/data/__tests__/framework-migration-rewriter.test.js.map +0 -1
  381. package/dist/data/__tests__/migration.test.d.ts +0 -2
  382. package/dist/data/__tests__/migration.test.d.ts.map +0 -1
  383. package/dist/data/__tests__/migration.test.js +0 -143
  384. package/dist/data/__tests__/migration.test.js.map +0 -1
  385. package/dist/data/__tests__/registry.test.d.ts +0 -2
  386. package/dist/data/__tests__/registry.test.d.ts.map +0 -1
  387. package/dist/data/__tests__/registry.test.js +0 -70
  388. package/dist/data/__tests__/registry.test.js.map +0 -1
  389. package/dist/data/__tests__/repository.test.d.ts +0 -2
  390. package/dist/data/__tests__/repository.test.d.ts.map +0 -1
  391. package/dist/data/__tests__/repository.test.js +0 -559
  392. package/dist/data/__tests__/repository.test.js.map +0 -1
  393. package/dist/data/__tests__/schema-generator.test.d.ts +0 -2
  394. package/dist/data/__tests__/schema-generator.test.d.ts.map +0 -1
  395. package/dist/data/__tests__/schema-generator.test.js +0 -112
  396. package/dist/data/__tests__/schema-generator.test.js.map +0 -1
  397. package/dist/define/__tests__/defineCapability.test.d.ts +0 -2
  398. package/dist/define/__tests__/defineCapability.test.d.ts.map +0 -1
  399. package/dist/define/__tests__/defineCapability.test.js +0 -66
  400. package/dist/define/__tests__/defineCapability.test.js.map +0 -1
  401. package/dist/define/__tests__/defineEntity.test.d.ts +0 -2
  402. package/dist/define/__tests__/defineEntity.test.d.ts.map +0 -1
  403. package/dist/define/__tests__/defineEntity.test.js +0 -50
  404. package/dist/define/__tests__/defineEntity.test.js.map +0 -1
  405. package/dist/define/__tests__/defineEvent.test.d.ts +0 -2
  406. package/dist/define/__tests__/defineEvent.test.d.ts.map +0 -1
  407. package/dist/define/__tests__/defineEvent.test.js +0 -35
  408. package/dist/define/__tests__/defineEvent.test.js.map +0 -1
  409. package/dist/define/__tests__/defineFlow.test.d.ts +0 -2
  410. package/dist/define/__tests__/defineFlow.test.d.ts.map +0 -1
  411. package/dist/define/__tests__/defineFlow.test.js +0 -49
  412. package/dist/define/__tests__/defineFlow.test.js.map +0 -1
  413. package/dist/define/__tests__/definePrompt.test.d.ts +0 -2
  414. package/dist/define/__tests__/definePrompt.test.d.ts.map +0 -1
  415. package/dist/define/__tests__/definePrompt.test.js +0 -77
  416. package/dist/define/__tests__/definePrompt.test.js.map +0 -1
  417. package/dist/define/__tests__/defineTranslation.test.d.ts +0 -2
  418. package/dist/define/__tests__/defineTranslation.test.d.ts.map +0 -1
  419. package/dist/define/__tests__/defineTranslation.test.js +0 -90
  420. package/dist/define/__tests__/defineTranslation.test.js.map +0 -1
  421. package/dist/errors/__tests__/errors.test.d.ts +0 -2
  422. package/dist/errors/__tests__/errors.test.d.ts.map +0 -1
  423. package/dist/errors/__tests__/errors.test.js +0 -70
  424. package/dist/errors/__tests__/errors.test.js.map +0 -1
  425. package/dist/errors/__tests__/http.test.d.ts +0 -2
  426. package/dist/errors/__tests__/http.test.d.ts.map +0 -1
  427. package/dist/errors/__tests__/http.test.js +0 -35
  428. package/dist/errors/__tests__/http.test.js.map +0 -1
  429. package/dist/events/__tests__/consumer-registry.test.d.ts +0 -2
  430. package/dist/events/__tests__/consumer-registry.test.d.ts.map +0 -1
  431. package/dist/events/__tests__/consumer-registry.test.js +0 -68
  432. package/dist/events/__tests__/consumer-registry.test.js.map +0 -1
  433. package/dist/events/__tests__/dispatcher.test.d.ts +0 -2
  434. package/dist/events/__tests__/dispatcher.test.d.ts.map +0 -1
  435. package/dist/events/__tests__/dispatcher.test.js +0 -168
  436. package/dist/events/__tests__/dispatcher.test.js.map +0 -1
  437. package/dist/events/__tests__/event-emitter.test.d.ts +0 -2
  438. package/dist/events/__tests__/event-emitter.test.d.ts.map +0 -1
  439. package/dist/events/__tests__/event-emitter.test.js +0 -172
  440. package/dist/events/__tests__/event-emitter.test.js.map +0 -1
  441. package/dist/events/__tests__/event-registry.test.d.ts +0 -2
  442. package/dist/events/__tests__/event-registry.test.d.ts.map +0 -1
  443. package/dist/events/__tests__/event-registry.test.js +0 -55
  444. package/dist/events/__tests__/event-registry.test.js.map +0 -1
  445. package/dist/events/__tests__/event-worker.test.d.ts +0 -2
  446. package/dist/events/__tests__/event-worker.test.d.ts.map +0 -1
  447. package/dist/events/__tests__/event-worker.test.js +0 -174
  448. package/dist/events/__tests__/event-worker.test.js.map +0 -1
  449. package/dist/events/__tests__/queue.test.d.ts +0 -2
  450. package/dist/events/__tests__/queue.test.d.ts.map +0 -1
  451. package/dist/events/__tests__/queue.test.js +0 -73
  452. package/dist/events/__tests__/queue.test.js.map +0 -1
  453. package/dist/execution/__tests__/authorization.test.d.ts +0 -2
  454. package/dist/execution/__tests__/authorization.test.d.ts.map +0 -1
  455. package/dist/execution/__tests__/authorization.test.js +0 -99
  456. package/dist/execution/__tests__/authorization.test.js.map +0 -1
  457. package/dist/execution/__tests__/capability-executor.test.d.ts +0 -2
  458. package/dist/execution/__tests__/capability-executor.test.d.ts.map +0 -1
  459. package/dist/execution/__tests__/capability-executor.test.js +0 -212
  460. package/dist/execution/__tests__/capability-executor.test.js.map +0 -1
  461. package/dist/execution/__tests__/capability-registry.test.d.ts +0 -2
  462. package/dist/execution/__tests__/capability-registry.test.d.ts.map +0 -1
  463. package/dist/execution/__tests__/capability-registry.test.js +0 -50
  464. package/dist/execution/__tests__/capability-registry.test.js.map +0 -1
  465. package/dist/execution/__tests__/context-factory.test.d.ts +0 -2
  466. package/dist/execution/__tests__/context-factory.test.d.ts.map +0 -1
  467. package/dist/execution/__tests__/context-factory.test.js +0 -133
  468. package/dist/execution/__tests__/context-factory.test.js.map +0 -1
  469. package/dist/explanation/__tests__/tracker.test.d.ts +0 -2
  470. package/dist/explanation/__tests__/tracker.test.d.ts.map +0 -1
  471. package/dist/explanation/__tests__/tracker.test.js +0 -169
  472. package/dist/explanation/__tests__/tracker.test.js.map +0 -1
  473. package/dist/fields/__tests__/fields.test.d.ts +0 -2
  474. package/dist/fields/__tests__/fields.test.d.ts.map +0 -1
  475. package/dist/fields/__tests__/fields.test.js +0 -60
  476. package/dist/fields/__tests__/fields.test.js.map +0 -1
  477. package/dist/flows/__tests__/flow-engine.test.d.ts +0 -2
  478. package/dist/flows/__tests__/flow-engine.test.d.ts.map +0 -1
  479. package/dist/flows/__tests__/flow-engine.test.js +0 -797
  480. package/dist/flows/__tests__/flow-engine.test.js.map +0 -1
  481. package/dist/flows/__tests__/flow-registry.test.d.ts +0 -2
  482. package/dist/flows/__tests__/flow-registry.test.d.ts.map +0 -1
  483. package/dist/flows/__tests__/flow-registry.test.js +0 -69
  484. package/dist/flows/__tests__/flow-registry.test.js.map +0 -1
  485. package/dist/flows/__tests__/flow-service.test.d.ts +0 -2
  486. package/dist/flows/__tests__/flow-service.test.d.ts.map +0 -1
  487. package/dist/flows/__tests__/flow-service.test.js +0 -55
  488. package/dist/flows/__tests__/flow-service.test.js.map +0 -1
  489. package/dist/flows/__tests__/scheduler.test.d.ts +0 -2
  490. package/dist/flows/__tests__/scheduler.test.d.ts.map +0 -1
  491. package/dist/flows/__tests__/scheduler.test.js +0 -120
  492. package/dist/flows/__tests__/scheduler.test.js.map +0 -1
  493. package/dist/flows/__tests__/state-machine.test.d.ts +0 -2
  494. package/dist/flows/__tests__/state-machine.test.d.ts.map +0 -1
  495. package/dist/flows/__tests__/state-machine.test.js +0 -62
  496. package/dist/flows/__tests__/state-machine.test.js.map +0 -1
  497. package/dist/flows/__tests__/step-executor.test.d.ts +0 -2
  498. package/dist/flows/__tests__/step-executor.test.d.ts.map +0 -1
  499. package/dist/flows/__tests__/step-executor.test.js +0 -201
  500. package/dist/flows/__tests__/step-executor.test.js.map +0 -1
  501. package/dist/flows/__tests__/triggers.test.d.ts +0 -2
  502. package/dist/flows/__tests__/triggers.test.d.ts.map +0 -1
  503. package/dist/flows/__tests__/triggers.test.js +0 -98
  504. package/dist/flows/__tests__/triggers.test.js.map +0 -1
  505. package/dist/governance/__tests__/overrides.test.d.ts +0 -2
  506. package/dist/governance/__tests__/overrides.test.d.ts.map +0 -1
  507. package/dist/governance/__tests__/overrides.test.js +0 -123
  508. package/dist/governance/__tests__/overrides.test.js.map +0 -1
  509. package/dist/governance/__tests__/policies.test.d.ts +0 -2
  510. package/dist/governance/__tests__/policies.test.d.ts.map +0 -1
  511. package/dist/governance/__tests__/policies.test.js +0 -203
  512. package/dist/governance/__tests__/policies.test.js.map +0 -1
  513. package/dist/governance/__tests__/rules.test.d.ts +0 -2
  514. package/dist/governance/__tests__/rules.test.d.ts.map +0 -1
  515. package/dist/governance/__tests__/rules.test.js +0 -342
  516. package/dist/governance/__tests__/rules.test.js.map +0 -1
  517. package/dist/observability/__tests__/metrics.test.d.ts +0 -2
  518. package/dist/observability/__tests__/metrics.test.d.ts.map +0 -1
  519. package/dist/observability/__tests__/metrics.test.js +0 -301
  520. package/dist/observability/__tests__/metrics.test.js.map +0 -1
  521. package/dist/server/__tests__/bootstrap.test.d.ts +0 -2
  522. package/dist/server/__tests__/bootstrap.test.d.ts.map +0 -1
  523. package/dist/server/__tests__/bootstrap.test.js +0 -381
  524. package/dist/server/__tests__/bootstrap.test.js.map +0 -1
  525. package/dist/testing/__tests__/context.test.d.ts +0 -2
  526. package/dist/testing/__tests__/context.test.d.ts.map +0 -1
  527. package/dist/testing/__tests__/context.test.js +0 -341
  528. package/dist/testing/__tests__/context.test.js.map +0 -1
  529. package/dist/testing/__tests__/e2e.test.d.ts +0 -2
  530. package/dist/testing/__tests__/e2e.test.d.ts.map +0 -1
  531. package/dist/testing/__tests__/e2e.test.js +0 -119
  532. package/dist/testing/__tests__/e2e.test.js.map +0 -1
  533. package/dist/testing/__tests__/field-validation.test.d.ts +0 -2
  534. package/dist/testing/__tests__/field-validation.test.d.ts.map +0 -1
  535. package/dist/testing/__tests__/field-validation.test.js +0 -178
  536. package/dist/testing/__tests__/field-validation.test.js.map +0 -1
  537. package/dist/testing/__tests__/governance.test.d.ts +0 -2
  538. package/dist/testing/__tests__/governance.test.d.ts.map +0 -1
  539. package/dist/testing/__tests__/governance.test.js +0 -222
  540. package/dist/testing/__tests__/governance.test.js.map +0 -1
  541. package/dist/testing/__tests__/run-capability.test.d.ts +0 -2
  542. package/dist/testing/__tests__/run-capability.test.d.ts.map +0 -1
  543. package/dist/testing/__tests__/run-capability.test.js +0 -114
  544. package/dist/testing/__tests__/run-capability.test.js.map +0 -1
  545. package/dist/testing/__tests__/scaffolding.test.d.ts +0 -2
  546. package/dist/testing/__tests__/scaffolding.test.d.ts.map +0 -1
  547. package/dist/testing/__tests__/scaffolding.test.js +0 -70
  548. package/dist/testing/__tests__/scaffolding.test.js.map +0 -1
  549. package/dist/testing/__tests__/security.test.d.ts +0 -2
  550. package/dist/testing/__tests__/security.test.d.ts.map +0 -1
  551. package/dist/testing/__tests__/security.test.js +0 -186
  552. package/dist/testing/__tests__/security.test.js.map +0 -1
  553. package/dist/testing/__tests__/simulate-flow.test.d.ts +0 -2
  554. package/dist/testing/__tests__/simulate-flow.test.d.ts.map +0 -1
  555. package/dist/testing/__tests__/simulate-flow.test.js +0 -230
  556. package/dist/testing/__tests__/simulate-flow.test.js.map +0 -1
  557. package/dist/translations/__tests__/translation-resolver.test.d.ts +0 -2
  558. package/dist/translations/__tests__/translation-resolver.test.d.ts.map +0 -1
  559. package/dist/translations/__tests__/translation-resolver.test.js +0 -140
  560. package/dist/translations/__tests__/translation-resolver.test.js.map +0 -1
  561. package/dist/types/__tests__/registry.test.d.ts +0 -2
  562. package/dist/types/__tests__/registry.test.d.ts.map +0 -1
  563. package/dist/types/__tests__/registry.test.js +0 -34
  564. package/dist/types/__tests__/registry.test.js.map +0 -1
  565. package/dist/worker/__tests__/bootstrap.test.d.ts +0 -2
  566. package/dist/worker/__tests__/bootstrap.test.d.ts.map +0 -1
  567. package/dist/worker/__tests__/bootstrap.test.js +0 -268
  568. package/dist/worker/__tests__/bootstrap.test.js.map +0 -1
  569. package/dist/worker/__tests__/flow-service-wiring.test.d.ts +0 -2
  570. package/dist/worker/__tests__/flow-service-wiring.test.d.ts.map +0 -1
  571. package/dist/worker/__tests__/flow-service-wiring.test.js +0 -64
  572. package/dist/worker/__tests__/flow-service-wiring.test.js.map +0 -1
@@ -1,979 +0,0 @@
1
- import { describe, expect, it, vi } from 'vitest';
2
- import { createAnthropicAdapter, createOpenAIAdapter, createProviderAdapter, joinAndFilterModels, ProviderAPIError, } from '../provider.js';
3
- import { AIIncompleteOutputError, AIRefusalError } from '../refusal.js';
4
- // The built-in OpenAI and Anthropic adapters always implement `listModels`;
5
- // the interface marks it optional only for third-party adapters. This helper
6
- // narrows the type so tests don't need non-null assertions on every call.
7
- function callListModels(adapter, filter) {
8
- if (!adapter.listModels)
9
- throw new Error('adapter must implement listModels');
10
- return adapter.listModels(filter);
11
- }
12
- // ── Helper: create a mock provider ──
13
- // biome-ignore lint/suspicious/noExportsInTest: shared test helper used by multiple test files
14
- export function createMockProvider(overrides) {
15
- return {
16
- name: 'mock',
17
- complete: vi.fn(async () => ({
18
- content: '{"result": "test"}',
19
- model: 'mock-model',
20
- usage: { inputTokens: 10, outputTokens: 20, totalTokens: 30 },
21
- finishReason: 'stop',
22
- })),
23
- async *stream() {
24
- yield { type: 'content_delta', delta: 'mock' };
25
- yield { type: 'done', finishReason: 'stop' };
26
- },
27
- embed: vi.fn(async () => ({
28
- embeddings: [[0.1, 0.2, 0.3]],
29
- model: 'mock-embed',
30
- usage: { totalTokens: 5 },
31
- cost: 0,
32
- })),
33
- ...overrides,
34
- };
35
- }
36
- describe('AI Provider Adapters', () => {
37
- describe('createOpenAIAdapter', () => {
38
- it("creates an adapter with name 'openai'", () => {
39
- const adapter = createOpenAIAdapter({ apiKey: 'test-key' });
40
- expect(adapter.name).toBe('openai');
41
- });
42
- it('calls OpenAI chat completions endpoint', async () => {
43
- const mockFetch = vi.fn().mockResolvedValue({
44
- ok: true,
45
- json: async () => ({
46
- choices: [{ message: { content: 'Hello' }, finish_reason: 'stop' }],
47
- model: 'gpt-4o',
48
- usage: {
49
- prompt_tokens: 10,
50
- completion_tokens: 5,
51
- total_tokens: 15,
52
- prompt_tokens_details: { cached_tokens: 4 },
53
- },
54
- }),
55
- });
56
- vi.stubGlobal('fetch', mockFetch);
57
- const adapter = createOpenAIAdapter({ apiKey: 'sk-test' });
58
- const result = await adapter.complete({ prompt: 'Say hello' });
59
- expect(result.content).toBe('Hello');
60
- expect(result.model).toBe('gpt-4o');
61
- expect(result.usage.inputTokens).toBe(10);
62
- expect(result.usage.outputTokens).toBe(5);
63
- expect(result.usage.cachedInputTokens).toBe(4);
64
- expect(mockFetch).toHaveBeenCalledWith('https://api.openai.com/v1/chat/completions', expect.objectContaining({ method: 'POST' }));
65
- vi.unstubAllGlobals();
66
- });
67
- it('sends system message when provided', async () => {
68
- const mockFetch = vi.fn().mockResolvedValue({
69
- ok: true,
70
- json: async () => ({
71
- choices: [{ message: { content: 'Hi' }, finish_reason: 'stop' }],
72
- model: 'gpt-4o',
73
- usage: { prompt_tokens: 15, completion_tokens: 3, total_tokens: 18 },
74
- }),
75
- });
76
- vi.stubGlobal('fetch', mockFetch);
77
- const adapter = createOpenAIAdapter({ apiKey: 'sk-test' });
78
- await adapter.complete({ prompt: 'Hello', system: 'Be nice' });
79
- const call = mockFetch.mock.calls[0];
80
- const body = JSON.parse(call?.[1].body);
81
- expect(body.messages).toHaveLength(2);
82
- expect(body.messages[0].role).toBe('system');
83
- vi.unstubAllGlobals();
84
- });
85
- it('uses max_completion_tokens for newer OpenAI completion models', async () => {
86
- const mockFetch = vi.fn().mockResolvedValue({
87
- ok: true,
88
- json: async () => ({
89
- choices: [{ message: { content: 'Hello' }, finish_reason: 'stop' }],
90
- model: 'gpt-5-mini',
91
- usage: { prompt_tokens: 10, completion_tokens: 5, total_tokens: 15 },
92
- }),
93
- });
94
- vi.stubGlobal('fetch', mockFetch);
95
- const adapter = createOpenAIAdapter({ apiKey: 'sk-test', model: 'gpt-5-mini' });
96
- await adapter.complete({ prompt: 'Say hello', maxTokens: 1234 });
97
- const call = mockFetch.mock.calls[0];
98
- const body = JSON.parse(call?.[1].body);
99
- expect(body.max_completion_tokens).toBe(1234);
100
- expect(body.max_tokens).toBeUndefined();
101
- vi.unstubAllGlobals();
102
- });
103
- it('uses max_completion_tokens for newer OpenAI streaming models', async () => {
104
- const mockFetch = vi.fn().mockResolvedValue(new Response('data: [DONE]\n\n', {
105
- status: 200,
106
- headers: { 'Content-Type': 'text/event-stream' },
107
- }));
108
- vi.stubGlobal('fetch', mockFetch);
109
- const adapter = createOpenAIAdapter({ apiKey: 'sk-test', model: 'o4-mini' });
110
- for await (const event of adapter.stream({ prompt: 'Stream hello', maxTokens: 4321 })) {
111
- expect(event.type).toBe('done');
112
- break;
113
- }
114
- const call = mockFetch.mock.calls[0];
115
- const body = JSON.parse(call?.[1].body);
116
- expect(body.max_completion_tokens).toBe(4321);
117
- expect(body.max_tokens).toBeUndefined();
118
- vi.unstubAllGlobals();
119
- });
120
- it('sends strict JSON Schema response_format when responseSchema is provided', async () => {
121
- const mockFetch = vi.fn().mockResolvedValue({
122
- ok: true,
123
- json: async () => ({
124
- choices: [{ message: { content: '{"name":"Alice"}' }, finish_reason: 'stop' }],
125
- model: 'gpt-4o',
126
- usage: { prompt_tokens: 10, completion_tokens: 5, total_tokens: 15 },
127
- }),
128
- });
129
- vi.stubGlobal('fetch', mockFetch);
130
- const adapter = createOpenAIAdapter({ apiKey: 'sk-test' });
131
- await adapter.complete({
132
- prompt: 'Return JSON',
133
- responseFormat: 'json',
134
- responseSchema: {
135
- type: 'object',
136
- properties: { name: { type: 'string' } },
137
- required: ['name'],
138
- additionalProperties: false,
139
- },
140
- });
141
- const call = mockFetch.mock.calls[0];
142
- const body = JSON.parse(call?.[1].body);
143
- expect(body.response_format).toEqual({
144
- type: 'json_schema',
145
- json_schema: {
146
- name: 'response',
147
- strict: true,
148
- schema: {
149
- type: 'object',
150
- properties: { name: { type: 'string' } },
151
- required: ['name'],
152
- additionalProperties: false,
153
- },
154
- },
155
- });
156
- vi.unstubAllGlobals();
157
- });
158
- it('uses strict tool-call arguments for Grok structured output', async () => {
159
- const mockFetch = vi.fn().mockResolvedValue({
160
- ok: true,
161
- json: async () => ({
162
- choices: [
163
- {
164
- message: {
165
- content: null,
166
- tool_calls: [
167
- {
168
- type: 'function',
169
- function: {
170
- name: 'return_structured_response',
171
- arguments: '{"name":"Alice"}',
172
- },
173
- },
174
- ],
175
- },
176
- finish_reason: 'tool_calls',
177
- },
178
- ],
179
- model: 'grok-4-1-fast-non-reasoning',
180
- usage: { prompt_tokens: 10, completion_tokens: 5, total_tokens: 15 },
181
- }),
182
- });
183
- vi.stubGlobal('fetch', mockFetch);
184
- const adapter = createOpenAIAdapter({
185
- apiKey: 'sk-test',
186
- model: 'grok-4-1-fast-non-reasoning',
187
- });
188
- const result = await adapter.complete({
189
- prompt: 'Return JSON',
190
- responseFormat: 'json',
191
- structuredOutputTransport: 'tool',
192
- responseSchema: {
193
- type: 'object',
194
- properties: { name: { type: 'string' } },
195
- required: ['name'],
196
- additionalProperties: false,
197
- },
198
- });
199
- const call = mockFetch.mock.calls[0];
200
- const body = JSON.parse(call?.[1].body);
201
- expect(body.response_format).toBeUndefined();
202
- expect(body.tool_choice).toEqual({
203
- type: 'function',
204
- function: { name: 'return_structured_response' },
205
- });
206
- expect(body.tools).toEqual([
207
- {
208
- type: 'function',
209
- function: {
210
- name: 'return_structured_response',
211
- description: 'Return the complete structured response for this request.',
212
- strict: true,
213
- parameters: {
214
- type: 'object',
215
- properties: { name: { type: 'string' } },
216
- required: ['name'],
217
- additionalProperties: false,
218
- },
219
- },
220
- },
221
- ]);
222
- expect(result.content).toBe('{"name":"Alice"}');
223
- expect(result.finishReason).toBe('tool_calls');
224
- vi.unstubAllGlobals();
225
- });
226
- it('keeps Grok structured output on response_format unless explicitly opted into tools', async () => {
227
- const mockFetch = vi.fn().mockResolvedValue({
228
- ok: true,
229
- json: async () => ({
230
- choices: [{ message: { content: '{"name":"Alice"}' }, finish_reason: 'stop' }],
231
- model: 'grok-4-1-fast-non-reasoning',
232
- usage: { prompt_tokens: 10, completion_tokens: 5, total_tokens: 15 },
233
- }),
234
- });
235
- vi.stubGlobal('fetch', mockFetch);
236
- const adapter = createOpenAIAdapter({
237
- apiKey: 'sk-test',
238
- model: 'grok-4-1-fast-non-reasoning',
239
- });
240
- await adapter.complete({
241
- prompt: 'Return JSON',
242
- responseFormat: 'json',
243
- responseSchema: {
244
- type: 'object',
245
- properties: { name: { type: 'string' } },
246
- required: ['name'],
247
- additionalProperties: false,
248
- },
249
- });
250
- const call = mockFetch.mock.calls[0];
251
- const body = JSON.parse(call?.[1].body);
252
- expect(body.tools).toBeUndefined();
253
- expect(body.tool_choice).toBeUndefined();
254
- expect(body.response_format?.type).toBe('json_schema');
255
- vi.unstubAllGlobals();
256
- });
257
- it('throws a descriptive error when structured JSON content is empty', async () => {
258
- const mockFetch = vi.fn().mockResolvedValue({
259
- ok: true,
260
- json: async () => ({
261
- id: 'chatcmpl-empty',
262
- choices: [
263
- {
264
- message: { content: null, role: 'assistant' },
265
- finish_reason: 'stop',
266
- },
267
- ],
268
- model: 'grok-4-1-fast-non-reasoning',
269
- usage: { prompt_tokens: 10, completion_tokens: 0, total_tokens: 10 },
270
- }),
271
- });
272
- vi.stubGlobal('fetch', mockFetch);
273
- const adapter = createOpenAIAdapter({
274
- apiKey: 'sk-test',
275
- model: 'grok-4-1-fast-non-reasoning',
276
- });
277
- await expect(adapter.complete({
278
- prompt: 'Return JSON',
279
- responseFormat: 'json',
280
- responseSchema: {
281
- type: 'object',
282
- properties: { evidence: { type: 'array', items: { type: 'object' } } },
283
- required: ['evidence'],
284
- additionalProperties: false,
285
- },
286
- })).rejects.toThrow(/OpenAI-compatible provider returned empty JSON response content .*finish_reason=stop/);
287
- // Ensure the framework does NOT phone home to a debug ingest endpoint.
288
- const debugCall = mockFetch.mock.calls.find(([url]) => String(url).includes('/ingest/'));
289
- expect(debugCall).toBeUndefined();
290
- vi.unstubAllGlobals();
291
- });
292
- it('throws AIRefusalError when OpenAI returns a refusal', async () => {
293
- vi.stubGlobal('fetch', vi.fn().mockResolvedValue({
294
- ok: true,
295
- json: async () => ({
296
- choices: [{ message: { content: null, refusal: 'No.' }, finish_reason: 'stop' }],
297
- model: 'gpt-4o',
298
- usage: { prompt_tokens: 3, completion_tokens: 2, total_tokens: 5 },
299
- }),
300
- }));
301
- const adapter = createOpenAIAdapter({ apiKey: 'sk-test' });
302
- await expect(adapter.complete({ prompt: 'bad', responseFormat: 'json' })).rejects.toThrow(AIRefusalError);
303
- vi.unstubAllGlobals();
304
- });
305
- it('throws AIIncompleteOutputError when OpenAI stops for length', async () => {
306
- vi.stubGlobal('fetch', vi.fn().mockResolvedValue({
307
- ok: true,
308
- json: async () => ({
309
- choices: [{ message: { content: '{"partial":' }, finish_reason: 'length' }],
310
- model: 'gpt-4o',
311
- usage: { prompt_tokens: 3, completion_tokens: 8, total_tokens: 11 },
312
- }),
313
- }));
314
- const adapter = createOpenAIAdapter({ apiKey: 'sk-test' });
315
- await expect(adapter.complete({ prompt: 'long', responseFormat: 'json' })).rejects.toThrow(AIIncompleteOutputError);
316
- vi.unstubAllGlobals();
317
- });
318
- it('throws on non-retryable API error', async () => {
319
- vi.stubGlobal('fetch', vi.fn().mockResolvedValue({
320
- ok: false,
321
- status: 400,
322
- headers: new Headers(),
323
- text: async () => 'rate limited',
324
- }));
325
- const adapter = createOpenAIAdapter({ apiKey: 'sk-test' });
326
- await expect(adapter.complete({ prompt: 'test' })).rejects.toThrow('OpenAI API error (400)');
327
- vi.unstubAllGlobals();
328
- });
329
- it('retries transient API errors before succeeding', async () => {
330
- vi.useFakeTimers();
331
- const mockFetch = vi
332
- .fn()
333
- .mockResolvedValueOnce({
334
- ok: false,
335
- status: 503,
336
- headers: new Headers(),
337
- text: async () => 'capacity',
338
- })
339
- .mockResolvedValueOnce({
340
- ok: true,
341
- json: async () => ({
342
- choices: [{ message: { content: 'Recovered' }, finish_reason: 'stop' }],
343
- model: 'gpt-4o',
344
- usage: { prompt_tokens: 10, completion_tokens: 5, total_tokens: 15 },
345
- }),
346
- });
347
- vi.stubGlobal('fetch', mockFetch);
348
- const adapter = createOpenAIAdapter({ apiKey: 'sk-test' });
349
- const resultPromise = adapter.complete({ prompt: 'retry me' });
350
- await vi.runAllTimersAsync();
351
- const result = await resultPromise;
352
- expect(result.content).toBe('Recovered');
353
- expect(mockFetch).toHaveBeenCalledTimes(2);
354
- vi.useRealTimers();
355
- vi.unstubAllGlobals();
356
- });
357
- it('throws ProviderAPIError after exhausting transient retries', async () => {
358
- vi.useFakeTimers();
359
- const mockFetch = vi.fn().mockResolvedValue({
360
- ok: false,
361
- status: 503,
362
- headers: new Headers(),
363
- text: async () => 'still unavailable',
364
- });
365
- vi.stubGlobal('fetch', mockFetch);
366
- const adapter = createOpenAIAdapter({ apiKey: 'sk-test' });
367
- const errorPromise = adapter.complete({ prompt: 'retry me' }).then(() => new Error('Expected provider error'), (reason) => reason);
368
- await vi.runAllTimersAsync();
369
- const error = await errorPromise;
370
- expect(error).toBeInstanceOf(ProviderAPIError);
371
- expect(error).toMatchObject({
372
- providerName: 'openai',
373
- retryable: true,
374
- statusCode: 503,
375
- attempts: 3,
376
- });
377
- expect(mockFetch).toHaveBeenCalledTimes(3);
378
- vi.useRealTimers();
379
- vi.unstubAllGlobals();
380
- });
381
- it('calls embeddings endpoint', async () => {
382
- const mockFetch = vi.fn().mockResolvedValue({
383
- ok: true,
384
- json: async () => ({
385
- data: [{ embedding: [0.1, 0.2, 0.3] }],
386
- model: 'text-embedding-3-small',
387
- usage: { total_tokens: 8 },
388
- }),
389
- });
390
- vi.stubGlobal('fetch', mockFetch);
391
- const adapter = createOpenAIAdapter({ apiKey: 'sk-test' });
392
- const result = await adapter.embed({ texts: ['hello world'] });
393
- expect(result.embeddings).toHaveLength(1);
394
- expect(result.embeddings[0]).toEqual([0.1, 0.2, 0.3]);
395
- expect(result.model).toBe('text-embedding-3-small');
396
- expect(result.usage.totalTokens).toBe(8);
397
- vi.unstubAllGlobals();
398
- });
399
- it('computes cost from the pricing catalog for a known embedding model', async () => {
400
- const mockFetch = vi.fn().mockResolvedValue({
401
- ok: true,
402
- json: async () => ({
403
- data: [{ embedding: [0.1] }],
404
- model: 'text-embedding-3-large',
405
- usage: { total_tokens: 1_000_000 },
406
- }),
407
- });
408
- vi.stubGlobal('fetch', mockFetch);
409
- const adapter = createOpenAIAdapter({ apiKey: 'sk-test' });
410
- const result = await adapter.embed({ texts: ['x'] });
411
- // text-embedding-3-large: $0.13 per 1M input tokens → 1M tokens = $0.13
412
- expect(result.cost).toBeCloseTo(0.13, 6);
413
- vi.unstubAllGlobals();
414
- });
415
- it('returns cost=0 for an embedding model not in the pricing catalog', async () => {
416
- const mockFetch = vi.fn().mockResolvedValue({
417
- ok: true,
418
- json: async () => ({
419
- data: [{ embedding: [0.1] }],
420
- model: 'custom-embedding-model-not-in-catalog',
421
- usage: { total_tokens: 1000 },
422
- }),
423
- });
424
- vi.stubGlobal('fetch', mockFetch);
425
- const adapter = createOpenAIAdapter({ apiKey: 'sk-test' });
426
- const result = await adapter.embed({ texts: ['x'] });
427
- expect(result.cost).toBe(0);
428
- vi.unstubAllGlobals();
429
- });
430
- it('sends multi-turn messages verbatim and does not append prompt as an extra user message', async () => {
431
- const mockFetch = vi.fn().mockResolvedValue({
432
- ok: true,
433
- json: async () => ({
434
- choices: [{ message: { content: 'ok' }, finish_reason: 'stop' }],
435
- model: 'gpt-4o',
436
- usage: { prompt_tokens: 10, completion_tokens: 5, total_tokens: 15 },
437
- }),
438
- });
439
- vi.stubGlobal('fetch', mockFetch);
440
- const adapter = createOpenAIAdapter({ apiKey: 'sk-test' });
441
- await adapter.complete({
442
- prompt: 'IGNORED_WHEN_MESSAGES',
443
- system: 'Sys',
444
- messages: [
445
- { role: 'user', content: 'Hi' },
446
- { role: 'assistant', content: 'Hello' },
447
- { role: 'user', content: 'Last' },
448
- ],
449
- });
450
- const call = mockFetch.mock.calls[0];
451
- const body = JSON.parse(call?.[1].body);
452
- expect(body.messages).toEqual([
453
- { role: 'system', content: 'Sys' },
454
- { role: 'user', content: 'Hi' },
455
- { role: 'assistant', content: 'Hello' },
456
- { role: 'user', content: 'Last' },
457
- ]);
458
- vi.unstubAllGlobals();
459
- });
460
- });
461
- describe('createAnthropicAdapter', () => {
462
- it("creates an adapter with name 'anthropic'", () => {
463
- const adapter = createAnthropicAdapter({ apiKey: 'test-key' });
464
- expect(adapter.name).toBe('anthropic');
465
- });
466
- it('calls Anthropic messages endpoint', async () => {
467
- const mockFetch = vi.fn().mockResolvedValue({
468
- ok: true,
469
- json: async () => ({
470
- content: [{ type: 'text', text: 'Hello from Claude' }],
471
- model: 'claude-sonnet-4-20250514',
472
- usage: {
473
- input_tokens: 10,
474
- output_tokens: 8,
475
- cache_read_input_tokens: 3,
476
- cache_creation_input_tokens: 2,
477
- },
478
- stop_reason: 'end_turn',
479
- }),
480
- });
481
- vi.stubGlobal('fetch', mockFetch);
482
- const adapter = createAnthropicAdapter({ apiKey: 'ant-test' });
483
- const result = await adapter.complete({ prompt: 'Say hello' });
484
- expect(result.content).toBe('Hello from Claude');
485
- expect(result.usage.totalTokens).toBe(18);
486
- expect(result.usage.cachedInputTokens).toBe(3);
487
- expect(result.usage.cacheWriteTokens).toBe(2);
488
- expect(mockFetch).toHaveBeenCalledWith('https://api.anthropic.com/v1/messages', expect.objectContaining({
489
- method: 'POST',
490
- headers: expect.objectContaining({ 'x-api-key': 'ant-test' }),
491
- }));
492
- vi.unstubAllGlobals();
493
- });
494
- it('sends Anthropic output_config.format when responseSchema is provided', async () => {
495
- const mockFetch = vi.fn().mockResolvedValue({
496
- ok: true,
497
- json: async () => ({
498
- content: [{ type: 'text', text: '{"name":"Alice"}' }],
499
- model: 'claude-sonnet-4-5',
500
- usage: { input_tokens: 10, output_tokens: 8 },
501
- stop_reason: 'end_turn',
502
- }),
503
- });
504
- vi.stubGlobal('fetch', mockFetch);
505
- const adapter = createAnthropicAdapter({ apiKey: 'ant-test' });
506
- await adapter.complete({
507
- prompt: 'Return JSON',
508
- responseFormat: 'json',
509
- responseSchema: {
510
- type: 'object',
511
- properties: { name: { type: 'string' } },
512
- required: ['name'],
513
- additionalProperties: false,
514
- },
515
- });
516
- const call = mockFetch.mock.calls[0];
517
- const body = JSON.parse(call?.[1].body);
518
- expect(body.output_config).toEqual({
519
- format: {
520
- type: 'json_schema',
521
- schema: {
522
- type: 'object',
523
- properties: { name: { type: 'string' } },
524
- required: ['name'],
525
- additionalProperties: false,
526
- },
527
- },
528
- });
529
- expect(call?.[1].headers).not.toHaveProperty('anthropic-beta');
530
- vi.unstubAllGlobals();
531
- });
532
- it('throws AIRefusalError when Anthropic returns stop_reason refusal', async () => {
533
- vi.stubGlobal('fetch', vi.fn().mockResolvedValue({
534
- ok: true,
535
- json: async () => ({
536
- content: [{ type: 'text', text: 'No.' }],
537
- model: 'claude-sonnet-4-5',
538
- usage: { input_tokens: 10, output_tokens: 2 },
539
- stop_reason: 'refusal',
540
- }),
541
- }));
542
- const adapter = createAnthropicAdapter({ apiKey: 'ant-test' });
543
- await expect(adapter.complete({ prompt: 'bad', responseFormat: 'json' })).rejects.toThrow(AIRefusalError);
544
- vi.unstubAllGlobals();
545
- });
546
- it('throws AIIncompleteOutputError when Anthropic reaches max_tokens', async () => {
547
- vi.stubGlobal('fetch', vi.fn().mockResolvedValue({
548
- ok: true,
549
- json: async () => ({
550
- content: [{ type: 'text', text: '{"partial":' }],
551
- model: 'claude-sonnet-4-5',
552
- usage: { input_tokens: 10, output_tokens: 8 },
553
- stop_reason: 'max_tokens',
554
- }),
555
- }));
556
- const adapter = createAnthropicAdapter({ apiKey: 'ant-test' });
557
- await expect(adapter.complete({ prompt: 'long', responseFormat: 'json' })).rejects.toThrow(AIIncompleteOutputError);
558
- vi.unstubAllGlobals();
559
- });
560
- it('throws on embed (unsupported)', async () => {
561
- const adapter = createAnthropicAdapter({ apiKey: 'ant-test' });
562
- await expect(adapter.embed({ texts: ['hello'] })).rejects.toThrow('Anthropic does not provide an embedding API');
563
- });
564
- it('sends multi-turn messages verbatim when messages array is provided', async () => {
565
- const mockFetch = vi.fn().mockResolvedValue({
566
- ok: true,
567
- json: async () => ({
568
- content: [{ type: 'text', text: 'ok' }],
569
- model: 'claude-sonnet-4-20250514',
570
- usage: { input_tokens: 10, output_tokens: 5 },
571
- stop_reason: 'end_turn',
572
- }),
573
- });
574
- vi.stubGlobal('fetch', mockFetch);
575
- const adapter = createAnthropicAdapter({ apiKey: 'ant-test' });
576
- await adapter.complete({
577
- prompt: 'IGNORED',
578
- system: 'Merged system text',
579
- messages: [
580
- { role: 'user', content: 'First' },
581
- { role: 'assistant', content: 'Second' },
582
- ],
583
- });
584
- const call = mockFetch.mock.calls[0];
585
- const body = JSON.parse(call?.[1].body);
586
- expect(body.system).toBe('Merged system text');
587
- expect(body.messages).toEqual([
588
- { role: 'user', content: 'First' },
589
- { role: 'assistant', content: 'Second' },
590
- ]);
591
- vi.unstubAllGlobals();
592
- });
593
- });
594
- describe('external AbortSignal propagation', () => {
595
- it('fetch is called with a signal that fires when the caller aborts', async () => {
596
- let capturedSignal;
597
- const mockFetch = vi.fn().mockImplementation(async (_url, init) => {
598
- capturedSignal = init.signal;
599
- // Never resolves unless aborted — simulates an in-flight long request.
600
- return await new Promise((_resolve, reject) => {
601
- capturedSignal?.addEventListener('abort', () => {
602
- const err = new Error('aborted');
603
- err.name = 'AbortError';
604
- reject(err);
605
- });
606
- });
607
- });
608
- vi.stubGlobal('fetch', mockFetch);
609
- const adapter = createOpenAIAdapter({ apiKey: 'sk-test' });
610
- const ac = new AbortController();
611
- const p = adapter.complete({ prompt: 'long', signal: ac.signal });
612
- // Give the mock a tick to be called.
613
- await new Promise((r) => setTimeout(r, 10));
614
- ac.abort();
615
- await expect(p).rejects.toThrow(/abort/i);
616
- expect(capturedSignal).toBeInstanceOf(AbortSignal);
617
- vi.unstubAllGlobals();
618
- });
619
- it('completing normally before abort does not surface as aborted', async () => {
620
- const mockFetch = vi.fn().mockResolvedValue({
621
- ok: true,
622
- json: async () => ({
623
- choices: [{ message: { content: 'ok' }, finish_reason: 'stop' }],
624
- model: 'gpt-4o',
625
- usage: { prompt_tokens: 1, completion_tokens: 1, total_tokens: 2 },
626
- }),
627
- });
628
- vi.stubGlobal('fetch', mockFetch);
629
- const adapter = createOpenAIAdapter({ apiKey: 'sk-test' });
630
- const ac = new AbortController(); // never aborted
631
- const result = await adapter.complete({ prompt: 'fast', signal: ac.signal });
632
- expect(result.content).toBe('ok');
633
- vi.unstubAllGlobals();
634
- });
635
- it('uses a 600 second default request timeout for OpenAI-compatible calls', async () => {
636
- const originalTimeout = AbortSignal.timeout;
637
- let capturedTimeoutMs;
638
- const timeoutSpy = vi
639
- .spyOn(AbortSignal, 'timeout')
640
- .mockImplementation((timeoutMs) => {
641
- capturedTimeoutMs = timeoutMs;
642
- return originalTimeout.call(AbortSignal, timeoutMs);
643
- });
644
- const mockFetch = vi.fn().mockResolvedValue({
645
- ok: true,
646
- json: async () => ({
647
- choices: [{ message: { content: 'ok' }, finish_reason: 'stop' }],
648
- model: 'gpt-4o',
649
- usage: { prompt_tokens: 1, completion_tokens: 1, total_tokens: 2 },
650
- }),
651
- });
652
- vi.stubGlobal('fetch', mockFetch);
653
- const adapter = createOpenAIAdapter({ apiKey: 'sk-test' });
654
- await adapter.complete({ prompt: 'fast' });
655
- expect(capturedTimeoutMs).toBe(120_000);
656
- timeoutSpy.mockRestore();
657
- vi.unstubAllGlobals();
658
- });
659
- });
660
- describe('createProviderAdapter', () => {
661
- it('creates an OpenAI adapter for "openai"', () => {
662
- const adapter = createProviderAdapter('openai', { provider: 'openai', apiKey: 'sk-test' });
663
- expect(adapter.name).toBe('openai');
664
- });
665
- it('creates an Anthropic adapter for "anthropic"', () => {
666
- const adapter = createProviderAdapter('anthropic', {
667
- provider: 'anthropic',
668
- apiKey: 'ant-test',
669
- });
670
- expect(adapter.name).toBe('anthropic');
671
- });
672
- it('falls back to OpenAI-compat adapter for unknown providers', () => {
673
- const adapter = createProviderAdapter('ollama', {
674
- provider: 'ollama',
675
- apiKey: '',
676
- baseUrl: 'http://localhost:11434/v1',
677
- });
678
- // Unknown providers use OpenAI-compatible adapter
679
- expect(adapter.name).toBe('openai');
680
- });
681
- });
682
- describe('joinAndFilterModels', () => {
683
- it('joins entries against the pricing catalog (kind + prices for known, unknown otherwise)', () => {
684
- const result = joinAndFilterModels({
685
- provider: 'openai',
686
- entries: [{ id: 'gpt-4o' }, { id: 'text-embedding-3-small' }, { id: 'llama3:8b' }],
687
- filter: undefined,
688
- isOfficial: true,
689
- });
690
- expect(result).toHaveLength(3);
691
- const gpt = result.find((m) => m.id === 'gpt-4o');
692
- expect(gpt?.kind).toBe('text');
693
- expect(gpt?.inputPerMTok).toBe(2.5);
694
- const emb = result.find((m) => m.id === 'text-embedding-3-small');
695
- expect(emb?.kind).toBe('embedding');
696
- const llama = result.find((m) => m.id === 'llama3:8b');
697
- expect(llama?.kind).toBe('unknown');
698
- expect(llama?.inputPerMTok).toBeNull();
699
- expect(llama?.outputPerMTok).toBeNull();
700
- });
701
- it('passes through every entry when no filter is supplied', () => {
702
- const r = joinAndFilterModels({
703
- provider: 'openai',
704
- entries: [{ id: 'gpt-4o' }, { id: 'text-embedding-3-small' }, { id: 'mystery' }],
705
- filter: undefined,
706
- isOfficial: false,
707
- });
708
- expect(r.map((m) => m.id)).toEqual(['gpt-4o', 'text-embedding-3-small', 'mystery']);
709
- });
710
- it('single-kind filter against the official endpoint excludes unknowns', () => {
711
- const r = joinAndFilterModels({
712
- provider: 'openai',
713
- entries: [{ id: 'gpt-4o' }, { id: 'text-embedding-3-small' }, { id: 'nomic-embed' }],
714
- filter: { kind: 'embedding' },
715
- isOfficial: true,
716
- });
717
- expect(r.map((m) => m.id)).toEqual(['text-embedding-3-small']);
718
- });
719
- it('single-kind filter against a custom endpoint includes unknowns alongside matches', () => {
720
- const r = joinAndFilterModels({
721
- provider: 'ollama',
722
- entries: [{ id: 'gpt-4o' }, { id: 'text-embedding-3-small' }, { id: 'nomic-embed' }],
723
- filter: { kind: 'embedding' },
724
- isOfficial: false,
725
- });
726
- expect(r.map((m) => m.id).sort()).toEqual(['nomic-embed', 'text-embedding-3-small']);
727
- });
728
- it('multi-kind filter accepts arrays', () => {
729
- const r = joinAndFilterModels({
730
- provider: 'openai',
731
- entries: [
732
- { id: 'gpt-4o' },
733
- { id: 'text-embedding-3-small' },
734
- { id: 'omni-moderation-latest' },
735
- ],
736
- filter: { kind: ['text', 'embedding'] },
737
- isOfficial: true,
738
- });
739
- expect(r.map((m) => m.id).sort()).toEqual(['gpt-4o', 'text-embedding-3-small']);
740
- });
741
- it('carries through optional displayName / createdAt / ownedBy fields', () => {
742
- const r = joinAndFilterModels({
743
- provider: 'anthropic',
744
- entries: [
745
- {
746
- id: 'claude-opus-4-7',
747
- displayName: 'Claude Opus 4.7',
748
- createdAt: '2026-01-15T00:00:00Z',
749
- },
750
- ],
751
- filter: undefined,
752
- isOfficial: true,
753
- });
754
- expect(r[0]?.displayName).toBe('Claude Opus 4.7');
755
- expect(r[0]?.createdAt).toBe('2026-01-15T00:00:00Z');
756
- });
757
- });
758
- describe('OpenAI listModels', () => {
759
- it('returns joined entries from /v1/models with kind + pricing', async () => {
760
- const mockFetch = vi.fn().mockResolvedValue({
761
- ok: true,
762
- json: async () => ({
763
- object: 'list',
764
- data: [
765
- { id: 'gpt-4o', object: 'model', created: 1715367049, owned_by: 'system' },
766
- {
767
- id: 'text-embedding-3-small',
768
- object: 'model',
769
- created: 1700000000,
770
- owned_by: 'openai',
771
- },
772
- ],
773
- }),
774
- });
775
- vi.stubGlobal('fetch', mockFetch);
776
- const adapter = createOpenAIAdapter({ apiKey: 'sk-test' });
777
- const models = await callListModels(adapter);
778
- const call = mockFetch.mock.calls[0];
779
- expect(call?.[0]).toBe('https://api.openai.com/v1/models');
780
- expect(call?.[1].headers.Authorization).toBe('Bearer sk-test');
781
- expect(models).toHaveLength(2);
782
- expect(models[0]).toMatchObject({
783
- id: 'gpt-4o',
784
- provider: 'openai',
785
- kind: 'text',
786
- inputPerMTok: 2.5,
787
- outputPerMTok: 10,
788
- ownedBy: 'system',
789
- });
790
- expect(models[0]?.createdAt).toBe(new Date(1715367049 * 1000).toISOString());
791
- vi.unstubAllGlobals();
792
- });
793
- it('filters by kind against the official endpoint (unknowns excluded)', async () => {
794
- const mockFetch = vi.fn().mockResolvedValue({
795
- ok: true,
796
- json: async () => ({
797
- data: [
798
- { id: 'gpt-4o' },
799
- { id: 'text-embedding-3-small' },
800
- { id: 'fine-tune-ft-custom-12345' },
801
- ],
802
- }),
803
- });
804
- vi.stubGlobal('fetch', mockFetch);
805
- const adapter = createOpenAIAdapter({ apiKey: 'sk-test' });
806
- const models = await callListModels(adapter, { kind: 'embedding' });
807
- expect(models.map((m) => m.id)).toEqual(['text-embedding-3-small']);
808
- vi.unstubAllGlobals();
809
- });
810
- it('filters by kind against a custom endpoint and includes unknowns', async () => {
811
- const mockFetch = vi.fn().mockResolvedValue({
812
- ok: true,
813
- json: async () => ({
814
- data: [{ id: 'gpt-4o' }, { id: 'nomic-embed-text' }, { id: 'llama3:8b' }],
815
- }),
816
- });
817
- vi.stubGlobal('fetch', mockFetch);
818
- const adapter = createOpenAIAdapter({
819
- apiKey: 'ollama',
820
- baseUrl: 'http://localhost:11434/v1',
821
- });
822
- const models = await callListModels(adapter, { kind: 'embedding' });
823
- // text-known stays out, but both unknowns (nomic-embed-text, llama3:8b) come through.
824
- expect(models.map((m) => m.id).sort()).toEqual(['llama3:8b', 'nomic-embed-text']);
825
- vi.unstubAllGlobals();
826
- });
827
- it('returns empty list and warns on 404 (endpoint not implemented)', async () => {
828
- const mockFetch = vi.fn().mockResolvedValue({
829
- ok: false,
830
- status: 404,
831
- statusText: 'Not Found',
832
- });
833
- vi.stubGlobal('fetch', mockFetch);
834
- const warn = vi.spyOn(console, 'warn').mockImplementation(() => { });
835
- const adapter = createOpenAIAdapter({
836
- apiKey: 'azure-key',
837
- baseUrl: 'https://example.openai.azure.com/openai/v1',
838
- });
839
- const models = await callListModels(adapter);
840
- expect(models).toEqual([]);
841
- expect(warn).toHaveBeenCalledTimes(1);
842
- expect(warn.mock.calls[0]?.[0]).toMatch(/returned 404/);
843
- warn.mockRestore();
844
- vi.unstubAllGlobals();
845
- });
846
- it('returns empty list and warns on network error', async () => {
847
- const mockFetch = vi.fn().mockRejectedValue(new Error('ECONNREFUSED'));
848
- vi.stubGlobal('fetch', mockFetch);
849
- const warn = vi.spyOn(console, 'warn').mockImplementation(() => { });
850
- const adapter = createOpenAIAdapter({ apiKey: 'sk-test' });
851
- const models = await callListModels(adapter);
852
- expect(models).toEqual([]);
853
- expect(warn).toHaveBeenCalledTimes(1);
854
- expect(warn.mock.calls[0]?.[0]).toMatch(/ECONNREFUSED/);
855
- warn.mockRestore();
856
- vi.unstubAllGlobals();
857
- });
858
- it('returns empty list when response shape is unexpected', async () => {
859
- const mockFetch = vi.fn().mockResolvedValue({
860
- ok: true,
861
- json: async () => ({ message: 'hello' }),
862
- });
863
- vi.stubGlobal('fetch', mockFetch);
864
- const warn = vi.spyOn(console, 'warn').mockImplementation(() => { });
865
- const adapter = createOpenAIAdapter({ apiKey: 'sk-test' });
866
- const models = await callListModels(adapter);
867
- expect(models).toEqual([]);
868
- expect(warn).toHaveBeenCalledTimes(1);
869
- warn.mockRestore();
870
- vi.unstubAllGlobals();
871
- });
872
- });
873
- describe('Anthropic listModels', () => {
874
- it('returns joined entries from /v1/models with displayName + createdAt', async () => {
875
- const mockFetch = vi.fn().mockResolvedValue({
876
- ok: true,
877
- json: async () => ({
878
- data: [
879
- {
880
- id: 'claude-opus-4-7',
881
- type: 'model',
882
- display_name: 'Claude Opus 4.7',
883
- created_at: '2026-01-15T00:00:00Z',
884
- },
885
- {
886
- id: 'claude-sonnet-4-6',
887
- type: 'model',
888
- display_name: 'Claude Sonnet 4.6',
889
- created_at: '2025-09-01T00:00:00Z',
890
- },
891
- ],
892
- has_more: false,
893
- last_id: 'claude-sonnet-4-6',
894
- }),
895
- });
896
- vi.stubGlobal('fetch', mockFetch);
897
- const adapter = createAnthropicAdapter({ apiKey: 'ant-test' });
898
- const models = await callListModels(adapter);
899
- const call = mockFetch.mock.calls[0];
900
- expect(String(call?.[0])).toContain('https://api.anthropic.com/v1/models');
901
- expect(call?.[1].headers['x-api-key']).toBe('ant-test');
902
- expect(call?.[1].headers['anthropic-version']).toBe('2023-06-01');
903
- expect(models).toHaveLength(2);
904
- expect(models[0]).toMatchObject({
905
- id: 'claude-opus-4-7',
906
- provider: 'anthropic',
907
- kind: 'text',
908
- inputPerMTok: 5,
909
- outputPerMTok: 25,
910
- displayName: 'Claude Opus 4.7',
911
- createdAt: '2026-01-15T00:00:00Z',
912
- });
913
- vi.unstubAllGlobals();
914
- });
915
- it('paginates via has_more / last_id', async () => {
916
- const mockFetch = vi
917
- .fn()
918
- .mockResolvedValueOnce({
919
- ok: true,
920
- json: async () => ({
921
- data: [{ id: 'claude-opus-4-7', display_name: 'Claude Opus 4.7' }],
922
- has_more: true,
923
- last_id: 'claude-opus-4-7',
924
- }),
925
- })
926
- .mockResolvedValueOnce({
927
- ok: true,
928
- json: async () => ({
929
- data: [{ id: 'claude-sonnet-4-6', display_name: 'Claude Sonnet 4.6' }],
930
- has_more: false,
931
- last_id: 'claude-sonnet-4-6',
932
- }),
933
- });
934
- vi.stubGlobal('fetch', mockFetch);
935
- const adapter = createAnthropicAdapter({ apiKey: 'ant-test' });
936
- const models = await callListModels(adapter);
937
- expect(mockFetch).toHaveBeenCalledTimes(2);
938
- const page2Url = String(mockFetch.mock.calls[1]?.[0]);
939
- expect(page2Url).toContain('after_id=claude-opus-4-7');
940
- expect(models.map((m) => m.id)).toEqual(['claude-opus-4-7', 'claude-sonnet-4-6']);
941
- vi.unstubAllGlobals();
942
- });
943
- it('returns empty list and warns on 401', async () => {
944
- const mockFetch = vi.fn().mockResolvedValue({
945
- ok: false,
946
- status: 401,
947
- statusText: 'Unauthorized',
948
- });
949
- vi.stubGlobal('fetch', mockFetch);
950
- const warn = vi.spyOn(console, 'warn').mockImplementation(() => { });
951
- const adapter = createAnthropicAdapter({ apiKey: 'bad-key' });
952
- const models = await callListModels(adapter);
953
- expect(models).toEqual([]);
954
- expect(warn).toHaveBeenCalled();
955
- warn.mockRestore();
956
- vi.unstubAllGlobals();
957
- });
958
- it('filters by kind (Anthropic models are all text)', async () => {
959
- const mockFetch = vi.fn().mockResolvedValue({
960
- ok: true,
961
- json: async () => ({
962
- data: [
963
- { id: 'claude-opus-4-7', display_name: 'Claude Opus 4.7' },
964
- { id: 'claude-sonnet-4-6', display_name: 'Claude Sonnet 4.6' },
965
- ],
966
- has_more: false,
967
- }),
968
- });
969
- vi.stubGlobal('fetch', mockFetch);
970
- const adapter = createAnthropicAdapter({ apiKey: 'ant-test' });
971
- const embeddings = await callListModels(adapter, { kind: 'embedding' });
972
- expect(embeddings).toEqual([]);
973
- const texts = await callListModels(adapter, { kind: 'text' });
974
- expect(texts.map((m) => m.id)).toEqual(['claude-opus-4-7', 'claude-sonnet-4-6']);
975
- vi.unstubAllGlobals();
976
- });
977
- });
978
- });
979
- //# sourceMappingURL=provider.test.js.map