@plumbus/core 0.3.3 → 0.4.1

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 (593) 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/ai/security.d.ts.map +1 -1
  15. package/dist/ai/security.js +14 -1
  16. package/dist/ai/security.js.map +1 -1
  17. package/dist/api/route-generator.d.ts.map +1 -1
  18. package/dist/api/route-generator.js +57 -11
  19. package/dist/api/route-generator.js.map +1 -1
  20. package/dist/auth/adapter.d.ts +0 -4
  21. package/dist/auth/adapter.d.ts.map +1 -1
  22. package/dist/auth/adapter.js +4 -0
  23. package/dist/auth/adapter.js.map +1 -1
  24. package/dist/auth/saml-adapter.d.ts.map +1 -1
  25. package/dist/auth/saml-adapter.js +73 -120
  26. package/dist/auth/saml-adapter.js.map +1 -1
  27. package/dist/auth/scim.d.ts.map +1 -1
  28. package/dist/auth/scim.js +6 -1
  29. package/dist/auth/scim.js.map +1 -1
  30. package/dist/cli/cli.d.ts.map +1 -1
  31. package/dist/cli/cli.js +3 -1
  32. package/dist/cli/cli.js.map +1 -1
  33. package/dist/cli/commands/browser-extension.d.ts +10 -0
  34. package/dist/cli/commands/browser-extension.d.ts.map +1 -0
  35. package/dist/cli/commands/browser-extension.js +235 -0
  36. package/dist/cli/commands/browser-extension.js.map +1 -0
  37. package/dist/cli/commands/dev.d.ts +4 -1
  38. package/dist/cli/commands/dev.d.ts.map +1 -1
  39. package/dist/cli/commands/dev.js +21 -30
  40. package/dist/cli/commands/dev.js.map +1 -1
  41. package/dist/cli/commands/doctor.d.ts +6 -0
  42. package/dist/cli/commands/doctor.d.ts.map +1 -1
  43. package/dist/cli/commands/doctor.js +146 -2
  44. package/dist/cli/commands/doctor.js.map +1 -1
  45. package/dist/cli/commands/generate.d.ts.map +1 -1
  46. package/dist/cli/commands/generate.js +18 -0
  47. package/dist/cli/commands/generate.js.map +1 -1
  48. package/dist/cli/commands/index.d.ts +2 -0
  49. package/dist/cli/commands/index.d.ts.map +1 -1
  50. package/dist/cli/commands/index.js +2 -0
  51. package/dist/cli/commands/index.js.map +1 -1
  52. package/dist/cli/commands/init.d.ts +1 -1
  53. package/dist/cli/commands/init.d.ts.map +1 -1
  54. package/dist/cli/commands/init.js +122 -3
  55. package/dist/cli/commands/init.js.map +1 -1
  56. package/dist/cli/commands/mcp.d.ts +9 -0
  57. package/dist/cli/commands/mcp.d.ts.map +1 -0
  58. package/dist/cli/commands/mcp.js +125 -0
  59. package/dist/cli/commands/mcp.js.map +1 -0
  60. package/dist/cli/commands/migrate.d.ts.map +1 -1
  61. package/dist/cli/commands/migrate.js +2 -1
  62. package/dist/cli/commands/migrate.js.map +1 -1
  63. package/dist/cli/commands/start.d.ts +4 -1
  64. package/dist/cli/commands/start.d.ts.map +1 -1
  65. package/dist/cli/commands/start.js +20 -29
  66. package/dist/cli/commands/start.js.map +1 -1
  67. package/dist/cli/commands/translation.d.ts.map +1 -1
  68. package/dist/cli/commands/translation.js +22 -10
  69. package/dist/cli/commands/translation.js.map +1 -1
  70. package/dist/cli/mcp-serve-context.d.ts +13 -0
  71. package/dist/cli/mcp-serve-context.d.ts.map +1 -0
  72. package/dist/cli/mcp-serve-context.js +119 -0
  73. package/dist/cli/mcp-serve-context.js.map +1 -0
  74. package/dist/cli/migrate-snapshot-schema.d.ts +15 -0
  75. package/dist/cli/migrate-snapshot-schema.d.ts.map +1 -0
  76. package/dist/cli/migrate-snapshot-schema.js +23 -0
  77. package/dist/cli/migrate-snapshot-schema.js.map +1 -0
  78. package/dist/cli/project-root.d.ts +9 -0
  79. package/dist/cli/project-root.d.ts.map +1 -0
  80. package/dist/cli/project-root.js +41 -0
  81. package/dist/cli/project-root.js.map +1 -0
  82. package/dist/cli/scaffold-write.d.ts +17 -0
  83. package/dist/cli/scaffold-write.d.ts.map +1 -0
  84. package/dist/cli/scaffold-write.js +40 -0
  85. package/dist/cli/scaffold-write.js.map +1 -0
  86. package/dist/cli/templates/resources.d.ts.map +1 -1
  87. package/dist/cli/templates/resources.js +7 -1
  88. package/dist/cli/templates/resources.js.map +1 -1
  89. package/dist/cli/utils.d.ts +2 -0
  90. package/dist/cli/utils.d.ts.map +1 -1
  91. package/dist/cli/utils.js +14 -0
  92. package/dist/cli/utils.js.map +1 -1
  93. package/dist/config/loader.d.ts +1 -1
  94. package/dist/config/loader.d.ts.map +1 -1
  95. package/dist/config/loader.js +46 -27
  96. package/dist/config/loader.js.map +1 -1
  97. package/dist/data/connection.d.ts +16 -0
  98. package/dist/data/connection.d.ts.map +1 -0
  99. package/dist/data/connection.js +27 -0
  100. package/dist/data/connection.js.map +1 -0
  101. package/dist/data/index.d.ts +1 -0
  102. package/dist/data/index.d.ts.map +1 -1
  103. package/dist/data/index.js +1 -0
  104. package/dist/data/index.js.map +1 -1
  105. package/dist/data/migration.d.ts.map +1 -1
  106. package/dist/data/migration.js +24 -0
  107. package/dist/data/migration.js.map +1 -1
  108. package/dist/data/repository.d.ts.map +1 -1
  109. package/dist/data/repository.js +32 -9
  110. package/dist/data/repository.js.map +1 -1
  111. package/dist/define/defineCapability.d.ts +4 -2
  112. package/dist/define/defineCapability.d.ts.map +1 -1
  113. package/dist/define/defineCapability.js +68 -8
  114. package/dist/define/defineCapability.js.map +1 -1
  115. package/dist/define/defineEntity.d.ts.map +1 -1
  116. package/dist/define/defineEntity.js +8 -4
  117. package/dist/define/defineEntity.js.map +1 -1
  118. package/dist/define/defineEvent.d.ts.map +1 -1
  119. package/dist/define/defineEvent.js +7 -3
  120. package/dist/define/defineEvent.js.map +1 -1
  121. package/dist/define/defineFlow.d.ts.map +1 -1
  122. package/dist/define/defineFlow.js +11 -5
  123. package/dist/define/defineFlow.js.map +1 -1
  124. package/dist/define/definePrompt.d.ts.map +1 -1
  125. package/dist/define/definePrompt.js +11 -5
  126. package/dist/define/definePrompt.js.map +1 -1
  127. package/dist/define/defineTranslation.d.ts.map +1 -1
  128. package/dist/define/defineTranslation.js +12 -10
  129. package/dist/define/defineTranslation.js.map +1 -1
  130. package/dist/define/validation-error.d.ts +2 -0
  131. package/dist/define/validation-error.d.ts.map +1 -0
  132. package/dist/define/validation-error.js +9 -0
  133. package/dist/define/validation-error.js.map +1 -0
  134. package/dist/errors/hints.d.ts +13 -0
  135. package/dist/errors/hints.d.ts.map +1 -0
  136. package/dist/errors/hints.js +15 -0
  137. package/dist/errors/hints.js.map +1 -0
  138. package/dist/errors/hook-log.d.ts +3 -0
  139. package/dist/errors/hook-log.d.ts.map +1 -0
  140. package/dist/errors/hook-log.js +6 -0
  141. package/dist/errors/hook-log.js.map +1 -0
  142. package/dist/errors/http.d.ts +19 -0
  143. package/dist/errors/http.d.ts.map +1 -1
  144. package/dist/errors/http.js +32 -2
  145. package/dist/errors/http.js.map +1 -1
  146. package/dist/errors/safe-metadata.d.ts +5 -0
  147. package/dist/errors/safe-metadata.d.ts.map +1 -0
  148. package/dist/errors/safe-metadata.js +24 -0
  149. package/dist/errors/safe-metadata.js.map +1 -0
  150. package/dist/execution/auth-roles.d.ts +6 -0
  151. package/dist/execution/auth-roles.d.ts.map +1 -0
  152. package/dist/execution/auth-roles.js +12 -0
  153. package/dist/execution/auth-roles.js.map +1 -0
  154. package/dist/execution/authorization.d.ts.map +1 -1
  155. package/dist/execution/authorization.js +3 -5
  156. package/dist/execution/authorization.js.map +1 -1
  157. package/dist/execution/context-factory.d.ts +2 -17
  158. package/dist/execution/context-factory.d.ts.map +1 -1
  159. package/dist/execution/context-factory.js +1 -0
  160. package/dist/execution/context-factory.js.map +1 -1
  161. package/dist/flows/engine.js +1 -1
  162. package/dist/flows/engine.js.map +1 -1
  163. package/dist/flows/evaluate-condition.d.ts +19 -0
  164. package/dist/flows/evaluate-condition.d.ts.map +1 -0
  165. package/dist/flows/evaluate-condition.js +276 -0
  166. package/dist/flows/evaluate-condition.js.map +1 -0
  167. package/dist/flows/index.d.ts +1 -0
  168. package/dist/flows/index.d.ts.map +1 -1
  169. package/dist/flows/index.js +2 -0
  170. package/dist/flows/index.js.map +1 -1
  171. package/dist/flows/step-executor.d.ts.map +1 -1
  172. package/dist/flows/step-executor.js +15 -7
  173. package/dist/flows/step-executor.js.map +1 -1
  174. package/dist/governance/rules/architecture.d.ts.map +1 -1
  175. package/dist/governance/rules/architecture.js +2 -0
  176. package/dist/governance/rules/architecture.js.map +1 -1
  177. package/dist/governance/rules/index.d.ts +1 -0
  178. package/dist/governance/rules/index.d.ts.map +1 -1
  179. package/dist/governance/rules/index.js +1 -0
  180. package/dist/governance/rules/index.js.map +1 -1
  181. package/dist/governance/rules/mcp.d.ts +5 -0
  182. package/dist/governance/rules/mcp.d.ts.map +1 -0
  183. package/dist/governance/rules/mcp.js +34 -0
  184. package/dist/governance/rules/mcp.js.map +1 -0
  185. package/dist/index.d.ts +1 -0
  186. package/dist/index.d.ts.map +1 -1
  187. package/dist/index.js +1 -0
  188. package/dist/index.js.map +1 -1
  189. package/dist/mcp/exposure.d.ts +4 -0
  190. package/dist/mcp/exposure.d.ts.map +1 -0
  191. package/dist/mcp/exposure.js +5 -0
  192. package/dist/mcp/exposure.js.map +1 -0
  193. package/dist/mcp/index.d.ts +4 -0
  194. package/dist/mcp/index.d.ts.map +1 -0
  195. package/dist/mcp/index.js +6 -0
  196. package/dist/mcp/index.js.map +1 -0
  197. package/dist/mcp/manifest-generator.d.ts +19 -0
  198. package/dist/mcp/manifest-generator.d.ts.map +1 -0
  199. package/dist/mcp/manifest-generator.js +29 -0
  200. package/dist/mcp/manifest-generator.js.map +1 -0
  201. package/dist/mcp/skill-generator.d.ts +4 -0
  202. package/dist/mcp/skill-generator.d.ts.map +1 -0
  203. package/dist/mcp/skill-generator.js +59 -0
  204. package/dist/mcp/skill-generator.js.map +1 -0
  205. package/dist/schema/zod-input-to-json-schema.d.ts +4 -0
  206. package/dist/schema/zod-input-to-json-schema.d.ts.map +1 -0
  207. package/dist/schema/zod-input-to-json-schema.js +6 -0
  208. package/dist/schema/zod-input-to-json-schema.js.map +1 -0
  209. package/dist/server/bootstrap.d.ts.map +1 -1
  210. package/dist/server/bootstrap.js +8 -3
  211. package/dist/server/bootstrap.js.map +1 -1
  212. package/dist/testing/context.d.ts +2 -0
  213. package/dist/testing/context.d.ts.map +1 -1
  214. package/dist/testing/context.js +1 -0
  215. package/dist/testing/context.js.map +1 -1
  216. package/dist/testing/e2e.js +1 -1
  217. package/dist/testing/e2e.js.map +1 -1
  218. package/dist/types/capability.d.ts +13 -0
  219. package/dist/types/capability.d.ts.map +1 -1
  220. package/dist/types/config.d.ts +9 -0
  221. package/dist/types/config.d.ts.map +1 -1
  222. package/dist/types/context.d.ts +31 -0
  223. package/dist/types/context.d.ts.map +1 -1
  224. package/dist/types/deep-freeze.d.ts +6 -0
  225. package/dist/types/deep-freeze.d.ts.map +1 -0
  226. package/dist/types/deep-freeze.js +38 -0
  227. package/dist/types/deep-freeze.js.map +1 -0
  228. package/dist/types/enums.d.ts +5 -0
  229. package/dist/types/enums.d.ts.map +1 -1
  230. package/dist/types/enums.js +5 -0
  231. package/dist/types/enums.js.map +1 -1
  232. package/dist/types/index.d.ts +2 -2
  233. package/dist/types/index.d.ts.map +1 -1
  234. package/dist/types/index.js.map +1 -1
  235. package/dist/types/security.d.ts +3 -1
  236. package/dist/types/security.d.ts.map +1 -1
  237. package/instructions/capabilities.md +36 -0
  238. package/instructions/cli.md +44 -0
  239. package/instructions/framework.md +20 -4
  240. package/instructions/mcp.md +20 -0
  241. package/package.json +21 -2
  242. package/dist/ai/__tests__/ai-service.test.d.ts +0 -2
  243. package/dist/ai/__tests__/ai-service.test.d.ts.map +0 -1
  244. package/dist/ai/__tests__/ai-service.test.js +0 -1078
  245. package/dist/ai/__tests__/ai-service.test.js.map +0 -1
  246. package/dist/ai/__tests__/chunking.test.d.ts +0 -2
  247. package/dist/ai/__tests__/chunking.test.d.ts.map +0 -1
  248. package/dist/ai/__tests__/chunking.test.js +0 -78
  249. package/dist/ai/__tests__/chunking.test.js.map +0 -1
  250. package/dist/ai/__tests__/cost-tracker.test.d.ts +0 -2
  251. package/dist/ai/__tests__/cost-tracker.test.d.ts.map +0 -1
  252. package/dist/ai/__tests__/cost-tracker.test.js +0 -201
  253. package/dist/ai/__tests__/cost-tracker.test.js.map +0 -1
  254. package/dist/ai/__tests__/explainability.test.d.ts +0 -2
  255. package/dist/ai/__tests__/explainability.test.d.ts.map +0 -1
  256. package/dist/ai/__tests__/explainability.test.js +0 -100
  257. package/dist/ai/__tests__/explainability.test.js.map +0 -1
  258. package/dist/ai/__tests__/model-pricing.test.d.ts +0 -2
  259. package/dist/ai/__tests__/model-pricing.test.d.ts.map +0 -1
  260. package/dist/ai/__tests__/model-pricing.test.js +0 -156
  261. package/dist/ai/__tests__/model-pricing.test.js.map +0 -1
  262. package/dist/ai/__tests__/prompt-registry.test.d.ts +0 -2
  263. package/dist/ai/__tests__/prompt-registry.test.d.ts.map +0 -1
  264. package/dist/ai/__tests__/prompt-registry.test.js +0 -56
  265. package/dist/ai/__tests__/prompt-registry.test.js.map +0 -1
  266. package/dist/ai/__tests__/provider.test.d.ts +0 -3
  267. package/dist/ai/__tests__/provider.test.d.ts.map +0 -1
  268. package/dist/ai/__tests__/provider.test.js +0 -979
  269. package/dist/ai/__tests__/provider.test.js.map +0 -1
  270. package/dist/ai/__tests__/rag-pipeline.test.d.ts +0 -2
  271. package/dist/ai/__tests__/rag-pipeline.test.d.ts.map +0 -1
  272. package/dist/ai/__tests__/rag-pipeline.test.js +0 -174
  273. package/dist/ai/__tests__/rag-pipeline.test.js.map +0 -1
  274. package/dist/ai/__tests__/security.test.d.ts +0 -2
  275. package/dist/ai/__tests__/security.test.d.ts.map +0 -1
  276. package/dist/ai/__tests__/security.test.js +0 -89
  277. package/dist/ai/__tests__/security.test.js.map +0 -1
  278. package/dist/ai/__tests__/usage-client.test.d.ts +0 -2
  279. package/dist/ai/__tests__/usage-client.test.d.ts.map +0 -1
  280. package/dist/ai/__tests__/usage-client.test.js +0 -120
  281. package/dist/ai/__tests__/usage-client.test.js.map +0 -1
  282. package/dist/ai/__tests__/validation.test.d.ts +0 -2
  283. package/dist/ai/__tests__/validation.test.d.ts.map +0 -1
  284. package/dist/ai/__tests__/validation.test.js +0 -384
  285. package/dist/ai/__tests__/validation.test.js.map +0 -1
  286. package/dist/ai/__tests__/zod-to-provider-schema.test.d.ts +0 -2
  287. package/dist/ai/__tests__/zod-to-provider-schema.test.d.ts.map +0 -1
  288. package/dist/ai/__tests__/zod-to-provider-schema.test.js +0 -60
  289. package/dist/ai/__tests__/zod-to-provider-schema.test.js.map +0 -1
  290. package/dist/api/__tests__/route-generator.test.d.ts +0 -2
  291. package/dist/api/__tests__/route-generator.test.d.ts.map +0 -1
  292. package/dist/api/__tests__/route-generator.test.js +0 -216
  293. package/dist/api/__tests__/route-generator.test.js.map +0 -1
  294. package/dist/audit/__tests__/service.test.d.ts +0 -2
  295. package/dist/audit/__tests__/service.test.d.ts.map +0 -1
  296. package/dist/audit/__tests__/service.test.js +0 -97
  297. package/dist/audit/__tests__/service.test.js.map +0 -1
  298. package/dist/auth/__tests__/adapter.test.d.ts +0 -2
  299. package/dist/auth/__tests__/adapter.test.d.ts.map +0 -1
  300. package/dist/auth/__tests__/adapter.test.js +0 -218
  301. package/dist/auth/__tests__/adapter.test.js.map +0 -1
  302. package/dist/auth/__tests__/oidc-adapter.test.d.ts +0 -2
  303. package/dist/auth/__tests__/oidc-adapter.test.d.ts.map +0 -1
  304. package/dist/auth/__tests__/oidc-adapter.test.js +0 -232
  305. package/dist/auth/__tests__/oidc-adapter.test.js.map +0 -1
  306. package/dist/auth/__tests__/password.test.d.ts +0 -2
  307. package/dist/auth/__tests__/password.test.d.ts.map +0 -1
  308. package/dist/auth/__tests__/password.test.js +0 -30
  309. package/dist/auth/__tests__/password.test.js.map +0 -1
  310. package/dist/auth/__tests__/saml-adapter.test.d.ts +0 -2
  311. package/dist/auth/__tests__/saml-adapter.test.d.ts.map +0 -1
  312. package/dist/auth/__tests__/saml-adapter.test.js +0 -188
  313. package/dist/auth/__tests__/saml-adapter.test.js.map +0 -1
  314. package/dist/auth/__tests__/scim.test.d.ts +0 -2
  315. package/dist/auth/__tests__/scim.test.d.ts.map +0 -1
  316. package/dist/auth/__tests__/scim.test.js +0 -306
  317. package/dist/auth/__tests__/scim.test.js.map +0 -1
  318. package/dist/cli/__tests__/agent.test.d.ts +0 -2
  319. package/dist/cli/__tests__/agent.test.d.ts.map +0 -1
  320. package/dist/cli/__tests__/agent.test.js +0 -63
  321. package/dist/cli/__tests__/agent.test.js.map +0 -1
  322. package/dist/cli/__tests__/certify.test.d.ts +0 -2
  323. package/dist/cli/__tests__/certify.test.d.ts.map +0 -1
  324. package/dist/cli/__tests__/certify.test.js +0 -97
  325. package/dist/cli/__tests__/certify.test.js.map +0 -1
  326. package/dist/cli/__tests__/cli.test.d.ts +0 -2
  327. package/dist/cli/__tests__/cli.test.d.ts.map +0 -1
  328. package/dist/cli/__tests__/cli.test.js +0 -123
  329. package/dist/cli/__tests__/cli.test.js.map +0 -1
  330. package/dist/cli/__tests__/create.test.d.ts +0 -2
  331. package/dist/cli/__tests__/create.test.d.ts.map +0 -1
  332. package/dist/cli/__tests__/create.test.js +0 -183
  333. package/dist/cli/__tests__/create.test.js.map +0 -1
  334. package/dist/cli/__tests__/dev.test.d.ts +0 -2
  335. package/dist/cli/__tests__/dev.test.d.ts.map +0 -1
  336. package/dist/cli/__tests__/dev.test.js +0 -132
  337. package/dist/cli/__tests__/dev.test.js.map +0 -1
  338. package/dist/cli/__tests__/discover.test.d.ts +0 -2
  339. package/dist/cli/__tests__/discover.test.d.ts.map +0 -1
  340. package/dist/cli/__tests__/discover.test.js +0 -48
  341. package/dist/cli/__tests__/discover.test.js.map +0 -1
  342. package/dist/cli/__tests__/doctor.test.d.ts +0 -2
  343. package/dist/cli/__tests__/doctor.test.d.ts.map +0 -1
  344. package/dist/cli/__tests__/doctor.test.js +0 -172
  345. package/dist/cli/__tests__/doctor.test.js.map +0 -1
  346. package/dist/cli/__tests__/generate.test.d.ts +0 -2
  347. package/dist/cli/__tests__/generate.test.d.ts.map +0 -1
  348. package/dist/cli/__tests__/generate.test.js +0 -478
  349. package/dist/cli/__tests__/generate.test.js.map +0 -1
  350. package/dist/cli/__tests__/init.test.d.ts +0 -2
  351. package/dist/cli/__tests__/init.test.d.ts.map +0 -1
  352. package/dist/cli/__tests__/init.test.js +0 -236
  353. package/dist/cli/__tests__/init.test.js.map +0 -1
  354. package/dist/cli/__tests__/migrate.test.d.ts +0 -2
  355. package/dist/cli/__tests__/migrate.test.d.ts.map +0 -1
  356. package/dist/cli/__tests__/migrate.test.js +0 -442
  357. package/dist/cli/__tests__/migrate.test.js.map +0 -1
  358. package/dist/cli/__tests__/start.test.d.ts +0 -2
  359. package/dist/cli/__tests__/start.test.d.ts.map +0 -1
  360. package/dist/cli/__tests__/start.test.js +0 -91
  361. package/dist/cli/__tests__/start.test.js.map +0 -1
  362. package/dist/cli/__tests__/templates.test.d.ts +0 -2
  363. package/dist/cli/__tests__/templates.test.d.ts.map +0 -1
  364. package/dist/cli/__tests__/templates.test.js +0 -49
  365. package/dist/cli/__tests__/templates.test.js.map +0 -1
  366. package/dist/cli/__tests__/test.test.d.ts +0 -2
  367. package/dist/cli/__tests__/test.test.d.ts.map +0 -1
  368. package/dist/cli/__tests__/test.test.js +0 -40
  369. package/dist/cli/__tests__/test.test.js.map +0 -1
  370. package/dist/cli/__tests__/ui.test.d.ts +0 -2
  371. package/dist/cli/__tests__/ui.test.d.ts.map +0 -1
  372. package/dist/cli/__tests__/ui.test.js +0 -76
  373. package/dist/cli/__tests__/ui.test.js.map +0 -1
  374. package/dist/cli/__tests__/upgrade.test.d.ts +0 -2
  375. package/dist/cli/__tests__/upgrade.test.d.ts.map +0 -1
  376. package/dist/cli/__tests__/upgrade.test.js +0 -20
  377. package/dist/cli/__tests__/upgrade.test.js.map +0 -1
  378. package/dist/cli/__tests__/utils.test.d.ts +0 -2
  379. package/dist/cli/__tests__/utils.test.d.ts.map +0 -1
  380. package/dist/cli/__tests__/utils.test.js +0 -255
  381. package/dist/cli/__tests__/utils.test.js.map +0 -1
  382. package/dist/cli/__tests__/verify.test.d.ts +0 -2
  383. package/dist/cli/__tests__/verify.test.d.ts.map +0 -1
  384. package/dist/cli/__tests__/verify.test.js +0 -138
  385. package/dist/cli/__tests__/verify.test.js.map +0 -1
  386. package/dist/cli/commands/__tests__/commands.test.d.ts +0 -2
  387. package/dist/cli/commands/__tests__/commands.test.d.ts.map +0 -1
  388. package/dist/cli/commands/__tests__/commands.test.js +0 -185
  389. package/dist/cli/commands/__tests__/commands.test.js.map +0 -1
  390. package/dist/config/__tests__/loader.test.d.ts +0 -2
  391. package/dist/config/__tests__/loader.test.d.ts.map +0 -1
  392. package/dist/config/__tests__/loader.test.js +0 -462
  393. package/dist/config/__tests__/loader.test.js.map +0 -1
  394. package/dist/data/__tests__/drift-inspector.test.d.ts +0 -2
  395. package/dist/data/__tests__/drift-inspector.test.d.ts.map +0 -1
  396. package/dist/data/__tests__/drift-inspector.test.js +0 -170
  397. package/dist/data/__tests__/drift-inspector.test.js.map +0 -1
  398. package/dist/data/__tests__/framework-migration-rewriter.test.d.ts +0 -2
  399. package/dist/data/__tests__/framework-migration-rewriter.test.d.ts.map +0 -1
  400. package/dist/data/__tests__/framework-migration-rewriter.test.js +0 -329
  401. package/dist/data/__tests__/framework-migration-rewriter.test.js.map +0 -1
  402. package/dist/data/__tests__/migration.test.d.ts +0 -2
  403. package/dist/data/__tests__/migration.test.d.ts.map +0 -1
  404. package/dist/data/__tests__/migration.test.js +0 -145
  405. package/dist/data/__tests__/migration.test.js.map +0 -1
  406. package/dist/data/__tests__/registry.test.d.ts +0 -2
  407. package/dist/data/__tests__/registry.test.d.ts.map +0 -1
  408. package/dist/data/__tests__/registry.test.js +0 -70
  409. package/dist/data/__tests__/registry.test.js.map +0 -1
  410. package/dist/data/__tests__/repository.test.d.ts +0 -2
  411. package/dist/data/__tests__/repository.test.d.ts.map +0 -1
  412. package/dist/data/__tests__/repository.test.js +0 -559
  413. package/dist/data/__tests__/repository.test.js.map +0 -1
  414. package/dist/data/__tests__/schema-generator.test.d.ts +0 -2
  415. package/dist/data/__tests__/schema-generator.test.d.ts.map +0 -1
  416. package/dist/data/__tests__/schema-generator.test.js +0 -112
  417. package/dist/data/__tests__/schema-generator.test.js.map +0 -1
  418. package/dist/define/__tests__/defineCapability.test.d.ts +0 -2
  419. package/dist/define/__tests__/defineCapability.test.d.ts.map +0 -1
  420. package/dist/define/__tests__/defineCapability.test.js +0 -66
  421. package/dist/define/__tests__/defineCapability.test.js.map +0 -1
  422. package/dist/define/__tests__/defineEntity.test.d.ts +0 -2
  423. package/dist/define/__tests__/defineEntity.test.d.ts.map +0 -1
  424. package/dist/define/__tests__/defineEntity.test.js +0 -50
  425. package/dist/define/__tests__/defineEntity.test.js.map +0 -1
  426. package/dist/define/__tests__/defineEvent.test.d.ts +0 -2
  427. package/dist/define/__tests__/defineEvent.test.d.ts.map +0 -1
  428. package/dist/define/__tests__/defineEvent.test.js +0 -35
  429. package/dist/define/__tests__/defineEvent.test.js.map +0 -1
  430. package/dist/define/__tests__/defineFlow.test.d.ts +0 -2
  431. package/dist/define/__tests__/defineFlow.test.d.ts.map +0 -1
  432. package/dist/define/__tests__/defineFlow.test.js +0 -49
  433. package/dist/define/__tests__/defineFlow.test.js.map +0 -1
  434. package/dist/define/__tests__/definePrompt.test.d.ts +0 -2
  435. package/dist/define/__tests__/definePrompt.test.d.ts.map +0 -1
  436. package/dist/define/__tests__/definePrompt.test.js +0 -77
  437. package/dist/define/__tests__/definePrompt.test.js.map +0 -1
  438. package/dist/define/__tests__/defineTranslation.test.d.ts +0 -2
  439. package/dist/define/__tests__/defineTranslation.test.d.ts.map +0 -1
  440. package/dist/define/__tests__/defineTranslation.test.js +0 -90
  441. package/dist/define/__tests__/defineTranslation.test.js.map +0 -1
  442. package/dist/errors/__tests__/errors.test.d.ts +0 -2
  443. package/dist/errors/__tests__/errors.test.d.ts.map +0 -1
  444. package/dist/errors/__tests__/errors.test.js +0 -70
  445. package/dist/errors/__tests__/errors.test.js.map +0 -1
  446. package/dist/errors/__tests__/http.test.d.ts +0 -2
  447. package/dist/errors/__tests__/http.test.d.ts.map +0 -1
  448. package/dist/errors/__tests__/http.test.js +0 -35
  449. package/dist/errors/__tests__/http.test.js.map +0 -1
  450. package/dist/events/__tests__/consumer-registry.test.d.ts +0 -2
  451. package/dist/events/__tests__/consumer-registry.test.d.ts.map +0 -1
  452. package/dist/events/__tests__/consumer-registry.test.js +0 -68
  453. package/dist/events/__tests__/consumer-registry.test.js.map +0 -1
  454. package/dist/events/__tests__/dispatcher.test.d.ts +0 -2
  455. package/dist/events/__tests__/dispatcher.test.d.ts.map +0 -1
  456. package/dist/events/__tests__/dispatcher.test.js +0 -168
  457. package/dist/events/__tests__/dispatcher.test.js.map +0 -1
  458. package/dist/events/__tests__/event-emitter.test.d.ts +0 -2
  459. package/dist/events/__tests__/event-emitter.test.d.ts.map +0 -1
  460. package/dist/events/__tests__/event-emitter.test.js +0 -172
  461. package/dist/events/__tests__/event-emitter.test.js.map +0 -1
  462. package/dist/events/__tests__/event-registry.test.d.ts +0 -2
  463. package/dist/events/__tests__/event-registry.test.d.ts.map +0 -1
  464. package/dist/events/__tests__/event-registry.test.js +0 -55
  465. package/dist/events/__tests__/event-registry.test.js.map +0 -1
  466. package/dist/events/__tests__/event-worker.test.d.ts +0 -2
  467. package/dist/events/__tests__/event-worker.test.d.ts.map +0 -1
  468. package/dist/events/__tests__/event-worker.test.js +0 -174
  469. package/dist/events/__tests__/event-worker.test.js.map +0 -1
  470. package/dist/events/__tests__/queue.test.d.ts +0 -2
  471. package/dist/events/__tests__/queue.test.d.ts.map +0 -1
  472. package/dist/events/__tests__/queue.test.js +0 -73
  473. package/dist/events/__tests__/queue.test.js.map +0 -1
  474. package/dist/execution/__tests__/authorization.test.d.ts +0 -2
  475. package/dist/execution/__tests__/authorization.test.d.ts.map +0 -1
  476. package/dist/execution/__tests__/authorization.test.js +0 -99
  477. package/dist/execution/__tests__/authorization.test.js.map +0 -1
  478. package/dist/execution/__tests__/capability-executor.test.d.ts +0 -2
  479. package/dist/execution/__tests__/capability-executor.test.d.ts.map +0 -1
  480. package/dist/execution/__tests__/capability-executor.test.js +0 -212
  481. package/dist/execution/__tests__/capability-executor.test.js.map +0 -1
  482. package/dist/execution/__tests__/capability-registry.test.d.ts +0 -2
  483. package/dist/execution/__tests__/capability-registry.test.d.ts.map +0 -1
  484. package/dist/execution/__tests__/capability-registry.test.js +0 -50
  485. package/dist/execution/__tests__/capability-registry.test.js.map +0 -1
  486. package/dist/execution/__tests__/context-factory.test.d.ts +0 -2
  487. package/dist/execution/__tests__/context-factory.test.d.ts.map +0 -1
  488. package/dist/execution/__tests__/context-factory.test.js +0 -133
  489. package/dist/execution/__tests__/context-factory.test.js.map +0 -1
  490. package/dist/explanation/__tests__/tracker.test.d.ts +0 -2
  491. package/dist/explanation/__tests__/tracker.test.d.ts.map +0 -1
  492. package/dist/explanation/__tests__/tracker.test.js +0 -169
  493. package/dist/explanation/__tests__/tracker.test.js.map +0 -1
  494. package/dist/fields/__tests__/fields.test.d.ts +0 -2
  495. package/dist/fields/__tests__/fields.test.d.ts.map +0 -1
  496. package/dist/fields/__tests__/fields.test.js +0 -60
  497. package/dist/fields/__tests__/fields.test.js.map +0 -1
  498. package/dist/flows/__tests__/flow-engine.test.d.ts +0 -2
  499. package/dist/flows/__tests__/flow-engine.test.d.ts.map +0 -1
  500. package/dist/flows/__tests__/flow-engine.test.js +0 -797
  501. package/dist/flows/__tests__/flow-engine.test.js.map +0 -1
  502. package/dist/flows/__tests__/flow-registry.test.d.ts +0 -2
  503. package/dist/flows/__tests__/flow-registry.test.d.ts.map +0 -1
  504. package/dist/flows/__tests__/flow-registry.test.js +0 -69
  505. package/dist/flows/__tests__/flow-registry.test.js.map +0 -1
  506. package/dist/flows/__tests__/flow-service.test.d.ts +0 -2
  507. package/dist/flows/__tests__/flow-service.test.d.ts.map +0 -1
  508. package/dist/flows/__tests__/flow-service.test.js +0 -55
  509. package/dist/flows/__tests__/flow-service.test.js.map +0 -1
  510. package/dist/flows/__tests__/scheduler.test.d.ts +0 -2
  511. package/dist/flows/__tests__/scheduler.test.d.ts.map +0 -1
  512. package/dist/flows/__tests__/scheduler.test.js +0 -120
  513. package/dist/flows/__tests__/scheduler.test.js.map +0 -1
  514. package/dist/flows/__tests__/state-machine.test.d.ts +0 -2
  515. package/dist/flows/__tests__/state-machine.test.d.ts.map +0 -1
  516. package/dist/flows/__tests__/state-machine.test.js +0 -62
  517. package/dist/flows/__tests__/state-machine.test.js.map +0 -1
  518. package/dist/flows/__tests__/step-executor.test.d.ts +0 -2
  519. package/dist/flows/__tests__/step-executor.test.d.ts.map +0 -1
  520. package/dist/flows/__tests__/step-executor.test.js +0 -201
  521. package/dist/flows/__tests__/step-executor.test.js.map +0 -1
  522. package/dist/flows/__tests__/triggers.test.d.ts +0 -2
  523. package/dist/flows/__tests__/triggers.test.d.ts.map +0 -1
  524. package/dist/flows/__tests__/triggers.test.js +0 -98
  525. package/dist/flows/__tests__/triggers.test.js.map +0 -1
  526. package/dist/governance/__tests__/overrides.test.d.ts +0 -2
  527. package/dist/governance/__tests__/overrides.test.d.ts.map +0 -1
  528. package/dist/governance/__tests__/overrides.test.js +0 -123
  529. package/dist/governance/__tests__/overrides.test.js.map +0 -1
  530. package/dist/governance/__tests__/policies.test.d.ts +0 -2
  531. package/dist/governance/__tests__/policies.test.d.ts.map +0 -1
  532. package/dist/governance/__tests__/policies.test.js +0 -203
  533. package/dist/governance/__tests__/policies.test.js.map +0 -1
  534. package/dist/governance/__tests__/rules.test.d.ts +0 -2
  535. package/dist/governance/__tests__/rules.test.d.ts.map +0 -1
  536. package/dist/governance/__tests__/rules.test.js +0 -342
  537. package/dist/governance/__tests__/rules.test.js.map +0 -1
  538. package/dist/observability/__tests__/metrics.test.d.ts +0 -2
  539. package/dist/observability/__tests__/metrics.test.d.ts.map +0 -1
  540. package/dist/observability/__tests__/metrics.test.js +0 -301
  541. package/dist/observability/__tests__/metrics.test.js.map +0 -1
  542. package/dist/server/__tests__/bootstrap.test.d.ts +0 -2
  543. package/dist/server/__tests__/bootstrap.test.d.ts.map +0 -1
  544. package/dist/server/__tests__/bootstrap.test.js +0 -381
  545. package/dist/server/__tests__/bootstrap.test.js.map +0 -1
  546. package/dist/testing/__tests__/context.test.d.ts +0 -2
  547. package/dist/testing/__tests__/context.test.d.ts.map +0 -1
  548. package/dist/testing/__tests__/context.test.js +0 -341
  549. package/dist/testing/__tests__/context.test.js.map +0 -1
  550. package/dist/testing/__tests__/e2e.test.d.ts +0 -2
  551. package/dist/testing/__tests__/e2e.test.d.ts.map +0 -1
  552. package/dist/testing/__tests__/e2e.test.js +0 -119
  553. package/dist/testing/__tests__/e2e.test.js.map +0 -1
  554. package/dist/testing/__tests__/field-validation.test.d.ts +0 -2
  555. package/dist/testing/__tests__/field-validation.test.d.ts.map +0 -1
  556. package/dist/testing/__tests__/field-validation.test.js +0 -178
  557. package/dist/testing/__tests__/field-validation.test.js.map +0 -1
  558. package/dist/testing/__tests__/governance.test.d.ts +0 -2
  559. package/dist/testing/__tests__/governance.test.d.ts.map +0 -1
  560. package/dist/testing/__tests__/governance.test.js +0 -222
  561. package/dist/testing/__tests__/governance.test.js.map +0 -1
  562. package/dist/testing/__tests__/run-capability.test.d.ts +0 -2
  563. package/dist/testing/__tests__/run-capability.test.d.ts.map +0 -1
  564. package/dist/testing/__tests__/run-capability.test.js +0 -114
  565. package/dist/testing/__tests__/run-capability.test.js.map +0 -1
  566. package/dist/testing/__tests__/scaffolding.test.d.ts +0 -2
  567. package/dist/testing/__tests__/scaffolding.test.d.ts.map +0 -1
  568. package/dist/testing/__tests__/scaffolding.test.js +0 -70
  569. package/dist/testing/__tests__/scaffolding.test.js.map +0 -1
  570. package/dist/testing/__tests__/security.test.d.ts +0 -2
  571. package/dist/testing/__tests__/security.test.d.ts.map +0 -1
  572. package/dist/testing/__tests__/security.test.js +0 -186
  573. package/dist/testing/__tests__/security.test.js.map +0 -1
  574. package/dist/testing/__tests__/simulate-flow.test.d.ts +0 -2
  575. package/dist/testing/__tests__/simulate-flow.test.d.ts.map +0 -1
  576. package/dist/testing/__tests__/simulate-flow.test.js +0 -230
  577. package/dist/testing/__tests__/simulate-flow.test.js.map +0 -1
  578. package/dist/translations/__tests__/translation-resolver.test.d.ts +0 -2
  579. package/dist/translations/__tests__/translation-resolver.test.d.ts.map +0 -1
  580. package/dist/translations/__tests__/translation-resolver.test.js +0 -140
  581. package/dist/translations/__tests__/translation-resolver.test.js.map +0 -1
  582. package/dist/types/__tests__/registry.test.d.ts +0 -2
  583. package/dist/types/__tests__/registry.test.d.ts.map +0 -1
  584. package/dist/types/__tests__/registry.test.js +0 -34
  585. package/dist/types/__tests__/registry.test.js.map +0 -1
  586. package/dist/worker/__tests__/bootstrap.test.d.ts +0 -2
  587. package/dist/worker/__tests__/bootstrap.test.d.ts.map +0 -1
  588. package/dist/worker/__tests__/bootstrap.test.js +0 -268
  589. package/dist/worker/__tests__/bootstrap.test.js.map +0 -1
  590. package/dist/worker/__tests__/flow-service-wiring.test.d.ts +0 -2
  591. package/dist/worker/__tests__/flow-service-wiring.test.d.ts.map +0 -1
  592. package/dist/worker/__tests__/flow-service-wiring.test.js +0 -64
  593. 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