@dotsetlabs/bellwether 0.10.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 (403) hide show
  1. package/CHANGELOG.md +291 -0
  2. package/LICENSE +21 -0
  3. package/README.md +739 -0
  4. package/dist/auth/credentials.d.ts +64 -0
  5. package/dist/auth/credentials.js +218 -0
  6. package/dist/auth/index.d.ts +6 -0
  7. package/dist/auth/index.js +6 -0
  8. package/dist/auth/keychain.d.ts +64 -0
  9. package/dist/auth/keychain.js +268 -0
  10. package/dist/baseline/ab-testing.d.ts +80 -0
  11. package/dist/baseline/ab-testing.js +236 -0
  12. package/dist/baseline/ai-compatibility-scorer.d.ts +95 -0
  13. package/dist/baseline/ai-compatibility-scorer.js +606 -0
  14. package/dist/baseline/calibration.d.ts +77 -0
  15. package/dist/baseline/calibration.js +136 -0
  16. package/dist/baseline/category-matching.d.ts +85 -0
  17. package/dist/baseline/category-matching.js +289 -0
  18. package/dist/baseline/change-impact-analyzer.d.ts +98 -0
  19. package/dist/baseline/change-impact-analyzer.js +592 -0
  20. package/dist/baseline/comparator.d.ts +64 -0
  21. package/dist/baseline/comparator.js +916 -0
  22. package/dist/baseline/confidence.d.ts +55 -0
  23. package/dist/baseline/confidence.js +122 -0
  24. package/dist/baseline/converter.d.ts +61 -0
  25. package/dist/baseline/converter.js +585 -0
  26. package/dist/baseline/dependency-analyzer.d.ts +89 -0
  27. package/dist/baseline/dependency-analyzer.js +567 -0
  28. package/dist/baseline/deprecation-tracker.d.ts +133 -0
  29. package/dist/baseline/deprecation-tracker.js +322 -0
  30. package/dist/baseline/diff.d.ts +55 -0
  31. package/dist/baseline/diff.js +1584 -0
  32. package/dist/baseline/documentation-scorer.d.ts +205 -0
  33. package/dist/baseline/documentation-scorer.js +466 -0
  34. package/dist/baseline/embeddings.d.ts +118 -0
  35. package/dist/baseline/embeddings.js +251 -0
  36. package/dist/baseline/error-analyzer.d.ts +198 -0
  37. package/dist/baseline/error-analyzer.js +721 -0
  38. package/dist/baseline/evaluation/evaluator.d.ts +42 -0
  39. package/dist/baseline/evaluation/evaluator.js +323 -0
  40. package/dist/baseline/evaluation/expanded-dataset.d.ts +45 -0
  41. package/dist/baseline/evaluation/expanded-dataset.js +1164 -0
  42. package/dist/baseline/evaluation/golden-dataset.d.ts +58 -0
  43. package/dist/baseline/evaluation/golden-dataset.js +717 -0
  44. package/dist/baseline/evaluation/index.d.ts +15 -0
  45. package/dist/baseline/evaluation/index.js +15 -0
  46. package/dist/baseline/evaluation/types.d.ts +186 -0
  47. package/dist/baseline/evaluation/types.js +8 -0
  48. package/dist/baseline/external-dependency-detector.d.ts +181 -0
  49. package/dist/baseline/external-dependency-detector.js +524 -0
  50. package/dist/baseline/golden-output.d.ts +162 -0
  51. package/dist/baseline/golden-output.js +636 -0
  52. package/dist/baseline/health-scorer.d.ts +174 -0
  53. package/dist/baseline/health-scorer.js +451 -0
  54. package/dist/baseline/incremental-checker.d.ts +97 -0
  55. package/dist/baseline/incremental-checker.js +174 -0
  56. package/dist/baseline/index.d.ts +31 -0
  57. package/dist/baseline/index.js +42 -0
  58. package/dist/baseline/migration-generator.d.ts +137 -0
  59. package/dist/baseline/migration-generator.js +554 -0
  60. package/dist/baseline/migrations.d.ts +60 -0
  61. package/dist/baseline/migrations.js +197 -0
  62. package/dist/baseline/performance-tracker.d.ts +214 -0
  63. package/dist/baseline/performance-tracker.js +577 -0
  64. package/dist/baseline/pr-comment-generator.d.ts +117 -0
  65. package/dist/baseline/pr-comment-generator.js +546 -0
  66. package/dist/baseline/response-fingerprint.d.ts +127 -0
  67. package/dist/baseline/response-fingerprint.js +728 -0
  68. package/dist/baseline/response-schema-tracker.d.ts +129 -0
  69. package/dist/baseline/response-schema-tracker.js +420 -0
  70. package/dist/baseline/risk-scorer.d.ts +54 -0
  71. package/dist/baseline/risk-scorer.js +434 -0
  72. package/dist/baseline/saver.d.ts +89 -0
  73. package/dist/baseline/saver.js +554 -0
  74. package/dist/baseline/scenario-generator.d.ts +151 -0
  75. package/dist/baseline/scenario-generator.js +905 -0
  76. package/dist/baseline/schema-compare.d.ts +86 -0
  77. package/dist/baseline/schema-compare.js +557 -0
  78. package/dist/baseline/schema-evolution.d.ts +189 -0
  79. package/dist/baseline/schema-evolution.js +467 -0
  80. package/dist/baseline/semantic.d.ts +203 -0
  81. package/dist/baseline/semantic.js +908 -0
  82. package/dist/baseline/synonyms.d.ts +60 -0
  83. package/dist/baseline/synonyms.js +386 -0
  84. package/dist/baseline/telemetry.d.ts +165 -0
  85. package/dist/baseline/telemetry.js +294 -0
  86. package/dist/baseline/test-pruner.d.ts +120 -0
  87. package/dist/baseline/test-pruner.js +387 -0
  88. package/dist/baseline/types.d.ts +449 -0
  89. package/dist/baseline/types.js +5 -0
  90. package/dist/baseline/version.d.ts +138 -0
  91. package/dist/baseline/version.js +206 -0
  92. package/dist/cache/index.d.ts +5 -0
  93. package/dist/cache/index.js +5 -0
  94. package/dist/cache/response-cache.d.ts +151 -0
  95. package/dist/cache/response-cache.js +287 -0
  96. package/dist/ci/index.d.ts +60 -0
  97. package/dist/ci/index.js +342 -0
  98. package/dist/cli/commands/auth.d.ts +12 -0
  99. package/dist/cli/commands/auth.js +352 -0
  100. package/dist/cli/commands/badge.d.ts +3 -0
  101. package/dist/cli/commands/badge.js +74 -0
  102. package/dist/cli/commands/baseline-accept.d.ts +15 -0
  103. package/dist/cli/commands/baseline-accept.js +178 -0
  104. package/dist/cli/commands/baseline-migrate.d.ts +12 -0
  105. package/dist/cli/commands/baseline-migrate.js +164 -0
  106. package/dist/cli/commands/baseline.d.ts +14 -0
  107. package/dist/cli/commands/baseline.js +449 -0
  108. package/dist/cli/commands/beta.d.ts +10 -0
  109. package/dist/cli/commands/beta.js +231 -0
  110. package/dist/cli/commands/check.d.ts +11 -0
  111. package/dist/cli/commands/check.js +820 -0
  112. package/dist/cli/commands/cloud/badge.d.ts +3 -0
  113. package/dist/cli/commands/cloud/badge.js +74 -0
  114. package/dist/cli/commands/cloud/diff.d.ts +6 -0
  115. package/dist/cli/commands/cloud/diff.js +79 -0
  116. package/dist/cli/commands/cloud/history.d.ts +6 -0
  117. package/dist/cli/commands/cloud/history.js +102 -0
  118. package/dist/cli/commands/cloud/link.d.ts +9 -0
  119. package/dist/cli/commands/cloud/link.js +119 -0
  120. package/dist/cli/commands/cloud/login.d.ts +7 -0
  121. package/dist/cli/commands/cloud/login.js +499 -0
  122. package/dist/cli/commands/cloud/projects.d.ts +6 -0
  123. package/dist/cli/commands/cloud/projects.js +44 -0
  124. package/dist/cli/commands/cloud/shared.d.ts +7 -0
  125. package/dist/cli/commands/cloud/shared.js +42 -0
  126. package/dist/cli/commands/cloud/teams.d.ts +8 -0
  127. package/dist/cli/commands/cloud/teams.js +169 -0
  128. package/dist/cli/commands/cloud/upload.d.ts +8 -0
  129. package/dist/cli/commands/cloud/upload.js +181 -0
  130. package/dist/cli/commands/contract.d.ts +11 -0
  131. package/dist/cli/commands/contract.js +280 -0
  132. package/dist/cli/commands/discover.d.ts +3 -0
  133. package/dist/cli/commands/discover.js +82 -0
  134. package/dist/cli/commands/eval.d.ts +9 -0
  135. package/dist/cli/commands/eval.js +187 -0
  136. package/dist/cli/commands/explore.d.ts +11 -0
  137. package/dist/cli/commands/explore.js +437 -0
  138. package/dist/cli/commands/feedback.d.ts +9 -0
  139. package/dist/cli/commands/feedback.js +174 -0
  140. package/dist/cli/commands/golden.d.ts +12 -0
  141. package/dist/cli/commands/golden.js +407 -0
  142. package/dist/cli/commands/history.d.ts +10 -0
  143. package/dist/cli/commands/history.js +202 -0
  144. package/dist/cli/commands/init.d.ts +9 -0
  145. package/dist/cli/commands/init.js +219 -0
  146. package/dist/cli/commands/interview.d.ts +3 -0
  147. package/dist/cli/commands/interview.js +903 -0
  148. package/dist/cli/commands/link.d.ts +10 -0
  149. package/dist/cli/commands/link.js +169 -0
  150. package/dist/cli/commands/login.d.ts +7 -0
  151. package/dist/cli/commands/login.js +499 -0
  152. package/dist/cli/commands/preset.d.ts +33 -0
  153. package/dist/cli/commands/preset.js +297 -0
  154. package/dist/cli/commands/profile.d.ts +33 -0
  155. package/dist/cli/commands/profile.js +286 -0
  156. package/dist/cli/commands/registry.d.ts +11 -0
  157. package/dist/cli/commands/registry.js +146 -0
  158. package/dist/cli/commands/shared.d.ts +79 -0
  159. package/dist/cli/commands/shared.js +196 -0
  160. package/dist/cli/commands/teams.d.ts +8 -0
  161. package/dist/cli/commands/teams.js +169 -0
  162. package/dist/cli/commands/test.d.ts +9 -0
  163. package/dist/cli/commands/test.js +500 -0
  164. package/dist/cli/commands/upload.d.ts +8 -0
  165. package/dist/cli/commands/upload.js +223 -0
  166. package/dist/cli/commands/validate-config.d.ts +6 -0
  167. package/dist/cli/commands/validate-config.js +35 -0
  168. package/dist/cli/commands/verify.d.ts +11 -0
  169. package/dist/cli/commands/verify.js +283 -0
  170. package/dist/cli/commands/watch.d.ts +12 -0
  171. package/dist/cli/commands/watch.js +253 -0
  172. package/dist/cli/index.d.ts +3 -0
  173. package/dist/cli/index.js +178 -0
  174. package/dist/cli/interactive.d.ts +47 -0
  175. package/dist/cli/interactive.js +216 -0
  176. package/dist/cli/output/terminal-reporter.d.ts +19 -0
  177. package/dist/cli/output/terminal-reporter.js +104 -0
  178. package/dist/cli/output.d.ts +226 -0
  179. package/dist/cli/output.js +438 -0
  180. package/dist/cli/utils/env.d.ts +5 -0
  181. package/dist/cli/utils/env.js +14 -0
  182. package/dist/cli/utils/progress.d.ts +59 -0
  183. package/dist/cli/utils/progress.js +206 -0
  184. package/dist/cli/utils/server-context.d.ts +10 -0
  185. package/dist/cli/utils/server-context.js +36 -0
  186. package/dist/cloud/auth.d.ts +144 -0
  187. package/dist/cloud/auth.js +374 -0
  188. package/dist/cloud/client.d.ts +24 -0
  189. package/dist/cloud/client.js +65 -0
  190. package/dist/cloud/http-client.d.ts +38 -0
  191. package/dist/cloud/http-client.js +215 -0
  192. package/dist/cloud/index.d.ts +23 -0
  193. package/dist/cloud/index.js +25 -0
  194. package/dist/cloud/mock-client.d.ts +107 -0
  195. package/dist/cloud/mock-client.js +545 -0
  196. package/dist/cloud/types.d.ts +515 -0
  197. package/dist/cloud/types.js +15 -0
  198. package/dist/config/defaults.d.ts +160 -0
  199. package/dist/config/defaults.js +169 -0
  200. package/dist/config/loader.d.ts +24 -0
  201. package/dist/config/loader.js +122 -0
  202. package/dist/config/template.d.ts +42 -0
  203. package/dist/config/template.js +647 -0
  204. package/dist/config/validator.d.ts +2112 -0
  205. package/dist/config/validator.js +658 -0
  206. package/dist/constants/cloud.d.ts +107 -0
  207. package/dist/constants/cloud.js +110 -0
  208. package/dist/constants/core.d.ts +521 -0
  209. package/dist/constants/core.js +556 -0
  210. package/dist/constants/testing.d.ts +1283 -0
  211. package/dist/constants/testing.js +1568 -0
  212. package/dist/constants.d.ts +10 -0
  213. package/dist/constants.js +10 -0
  214. package/dist/contract/index.d.ts +6 -0
  215. package/dist/contract/index.js +5 -0
  216. package/dist/contract/validator.d.ts +177 -0
  217. package/dist/contract/validator.js +574 -0
  218. package/dist/cost/index.d.ts +6 -0
  219. package/dist/cost/index.js +5 -0
  220. package/dist/cost/tracker.d.ts +134 -0
  221. package/dist/cost/tracker.js +313 -0
  222. package/dist/discovery/discovery.d.ts +16 -0
  223. package/dist/discovery/discovery.js +173 -0
  224. package/dist/discovery/types.d.ts +51 -0
  225. package/dist/discovery/types.js +2 -0
  226. package/dist/docs/agents.d.ts +3 -0
  227. package/dist/docs/agents.js +995 -0
  228. package/dist/docs/contract.d.ts +51 -0
  229. package/dist/docs/contract.js +1681 -0
  230. package/dist/docs/generator.d.ts +4 -0
  231. package/dist/docs/generator.js +4 -0
  232. package/dist/docs/html-reporter.d.ts +9 -0
  233. package/dist/docs/html-reporter.js +757 -0
  234. package/dist/docs/index.d.ts +10 -0
  235. package/dist/docs/index.js +11 -0
  236. package/dist/docs/junit-reporter.d.ts +18 -0
  237. package/dist/docs/junit-reporter.js +210 -0
  238. package/dist/docs/report.d.ts +14 -0
  239. package/dist/docs/report.js +44 -0
  240. package/dist/docs/sarif-reporter.d.ts +19 -0
  241. package/dist/docs/sarif-reporter.js +335 -0
  242. package/dist/docs/shared.d.ts +35 -0
  243. package/dist/docs/shared.js +162 -0
  244. package/dist/docs/templates.d.ts +12 -0
  245. package/dist/docs/templates.js +76 -0
  246. package/dist/errors/index.d.ts +6 -0
  247. package/dist/errors/index.js +6 -0
  248. package/dist/errors/retry.d.ts +92 -0
  249. package/dist/errors/retry.js +323 -0
  250. package/dist/errors/types.d.ts +321 -0
  251. package/dist/errors/types.js +584 -0
  252. package/dist/index.d.ts +32 -0
  253. package/dist/index.js +32 -0
  254. package/dist/interview/dependency-resolver.d.ts +11 -0
  255. package/dist/interview/dependency-resolver.js +32 -0
  256. package/dist/interview/interviewer.d.ts +232 -0
  257. package/dist/interview/interviewer.js +1939 -0
  258. package/dist/interview/mock-response-generator.d.ts +7 -0
  259. package/dist/interview/mock-response-generator.js +102 -0
  260. package/dist/interview/orchestrator.d.ts +237 -0
  261. package/dist/interview/orchestrator.js +1296 -0
  262. package/dist/interview/rate-limiter.d.ts +15 -0
  263. package/dist/interview/rate-limiter.js +55 -0
  264. package/dist/interview/response-validator.d.ts +10 -0
  265. package/dist/interview/response-validator.js +132 -0
  266. package/dist/interview/schema-inferrer.d.ts +8 -0
  267. package/dist/interview/schema-inferrer.js +71 -0
  268. package/dist/interview/schema-test-generator.d.ts +71 -0
  269. package/dist/interview/schema-test-generator.js +834 -0
  270. package/dist/interview/smart-value-generator.d.ts +155 -0
  271. package/dist/interview/smart-value-generator.js +554 -0
  272. package/dist/interview/stateful-test-runner.d.ts +19 -0
  273. package/dist/interview/stateful-test-runner.js +106 -0
  274. package/dist/interview/types.d.ts +561 -0
  275. package/dist/interview/types.js +2 -0
  276. package/dist/llm/anthropic.d.ts +41 -0
  277. package/dist/llm/anthropic.js +355 -0
  278. package/dist/llm/client.d.ts +123 -0
  279. package/dist/llm/client.js +42 -0
  280. package/dist/llm/factory.d.ts +38 -0
  281. package/dist/llm/factory.js +145 -0
  282. package/dist/llm/fallback.d.ts +140 -0
  283. package/dist/llm/fallback.js +379 -0
  284. package/dist/llm/index.d.ts +18 -0
  285. package/dist/llm/index.js +15 -0
  286. package/dist/llm/ollama.d.ts +37 -0
  287. package/dist/llm/ollama.js +330 -0
  288. package/dist/llm/openai.d.ts +25 -0
  289. package/dist/llm/openai.js +320 -0
  290. package/dist/llm/token-budget.d.ts +161 -0
  291. package/dist/llm/token-budget.js +395 -0
  292. package/dist/logging/logger.d.ts +70 -0
  293. package/dist/logging/logger.js +130 -0
  294. package/dist/metrics/collector.d.ts +106 -0
  295. package/dist/metrics/collector.js +547 -0
  296. package/dist/metrics/index.d.ts +7 -0
  297. package/dist/metrics/index.js +7 -0
  298. package/dist/metrics/prometheus.d.ts +20 -0
  299. package/dist/metrics/prometheus.js +241 -0
  300. package/dist/metrics/types.d.ts +209 -0
  301. package/dist/metrics/types.js +5 -0
  302. package/dist/persona/builtins.d.ts +54 -0
  303. package/dist/persona/builtins.js +219 -0
  304. package/dist/persona/index.d.ts +8 -0
  305. package/dist/persona/index.js +8 -0
  306. package/dist/persona/loader.d.ts +30 -0
  307. package/dist/persona/loader.js +190 -0
  308. package/dist/persona/types.d.ts +144 -0
  309. package/dist/persona/types.js +5 -0
  310. package/dist/persona/validation.d.ts +94 -0
  311. package/dist/persona/validation.js +332 -0
  312. package/dist/prompts/index.d.ts +5 -0
  313. package/dist/prompts/index.js +5 -0
  314. package/dist/prompts/templates.d.ts +180 -0
  315. package/dist/prompts/templates.js +431 -0
  316. package/dist/registry/client.d.ts +49 -0
  317. package/dist/registry/client.js +191 -0
  318. package/dist/registry/index.d.ts +7 -0
  319. package/dist/registry/index.js +6 -0
  320. package/dist/registry/types.d.ts +140 -0
  321. package/dist/registry/types.js +6 -0
  322. package/dist/scenarios/evaluator.d.ts +43 -0
  323. package/dist/scenarios/evaluator.js +206 -0
  324. package/dist/scenarios/index.d.ts +10 -0
  325. package/dist/scenarios/index.js +9 -0
  326. package/dist/scenarios/loader.d.ts +20 -0
  327. package/dist/scenarios/loader.js +285 -0
  328. package/dist/scenarios/types.d.ts +153 -0
  329. package/dist/scenarios/types.js +8 -0
  330. package/dist/security/index.d.ts +17 -0
  331. package/dist/security/index.js +18 -0
  332. package/dist/security/payloads.d.ts +61 -0
  333. package/dist/security/payloads.js +268 -0
  334. package/dist/security/security-tester.d.ts +42 -0
  335. package/dist/security/security-tester.js +582 -0
  336. package/dist/security/types.d.ts +166 -0
  337. package/dist/security/types.js +8 -0
  338. package/dist/transport/base-transport.d.ts +59 -0
  339. package/dist/transport/base-transport.js +38 -0
  340. package/dist/transport/http-transport.d.ts +67 -0
  341. package/dist/transport/http-transport.js +238 -0
  342. package/dist/transport/mcp-client.d.ts +141 -0
  343. package/dist/transport/mcp-client.js +496 -0
  344. package/dist/transport/sse-transport.d.ts +88 -0
  345. package/dist/transport/sse-transport.js +316 -0
  346. package/dist/transport/stdio-transport.d.ts +43 -0
  347. package/dist/transport/stdio-transport.js +238 -0
  348. package/dist/transport/types.d.ts +125 -0
  349. package/dist/transport/types.js +16 -0
  350. package/dist/utils/concurrency.d.ts +123 -0
  351. package/dist/utils/concurrency.js +213 -0
  352. package/dist/utils/formatters.d.ts +16 -0
  353. package/dist/utils/formatters.js +37 -0
  354. package/dist/utils/index.d.ts +8 -0
  355. package/dist/utils/index.js +8 -0
  356. package/dist/utils/jsonpath.d.ts +87 -0
  357. package/dist/utils/jsonpath.js +326 -0
  358. package/dist/utils/markdown.d.ts +113 -0
  359. package/dist/utils/markdown.js +265 -0
  360. package/dist/utils/network.d.ts +14 -0
  361. package/dist/utils/network.js +17 -0
  362. package/dist/utils/sanitize.d.ts +92 -0
  363. package/dist/utils/sanitize.js +191 -0
  364. package/dist/utils/semantic.d.ts +194 -0
  365. package/dist/utils/semantic.js +1051 -0
  366. package/dist/utils/smart-truncate.d.ts +94 -0
  367. package/dist/utils/smart-truncate.js +361 -0
  368. package/dist/utils/timeout.d.ts +153 -0
  369. package/dist/utils/timeout.js +205 -0
  370. package/dist/utils/yaml-parser.d.ts +58 -0
  371. package/dist/utils/yaml-parser.js +86 -0
  372. package/dist/validation/index.d.ts +32 -0
  373. package/dist/validation/index.js +32 -0
  374. package/dist/validation/semantic-test-generator.d.ts +50 -0
  375. package/dist/validation/semantic-test-generator.js +176 -0
  376. package/dist/validation/semantic-types.d.ts +66 -0
  377. package/dist/validation/semantic-types.js +94 -0
  378. package/dist/validation/semantic-validator.d.ts +38 -0
  379. package/dist/validation/semantic-validator.js +340 -0
  380. package/dist/verification/index.d.ts +6 -0
  381. package/dist/verification/index.js +5 -0
  382. package/dist/verification/types.d.ts +133 -0
  383. package/dist/verification/types.js +5 -0
  384. package/dist/verification/verifier.d.ts +30 -0
  385. package/dist/verification/verifier.js +309 -0
  386. package/dist/version.d.ts +19 -0
  387. package/dist/version.js +48 -0
  388. package/dist/workflow/auto-generator.d.ts +27 -0
  389. package/dist/workflow/auto-generator.js +513 -0
  390. package/dist/workflow/discovery.d.ts +40 -0
  391. package/dist/workflow/discovery.js +195 -0
  392. package/dist/workflow/executor.d.ts +82 -0
  393. package/dist/workflow/executor.js +611 -0
  394. package/dist/workflow/index.d.ts +10 -0
  395. package/dist/workflow/index.js +10 -0
  396. package/dist/workflow/loader.d.ts +24 -0
  397. package/dist/workflow/loader.js +194 -0
  398. package/dist/workflow/state-tracker.d.ts +98 -0
  399. package/dist/workflow/state-tracker.js +424 -0
  400. package/dist/workflow/types.d.ts +337 -0
  401. package/dist/workflow/types.js +5 -0
  402. package/package.json +94 -0
  403. package/schemas/bellwether-check.schema.json +651 -0
@@ -0,0 +1,574 @@
1
+ /**
2
+ * Contract-as-Code Validator.
3
+ *
4
+ * Validates MCP server behavior against defined contract expectations.
5
+ * Contracts define expected tools, parameters, and output constraints,
6
+ * enabling CI/CD integration and regression detection.
7
+ */
8
+ import { readFileSync, existsSync } from 'fs';
9
+ import * as yaml from 'yaml';
10
+ import { CONTRACT_TESTING } from '../constants.js';
11
+ /**
12
+ * Load a contract from a file.
13
+ */
14
+ export function loadContract(filePath) {
15
+ if (!existsSync(filePath)) {
16
+ throw new Error(`Contract file not found: ${filePath}`);
17
+ }
18
+ const content = readFileSync(filePath, 'utf-8');
19
+ try {
20
+ const parsed = yaml.parse(content);
21
+ // Validate schema version
22
+ if (parsed.version && parsed.version !== CONTRACT_TESTING.SCHEMA_VERSION) {
23
+ throw new Error(`Contract version ${parsed.version} is not supported. Expected version ${CONTRACT_TESTING.SCHEMA_VERSION}`);
24
+ }
25
+ // Set default version
26
+ if (!parsed.version) {
27
+ parsed.version = CONTRACT_TESTING.SCHEMA_VERSION;
28
+ }
29
+ // Ensure tools object exists
30
+ if (!parsed.tools) {
31
+ parsed.tools = {};
32
+ }
33
+ return parsed;
34
+ }
35
+ catch (error) {
36
+ if (error instanceof yaml.YAMLError) {
37
+ throw new Error(`Invalid YAML in contract file: ${error.message}`);
38
+ }
39
+ throw error;
40
+ }
41
+ }
42
+ /**
43
+ * Find a contract file in the given directory.
44
+ */
45
+ export function findContractFile(directory) {
46
+ for (const filename of CONTRACT_TESTING.CONTRACT_FILENAMES) {
47
+ const filePath = `${directory}/${filename}`;
48
+ if (existsSync(filePath)) {
49
+ return filePath;
50
+ }
51
+ }
52
+ return null;
53
+ }
54
+ /**
55
+ * Validate an MCP server against a contract.
56
+ */
57
+ export async function validateContract(contract, tools, options = {}) {
58
+ const mode = options.mode || 'strict';
59
+ const violations = [];
60
+ // Build tool map for quick lookup
61
+ const toolMap = new Map();
62
+ for (const tool of tools) {
63
+ toolMap.set(tool.name, tool);
64
+ }
65
+ // Check each contracted tool
66
+ for (const [toolName, toolContract] of Object.entries(contract.tools)) {
67
+ const tool = toolMap.get(toolName);
68
+ // Check if required tool exists
69
+ if (!tool) {
70
+ if (toolContract.required !== false) {
71
+ violations.push({
72
+ type: 'missing_tool',
73
+ severity: 'breaking',
74
+ tool: toolName,
75
+ expected: 'Tool should exist',
76
+ actual: 'Tool not found',
77
+ message: `Required tool "${toolName}" is missing from server`,
78
+ });
79
+ }
80
+ continue;
81
+ }
82
+ // Validate input parameters
83
+ if (toolContract.input) {
84
+ const paramViolations = validateParameters(tool, toolContract.input);
85
+ violations.push(...paramViolations);
86
+ }
87
+ // Validate output (if function provided)
88
+ if (options.validateOutput && options.callTool && toolContract.output) {
89
+ try {
90
+ const result = await options.callTool(toolName, {});
91
+ const outputViolations = validateOutput(toolName, result, toolContract.output);
92
+ violations.push(...outputViolations);
93
+ }
94
+ catch (error) {
95
+ violations.push({
96
+ type: 'output_assertion_failed',
97
+ severity: 'warning',
98
+ tool: toolName,
99
+ expected: 'Successful tool call',
100
+ actual: `Error: ${error instanceof Error ? error.message : String(error)}`,
101
+ message: `Failed to call tool "${toolName}" for output validation`,
102
+ });
103
+ }
104
+ }
105
+ }
106
+ // Check for unexpected tools (if strict mode or option enabled)
107
+ if (options.failOnUnexpectedTools || mode === 'strict') {
108
+ for (const tool of tools) {
109
+ if (!contract.tools[tool.name]) {
110
+ violations.push({
111
+ type: 'unexpected_tool',
112
+ severity: 'info',
113
+ tool: tool.name,
114
+ expected: 'Tool should be in contract',
115
+ actual: 'Tool not defined in contract',
116
+ message: `Unexpected tool "${tool.name}" not defined in contract`,
117
+ });
118
+ }
119
+ }
120
+ }
121
+ // Calculate summary
122
+ const breakingCount = violations.filter(v => v.severity === 'breaking').length;
123
+ const warningCount = violations.filter(v => v.severity === 'warning').length;
124
+ const infoCount = violations.filter(v => v.severity === 'info').length;
125
+ // Determine overall severity
126
+ let severity = 'none';
127
+ if (breakingCount > 0)
128
+ severity = 'breaking';
129
+ else if (warningCount > 0)
130
+ severity = 'warning';
131
+ else if (infoCount > 0)
132
+ severity = 'info';
133
+ // Determine pass/fail based on mode
134
+ let passed;
135
+ switch (mode) {
136
+ case 'strict':
137
+ passed = violations.length === 0;
138
+ break;
139
+ case 'lenient':
140
+ passed = breakingCount === 0;
141
+ break;
142
+ case 'report':
143
+ passed = true;
144
+ break;
145
+ }
146
+ return {
147
+ passed,
148
+ severity,
149
+ violations,
150
+ summary: {
151
+ totalViolations: violations.length,
152
+ breakingCount,
153
+ warningCount,
154
+ infoCount,
155
+ toolsChecked: Object.keys(contract.tools).length,
156
+ toolsPassed: Object.keys(contract.tools).length -
157
+ violations.filter(v => v.type === 'missing_tool').length,
158
+ },
159
+ mode,
160
+ };
161
+ }
162
+ /**
163
+ * Validate tool parameters against contract.
164
+ */
165
+ function validateParameters(tool, paramContracts) {
166
+ const violations = [];
167
+ const schema = tool.inputSchema;
168
+ const actualProperties = schema?.properties || {};
169
+ const actualRequired = schema?.required || [];
170
+ // Check contracted parameters
171
+ for (const [paramName, paramContract] of Object.entries(paramContracts)) {
172
+ const actualParam = actualProperties[paramName];
173
+ // Check if required parameter exists
174
+ if (!actualParam && paramContract.required !== false) {
175
+ violations.push({
176
+ type: 'missing_parameter',
177
+ severity: 'breaking',
178
+ tool: tool.name,
179
+ parameter: paramName,
180
+ expected: 'Parameter should exist',
181
+ actual: 'Parameter not found',
182
+ message: `Required parameter "${paramName}" is missing from tool "${tool.name}"`,
183
+ });
184
+ continue;
185
+ }
186
+ if (!actualParam)
187
+ continue;
188
+ // Check type
189
+ if (paramContract.type) {
190
+ const actualType = actualParam.type;
191
+ if (actualType !== paramContract.type) {
192
+ violations.push({
193
+ type: 'type_mismatch',
194
+ severity: 'breaking',
195
+ tool: tool.name,
196
+ parameter: paramName,
197
+ expected: paramContract.type,
198
+ actual: actualType || 'unknown',
199
+ message: `Parameter "${paramName}" type mismatch: expected ${paramContract.type}, got ${actualType}`,
200
+ });
201
+ }
202
+ }
203
+ // Check format
204
+ if (paramContract.format) {
205
+ const actualFormat = actualParam.format;
206
+ if (actualFormat !== paramContract.format) {
207
+ violations.push({
208
+ type: 'format_mismatch',
209
+ severity: 'warning',
210
+ tool: tool.name,
211
+ parameter: paramName,
212
+ expected: paramContract.format,
213
+ actual: actualFormat || 'none',
214
+ message: `Parameter "${paramName}" format mismatch: expected ${paramContract.format}, got ${actualFormat || 'none'}`,
215
+ });
216
+ }
217
+ }
218
+ // Check required status
219
+ if (paramContract.required === true && !actualRequired.includes(paramName)) {
220
+ violations.push({
221
+ type: 'constraint_violation',
222
+ severity: 'warning',
223
+ tool: tool.name,
224
+ parameter: paramName,
225
+ expected: 'Parameter should be required',
226
+ actual: 'Parameter is optional',
227
+ message: `Parameter "${paramName}" should be required in tool "${tool.name}"`,
228
+ });
229
+ }
230
+ // Check enum
231
+ if (paramContract.enum) {
232
+ const actualEnum = actualParam.enum;
233
+ if (actualEnum) {
234
+ for (const value of paramContract.enum) {
235
+ if (!actualEnum.includes(value)) {
236
+ violations.push({
237
+ type: 'constraint_violation',
238
+ severity: 'warning',
239
+ tool: tool.name,
240
+ parameter: paramName,
241
+ expected: `Enum should contain ${String(value)}`,
242
+ actual: `Enum values: ${actualEnum.join(', ')}`,
243
+ message: `Parameter "${paramName}" enum missing expected value: ${String(value)}`,
244
+ });
245
+ }
246
+ }
247
+ }
248
+ }
249
+ // Check min/max
250
+ if (paramContract.min !== undefined) {
251
+ const actualMin = actualParam.minimum;
252
+ if (actualMin === undefined || actualMin > paramContract.min) {
253
+ violations.push({
254
+ type: 'constraint_violation',
255
+ severity: 'warning',
256
+ tool: tool.name,
257
+ parameter: paramName,
258
+ expected: `minimum <= ${paramContract.min}`,
259
+ actual: `minimum = ${actualMin ?? 'none'}`,
260
+ message: `Parameter "${paramName}" minimum constraint mismatch`,
261
+ });
262
+ }
263
+ }
264
+ if (paramContract.max !== undefined) {
265
+ const actualMax = actualParam.maximum;
266
+ if (actualMax === undefined || actualMax < paramContract.max) {
267
+ violations.push({
268
+ type: 'constraint_violation',
269
+ severity: 'warning',
270
+ tool: tool.name,
271
+ parameter: paramName,
272
+ expected: `maximum >= ${paramContract.max}`,
273
+ actual: `maximum = ${actualMax ?? 'none'}`,
274
+ message: `Parameter "${paramName}" maximum constraint mismatch`,
275
+ });
276
+ }
277
+ }
278
+ }
279
+ return violations;
280
+ }
281
+ /**
282
+ * Validate tool output against contract.
283
+ */
284
+ function validateOutput(toolName, result, outputContract) {
285
+ const violations = [];
286
+ // Extract text content
287
+ const textContent = result.content.find(c => c.type === 'text');
288
+ const raw = textContent && 'text' in textContent ? String(textContent.text) : '';
289
+ // Check content type
290
+ if (outputContract.content_type) {
291
+ const actualType = detectContentType(raw);
292
+ if (actualType !== outputContract.content_type) {
293
+ violations.push({
294
+ type: 'content_type_mismatch',
295
+ severity: 'warning',
296
+ tool: toolName,
297
+ expected: outputContract.content_type,
298
+ actual: actualType,
299
+ message: `Output content type mismatch: expected ${outputContract.content_type}, got ${actualType}`,
300
+ });
301
+ }
302
+ }
303
+ // Parse JSON if applicable
304
+ let parsed;
305
+ try {
306
+ parsed = JSON.parse(raw);
307
+ }
308
+ catch {
309
+ // Not JSON - can only check content type
310
+ if (outputContract.must_contain || outputContract.must_not_contain) {
311
+ violations.push({
312
+ type: 'output_assertion_failed',
313
+ severity: 'warning',
314
+ tool: toolName,
315
+ expected: 'JSON output for path assertions',
316
+ actual: 'Non-JSON output',
317
+ message: `Cannot evaluate JSONPath assertions on non-JSON output`,
318
+ });
319
+ }
320
+ return violations;
321
+ }
322
+ // Check must_contain assertions
323
+ if (outputContract.must_contain) {
324
+ for (const assertion of outputContract.must_contain) {
325
+ const value = getValueAtPath(parsed, assertion.path);
326
+ if (value === undefined) {
327
+ violations.push({
328
+ type: 'missing_output_field',
329
+ severity: 'warning',
330
+ tool: toolName,
331
+ path: assertion.path,
332
+ expected: 'Path should exist',
333
+ actual: 'Path not found',
334
+ message: `Required output path "${assertion.path}" not found in tool "${toolName}" response`,
335
+ });
336
+ continue;
337
+ }
338
+ // Check type
339
+ if (assertion.type) {
340
+ const actualType = getValueType(value);
341
+ if (actualType !== assertion.type) {
342
+ violations.push({
343
+ type: 'output_assertion_failed',
344
+ severity: 'warning',
345
+ tool: toolName,
346
+ path: assertion.path,
347
+ expected: assertion.type,
348
+ actual: actualType,
349
+ message: `Output type mismatch at "${assertion.path}": expected ${assertion.type}, got ${actualType}`,
350
+ });
351
+ }
352
+ }
353
+ // Check pattern
354
+ if (assertion.pattern && typeof value === 'string') {
355
+ const regex = new RegExp(assertion.pattern);
356
+ if (!regex.test(value)) {
357
+ violations.push({
358
+ type: 'output_assertion_failed',
359
+ severity: 'warning',
360
+ tool: toolName,
361
+ path: assertion.path,
362
+ expected: `matches /${assertion.pattern}/`,
363
+ actual: truncate(value, 50),
364
+ message: `Output at "${assertion.path}" doesn't match pattern: ${assertion.pattern}`,
365
+ });
366
+ }
367
+ }
368
+ // Check exact value
369
+ if (assertion.value !== undefined && value !== assertion.value) {
370
+ violations.push({
371
+ type: 'output_assertion_failed',
372
+ severity: 'warning',
373
+ tool: toolName,
374
+ path: assertion.path,
375
+ expected: String(assertion.value),
376
+ actual: String(value),
377
+ message: `Output value mismatch at "${assertion.path}"`,
378
+ });
379
+ }
380
+ }
381
+ }
382
+ // Check must_not_contain assertions
383
+ if (outputContract.must_not_contain) {
384
+ for (const assertion of outputContract.must_not_contain) {
385
+ const value = getValueAtPath(parsed, assertion.path);
386
+ if (value !== undefined) {
387
+ violations.push({
388
+ type: 'unexpected_output_field',
389
+ severity: 'info',
390
+ tool: toolName,
391
+ path: assertion.path,
392
+ expected: 'Path should not exist',
393
+ actual: `Path exists with value: ${truncate(String(value), 50)}`,
394
+ message: `Forbidden output path "${assertion.path}" found in tool "${toolName}" response`,
395
+ });
396
+ }
397
+ }
398
+ }
399
+ return violations;
400
+ }
401
+ /**
402
+ * Get value at a JSONPath-like path.
403
+ * Supports simple paths like $.field.nested[0].value
404
+ */
405
+ function getValueAtPath(obj, path) {
406
+ // Remove leading $. if present
407
+ const cleanPath = path.replace(/^\$\.?/, '');
408
+ if (!cleanPath)
409
+ return obj;
410
+ const segments = cleanPath.split(/\.|\[|\]/).filter(Boolean);
411
+ let current = obj;
412
+ for (const segment of segments) {
413
+ if (current === null || current === undefined)
414
+ return undefined;
415
+ if (typeof current !== 'object')
416
+ return undefined;
417
+ // Handle array index
418
+ const index = parseInt(segment, 10);
419
+ if (!isNaN(index) && Array.isArray(current)) {
420
+ current = current[index];
421
+ }
422
+ else {
423
+ current = current[segment];
424
+ }
425
+ }
426
+ return current;
427
+ }
428
+ /**
429
+ * Get the type name of a value.
430
+ */
431
+ function getValueType(value) {
432
+ if (value === null)
433
+ return 'null';
434
+ if (Array.isArray(value))
435
+ return 'array';
436
+ return typeof value;
437
+ }
438
+ /**
439
+ * Detect content type from raw output.
440
+ */
441
+ function detectContentType(raw) {
442
+ const trimmed = raw.trim();
443
+ if ((trimmed.startsWith('{') && trimmed.endsWith('}')) ||
444
+ (trimmed.startsWith('[') && trimmed.endsWith(']'))) {
445
+ try {
446
+ JSON.parse(trimmed);
447
+ return 'json';
448
+ }
449
+ catch {
450
+ // Not valid JSON
451
+ }
452
+ }
453
+ if (/^#|^\*{1,3}[^*]|\[.*\]\(.*\)|^```/.test(trimmed)) {
454
+ return 'markdown';
455
+ }
456
+ return 'text';
457
+ }
458
+ /**
459
+ * Truncate a string for display.
460
+ */
461
+ function truncate(str, maxLen) {
462
+ if (str.length <= maxLen)
463
+ return str;
464
+ return str.slice(0, maxLen - 3) + '...';
465
+ }
466
+ /**
467
+ * Generate a contract from current server state.
468
+ */
469
+ export function generateContract(tools, serverName) {
470
+ const contract = {
471
+ version: CONTRACT_TESTING.SCHEMA_VERSION,
472
+ server: serverName ? { name: serverName } : undefined,
473
+ tools: {},
474
+ };
475
+ for (const tool of tools) {
476
+ const schema = tool.inputSchema;
477
+ const inputContracts = {};
478
+ if (schema?.properties) {
479
+ for (const [paramName, paramSchema] of Object.entries(schema.properties)) {
480
+ const param = paramSchema;
481
+ const paramContract = {
482
+ required: schema.required?.includes(paramName),
483
+ };
484
+ if (param.type)
485
+ paramContract.type = String(param.type);
486
+ if (param.format)
487
+ paramContract.format = String(param.format);
488
+ if (param.minimum !== undefined)
489
+ paramContract.min = Number(param.minimum);
490
+ if (param.maximum !== undefined)
491
+ paramContract.max = Number(param.maximum);
492
+ if (Array.isArray(param.enum))
493
+ paramContract.enum = param.enum;
494
+ inputContracts[paramName] = paramContract;
495
+ }
496
+ }
497
+ contract.tools[tool.name] = {
498
+ required: true,
499
+ input: Object.keys(inputContracts).length > 0 ? inputContracts : undefined,
500
+ description: tool.description,
501
+ };
502
+ }
503
+ return contract;
504
+ }
505
+ /**
506
+ * Generate contract YAML from contract object.
507
+ */
508
+ export function generateContractYaml(contract) {
509
+ return yaml.stringify(contract, {
510
+ indent: 2,
511
+ lineWidth: 100,
512
+ });
513
+ }
514
+ /**
515
+ * Generate markdown report for contract validation.
516
+ */
517
+ export function generateContractValidationMarkdown(result) {
518
+ const lines = [];
519
+ const statusIcon = result.passed ? '✓' : '✗';
520
+ const statusText = result.passed ? 'PASSED' : 'FAILED';
521
+ lines.push('## Contract Validation');
522
+ lines.push('');
523
+ lines.push(`**Status: ${statusIcon} ${statusText}** (${result.mode} mode)`);
524
+ lines.push('');
525
+ // Summary table
526
+ lines.push('| Metric | Count |');
527
+ lines.push('|--------|-------|');
528
+ lines.push(`| Tools Checked | ${result.summary.toolsChecked} |`);
529
+ lines.push(`| Tools Passed | ${result.summary.toolsPassed} |`);
530
+ lines.push(`| Total Violations | ${result.summary.totalViolations} |`);
531
+ if (result.summary.breakingCount > 0) {
532
+ lines.push(`| Breaking | ${result.summary.breakingCount} |`);
533
+ }
534
+ if (result.summary.warningCount > 0) {
535
+ lines.push(`| Warnings | ${result.summary.warningCount} |`);
536
+ }
537
+ if (result.summary.infoCount > 0) {
538
+ lines.push(`| Info | ${result.summary.infoCount} |`);
539
+ }
540
+ lines.push('');
541
+ // Violation details
542
+ if (result.violations.length > 0) {
543
+ lines.push('### Violations');
544
+ lines.push('');
545
+ // Group by severity
546
+ const bySeverity = {
547
+ breaking: [],
548
+ warning: [],
549
+ info: [],
550
+ none: [],
551
+ };
552
+ for (const v of result.violations) {
553
+ bySeverity[v.severity].push(v);
554
+ }
555
+ for (const severity of ['breaking', 'warning', 'info']) {
556
+ const violations = bySeverity[severity];
557
+ if (violations.length === 0)
558
+ continue;
559
+ const icon = severity === 'breaking' ? '❌' : severity === 'warning' ? '⚠️' : 'ℹ️';
560
+ lines.push(`#### ${icon} ${severity.charAt(0).toUpperCase() + severity.slice(1)} (${violations.length})`);
561
+ lines.push('');
562
+ for (const v of violations.slice(0, CONTRACT_TESTING.MAX_VALIDATION_ERRORS)) {
563
+ const location = [v.tool, v.parameter, v.path].filter(Boolean).join(' › ');
564
+ lines.push(`- **${location || v.type}**: ${v.message}`);
565
+ }
566
+ if (violations.length > CONTRACT_TESTING.MAX_VALIDATION_ERRORS) {
567
+ lines.push(`- ... and ${violations.length - CONTRACT_TESTING.MAX_VALIDATION_ERRORS} more`);
568
+ }
569
+ lines.push('');
570
+ }
571
+ }
572
+ return lines.join('\n');
573
+ }
574
+ //# sourceMappingURL=validator.js.map
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Cost tracking and estimation module.
3
+ */
4
+ export { CostTracker, estimateInterviewCost, formatCostEstimate, getModelPricing, estimateInterviewTime, formatCostAndTimeEstimate, suggestOptimizations, formatOptimizationSuggestions, isLocalProvider, } from './tracker.js';
5
+ export type { TokenUsage, CostEstimate, InterviewTimeEstimate, OptimizationSuggestion, OptimizationContext, } from './tracker.js';
6
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Cost tracking and estimation module.
3
+ */
4
+ export { CostTracker, estimateInterviewCost, formatCostEstimate, getModelPricing, estimateInterviewTime, formatCostAndTimeEstimate, suggestOptimizations, formatOptimizationSuggestions, isLocalProvider, } from './tracker.js';
5
+ //# sourceMappingURL=index.js.map