@langwatch/mcp-server 2.1.0 → 2.2.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 (415) hide show
  1. package/.gitignore +7 -0
  2. package/.npmignore +41 -0
  3. package/CHANGELOG.md +192 -201
  4. package/CONTRIBUTING.md +10 -0
  5. package/README.md +70 -68
  6. package/dist/add-dashboard-widget-4ZC5UN73.js +50 -0
  7. package/dist/add-dashboard-widget-ACC6QYBG.js +49 -0
  8. package/dist/archive-project-KWFM3I5Z.js +19 -0
  9. package/dist/{archive-project-W3IKI7UL.js → archive-project-SPVHXK6A.js} +4 -4
  10. package/dist/{archive-run-plan-474OKHHU.js → archive-run-plan-65QN3XRX.js} +4 -4
  11. package/dist/archive-run-plan-Z4SYE6BP.js +15 -0
  12. package/dist/{archive-scenario-V5UNYK26.js → archive-scenario-A6QV5LOW.js} +4 -4
  13. package/dist/archive-scenario-FXEYQNEH.js +19 -0
  14. package/dist/archive-test-suite-5ADQ3KPQ.js +15 -0
  15. package/dist/{archive-test-suite-O7FLDMXK.js → archive-test-suite-NKZDZY3D.js} +4 -4
  16. package/dist/assign-prompt-tag-5IUZXJ2T.js +23 -0
  17. package/dist/{assign-prompt-tag-Q46WRB6P.js → assign-prompt-tag-HBZLBIFD.js} +8 -4
  18. package/dist/{chunk-OJLYYJWI.js → chunk-3OCG3QPU.js} +7 -10
  19. package/dist/chunk-3YLN3ATJ.js +241 -0
  20. package/dist/chunk-5FNCQRCX.js +91 -0
  21. package/dist/{chunk-UF6XIGYR.js → chunk-6YZUOD34.js} +2 -5
  22. package/dist/{chunk-WVI5CJ6S.js → chunk-7AQLPVFL.js} +2 -2
  23. package/dist/chunk-7VNSRCXO.js +129 -0
  24. package/dist/{chunk-G4ZAZTZW.js → chunk-B4J3PJLX.js} +11 -10
  25. package/dist/{chunk-ZXKLPC2E.js → chunk-BIIKE3EI.js} +1 -1
  26. package/dist/{chunk-37SMPMQS.js → chunk-DMM3JQ42.js} +7 -2
  27. package/dist/{chunk-NUFPTHKU.js → chunk-FOKVY3BK.js} +1 -1
  28. package/dist/{chunk-WT4PO4A3.js → chunk-H6WGMG2T.js} +10 -7
  29. package/dist/{chunk-EYXMLE5X.js → chunk-HGELNIVY.js} +56 -47
  30. package/dist/{chunk-UJPXFA26.js → chunk-HHMNZAPD.js} +101 -74
  31. package/dist/{chunk-RVAE2MEA.js → chunk-I7ATIZED.js} +7 -7
  32. package/dist/{chunk-S4GVX36W.js → chunk-ILZDO25L.js} +7 -10
  33. package/dist/{chunk-QRO35SOM.js → chunk-J7PQKBLD.js} +652 -528
  34. package/dist/{chunk-SR6QPRIE.js → chunk-JRJPH75H.js} +7 -13
  35. package/dist/chunk-K2I7Z3LZ.js +28 -0
  36. package/dist/{chunk-UFOYFYI7.js → chunk-NKMTIHIJ.js} +6 -9
  37. package/dist/{chunk-HV4DWKBI.js → chunk-O7EMWEVJ.js} +2 -4
  38. package/dist/{chunk-56OGTS24.js → chunk-PTDR56Z3.js} +4 -11
  39. package/dist/{chunk-JR6DJZOU.js → chunk-RABYBFX5.js} +4 -10
  40. package/dist/chunk-SUPUUC4W.js +64 -0
  41. package/dist/{chunk-JTQJRNFJ.js → chunk-SXFC3FNH.js} +4 -4
  42. package/dist/{chunk-CKNAH5TG.js → chunk-U24EACC4.js} +3 -10
  43. package/dist/chunk-UNJM3BIL.js +21 -0
  44. package/dist/{chunk-QAMP3CGR.js → chunk-UTJCWXPZ.js} +4 -4
  45. package/dist/chunk-UWLE5T2F.js +312 -0
  46. package/dist/{chunk-6DJDEXET.js → chunk-VMWJD732.js} +6 -12
  47. package/dist/{chunk-7E4FZIYJ.js → chunk-XUIJYNRJ.js} +1 -1
  48. package/dist/chunk-Y34DHR5K.js +67 -0
  49. package/dist/chunk-YCARO42Z.js +516 -0
  50. package/dist/config-EAKW6F26.js +16 -0
  51. package/dist/config.cjs +109 -0
  52. package/dist/config.js +5 -4
  53. package/dist/{create-agent-JFJ2K6X3.js → create-agent-AYZ3LB3V.js} +4 -4
  54. package/dist/create-agent-S5ZBCEFT.js +23 -0
  55. package/dist/create-annotation-LHNCLHYP.js +29 -0
  56. package/dist/{create-annotation-EUXXQ6NS.js → create-annotation-VUSXULQK.js} +10 -5
  57. package/dist/{create-api-key-BGBB76JX.js → create-api-key-NNYKARMG.js} +5 -7
  58. package/dist/create-api-key-T4KSNPKB.js +24 -0
  59. package/dist/create-dashboard-DHVFMEU4.js +18 -0
  60. package/dist/{create-dashboard-Z76AIPLS.js → create-dashboard-WTKKS5Z6.js} +4 -4
  61. package/dist/{create-dataset-FD5VAXU4.js → create-dataset-2MC4SD4O.js} +6 -6
  62. package/dist/create-dataset-LUP5PVDM.js +22 -0
  63. package/dist/create-dataset-records-NSKBZP3G.js +20 -0
  64. package/dist/{create-dataset-records-WQUXXOHI.js → create-dataset-records-W4GLN2FV.js} +5 -7
  65. package/dist/{create-evaluator-6NV33Z52.js → create-evaluator-643G6LXL.js} +4 -4
  66. package/dist/create-evaluator-HMTYO5SS.js +27 -0
  67. package/dist/create-mcp-server.cjs +9513 -0
  68. package/dist/create-mcp-server.js +2647 -10
  69. package/dist/create-project-6N7PIYRY.js +44 -0
  70. package/dist/{create-project-ZQ7TFIO2.js → create-project-KAIC46UY.js} +4 -4
  71. package/dist/{create-prompt-BS6QRCUU.js → create-prompt-E67LXIYT.js} +5 -7
  72. package/dist/create-prompt-PKJ7EDRR.js +37 -0
  73. package/dist/create-prompt-tag-4YO3FSJ3.js +17 -0
  74. package/dist/{create-prompt-tag-FCQ5ELSQ.js → create-prompt-tag-OPQUEXWB.js} +3 -3
  75. package/dist/create-scenario-5PJBMAXO.js +33 -0
  76. package/dist/{create-scenario-DRPTPXU3.js → create-scenario-UEM77OE7.js} +5 -5
  77. package/dist/{create-test-suite-ZIZCCFJ4.js → create-test-suite-4IPXVSFQ.js} +6 -6
  78. package/dist/create-test-suite-SZEPVXSE.js +36 -0
  79. package/dist/create-trigger-5HZSCRAN.js +49 -0
  80. package/dist/create-trigger-PRELMT6J.js +48 -0
  81. package/dist/delete-agent-JUIZWAXG.js +18 -0
  82. package/dist/{delete-agent-SA4JMUZF.js → delete-agent-QMEK3XHK.js} +4 -4
  83. package/dist/delete-annotation-HY3O2OGR.js +17 -0
  84. package/dist/{delete-annotation-X3KKK7QY.js → delete-annotation-VKIQWUYW.js} +4 -4
  85. package/dist/delete-dashboard-76N66XXL.js +18 -0
  86. package/dist/{delete-dashboard-LYMRPF27.js → delete-dashboard-JVIVHNJM.js} +4 -4
  87. package/dist/delete-dataset-HQCR66MG.js +18 -0
  88. package/dist/{delete-dataset-W6EZ44HY.js → delete-dataset-WKRUW4FP.js} +5 -7
  89. package/dist/{delete-dataset-records-7CNOTALB.js → delete-dataset-records-C2IQFFVQ.js} +5 -7
  90. package/dist/delete-dataset-records-SDUHQLLM.js +19 -0
  91. package/dist/delete-evaluator-4SNSCLEE.js +16 -0
  92. package/dist/delete-evaluator-QP3QHTLN.js +15 -0
  93. package/dist/delete-prompt-tag-NZ4JQMYG.js +17 -0
  94. package/dist/{delete-prompt-tag-QB3OCZUV.js → delete-prompt-tag-YYQ4VMAD.js} +3 -3
  95. package/dist/{delete-workflow-NG2VXYYM.js → delete-workflow-5J46T73T.js} +4 -4
  96. package/dist/delete-workflow-JSWABL5F.js +17 -0
  97. package/dist/discover-evaluator-schema-UHWA5HVL.js +120 -0
  98. package/dist/discover-evaluator-schema-XKCLLCB3.js +118 -0
  99. package/dist/discover-scenario-schema-NOJY6S7E.js +137 -0
  100. package/dist/{discover-scenario-schema-W7KBXA4J.js → discover-scenario-schema-OOFIQKOG.js} +37 -17
  101. package/dist/{discover-schema-L6QDPON3.js → discover-schema-55YJBFGQ.js} +181 -186
  102. package/dist/discover-schema-A33SLMXW.js +442 -0
  103. package/dist/get-agent-LJQX7OF4.js +69 -0
  104. package/dist/{get-agent-7THF7HCU.js → get-agent-OTIYUFZA.js} +15 -11
  105. package/dist/get-analytics-DCS72353.js +80 -0
  106. package/dist/get-analytics-IQZKJHLW.js +79 -0
  107. package/dist/get-dashboard-5NYJEIX2.js +29 -0
  108. package/dist/{get-dashboard-P4WWVHFU.js → get-dashboard-QMTYDQAZ.js} +4 -4
  109. package/dist/get-dataset-ALSP7RTX.js +64 -0
  110. package/dist/get-dataset-X4B5JDG2.js +63 -0
  111. package/dist/{get-evaluator-RZ44UIN6.js → get-evaluator-ISMC4LLI.js} +4 -4
  112. package/dist/get-evaluator-XEUZGZCH.js +47 -0
  113. package/dist/{get-project-QSQEAB33.js → get-project-5VKROSDH.js} +4 -4
  114. package/dist/get-project-RM6FRCBK.js +26 -0
  115. package/dist/{get-prompt-GVKXF5NL.js → get-prompt-OWXJZHQQ.js} +53 -58
  116. package/dist/get-prompt-W52DY36E.js +94 -0
  117. package/dist/{get-run-plan-NY7J5GRE.js → get-run-plan-BE3M2AUX.js} +7 -9
  118. package/dist/get-run-plan-RUXQ6PAX.js +58 -0
  119. package/dist/get-scenario-RNM7BAX2.js +37 -0
  120. package/dist/{get-scenario-Q3LLQE4T.js → get-scenario-YM4ODWW6.js} +5 -5
  121. package/dist/get-simulation-run-6GIATQE5.js +69 -0
  122. package/dist/{get-simulation-run-WINEYDMK.js → get-simulation-run-LCFSMPGQ.js} +26 -22
  123. package/dist/{get-test-suite-4HKA7OKS.js → get-test-suite-GC2O7YH6.js} +5 -5
  124. package/dist/get-test-suite-P5LJ6VLF.js +48 -0
  125. package/dist/get-trace-KBRVV4NN.js +45 -0
  126. package/dist/{get-trace-RCUV7N4O.js → get-trace-SEF3FMR5.js} +11 -13
  127. package/dist/get-workflow-7PVED7JE.js +25 -0
  128. package/dist/{get-workflow-EEOC7Y45.js → get-workflow-YXULW4GC.js} +4 -4
  129. package/dist/{http-security-EU3CHTY7.js → http-security-DNLBNQZL.js} +2 -2
  130. package/dist/{http-server-WCDUARTH.js → http-server-IR4QOS43.js} +4071 -347
  131. package/dist/index.js +58671 -37983
  132. package/dist/langwatch-api-annotations-6SA4OB4C.js +17 -0
  133. package/dist/{langwatch-api-annotations-6DHVZWUK.js → langwatch-api-annotations-TBHLCQ5R.js} +4 -4
  134. package/dist/langwatch-api-dashboards-6EO2VVZW.js +17 -0
  135. package/dist/{langwatch-api-dashboards-2KJL2M42.js → langwatch-api-dashboards-NHU5BZD7.js} +4 -4
  136. package/dist/langwatch-api-monitors-CBF3FGIM.js +47 -0
  137. package/dist/{langwatch-api-monitors-6ISFJ7WX.js → langwatch-api-monitors-YNWA2M5Y.js} +11 -16
  138. package/dist/langwatch-api-secrets-ADTOJELP.js +56 -0
  139. package/dist/langwatch-api-secrets-RNFO4LF2.js +55 -0
  140. package/dist/langwatch-api-triggers-KQDQC6L5.js +22 -0
  141. package/dist/langwatch-api-triggers-QS7HDIVW.js +23 -0
  142. package/dist/{langwatch-api-workflows-6UJL5U4Z.js → langwatch-api-workflows-OKC4FXIF.js} +4 -4
  143. package/dist/langwatch-api-workflows-XGJE6SR5.js +15 -0
  144. package/dist/{list-agents-AMVDQPQL.js → list-agents-HVYWEQMI.js} +4 -4
  145. package/dist/list-agents-RBAOCF53.js +42 -0
  146. package/dist/list-annotations-HJH4NBLD.js +38 -0
  147. package/dist/{list-annotations-6PR5NLIC.js → list-annotations-J4UAHSUN.js} +10 -5
  148. package/dist/{list-api-keys-GVPNBIHQ.js → list-api-keys-7UTJRZDG.js} +10 -5
  149. package/dist/list-api-keys-A2PMV435.js +42 -0
  150. package/dist/list-dashboard-boards-OPWROHMG.js +33 -0
  151. package/dist/list-dashboard-boards-U2GJ7GC5.js +34 -0
  152. package/dist/{list-dashboards-Z3BZKDGF.js → list-dashboards-ROSYFLDQ.js} +4 -4
  153. package/dist/list-dashboards-RQ635ZU7.js +32 -0
  154. package/dist/{list-dataset-records-O67HZ35Y.js → list-dataset-records-M5TKA6LK.js} +14 -10
  155. package/dist/list-dataset-records-WFM7F33C.js +43 -0
  156. package/dist/{list-datasets-WXZL6UMP.js → list-datasets-B5X4P7BQ.js} +9 -13
  157. package/dist/list-datasets-THUKQHPX.js +56 -0
  158. package/dist/{list-evaluators-PAX72WCN.js → list-evaluators-MYH2OVCI.js} +5 -7
  159. package/dist/list-evaluators-NLVWF6K7.js +32 -0
  160. package/dist/{list-model-providers-VVLZMLCX.js → list-model-providers-3JUGP4YI.js} +4 -4
  161. package/dist/list-model-providers-H3XKORFO.js +35 -0
  162. package/dist/{list-projects-AQVI6SUK.js → list-projects-6EJ5YYEW.js} +5 -7
  163. package/dist/list-projects-YNNSRMLC.js +40 -0
  164. package/dist/{list-prompt-tags-WZFBJOHG.js → list-prompt-tags-NVFQO6WX.js} +3 -3
  165. package/dist/list-prompt-tags-XUQTTAH4.js +23 -0
  166. package/dist/{list-prompts-BQOHRHYT.js → list-prompts-HSIMMHTF.js} +4 -6
  167. package/dist/list-prompts-IRI6AIXO.js +30 -0
  168. package/dist/{list-run-plans-UBAU2DGH.js → list-run-plans-PX2XI6EK.js} +5 -5
  169. package/dist/list-run-plans-QALLCN2K.js +45 -0
  170. package/dist/{list-scenarios-MS4IUIVM.js → list-scenarios-QZRPVHS4.js} +13 -11
  171. package/dist/list-scenarios-SSJQEMJP.js +45 -0
  172. package/dist/list-simulation-runs-KNOLGMDV.js +53 -0
  173. package/dist/{list-simulation-runs-D6YF4ZOU.js → list-simulation-runs-OEADLLQR.js} +12 -8
  174. package/dist/list-test-suites-ANAQBLDG.js +34 -0
  175. package/dist/{list-test-suites-L7R2O6DR.js → list-test-suites-N4WVLBKK.js} +4 -4
  176. package/dist/list-trigger-fires-JV3WFYYG.js +19 -0
  177. package/dist/list-trigger-fires-WTW4DVIW.js +20 -0
  178. package/dist/list-triggers-SQNIG35Q.js +45 -0
  179. package/dist/list-triggers-V4Y356AK.js +46 -0
  180. package/dist/{list-workflows-6MV4XJHF.js → list-workflows-IMSX7SXU.js} +5 -7
  181. package/dist/list-workflows-ZHTX5LCL.js +33 -0
  182. package/dist/{rename-prompt-tag-K5LQ4CY6.js → rename-prompt-tag-IQI7MHOI.js} +3 -3
  183. package/dist/rename-prompt-tag-PA2BRIE6.js +18 -0
  184. package/dist/{rename-test-suite-NTYHAO5X.js → rename-test-suite-4J7UX5S2.js} +4 -4
  185. package/dist/rename-test-suite-NNVLMLKY.js +20 -0
  186. package/dist/report-issue-AS34ZMJF.js +898 -0
  187. package/dist/{report-issue-RHZZ33IS.js → report-issue-DCZFLGDO.js} +81 -67
  188. package/dist/{rerun-run-plan-MUDWJBE4.js → rerun-run-plan-TG2V4WM4.js} +5 -5
  189. package/dist/rerun-run-plan-ZI5OY3VA.js +21 -0
  190. package/dist/revoke-api-key-3J3ZA4UB.js +15 -0
  191. package/dist/{revoke-api-key-6LAZ3QZZ.js → revoke-api-key-SFMEFWJC.js} +4 -4
  192. package/dist/run-agent-W4VJP72Z.js +75 -0
  193. package/dist/run-agent-YNV6ZHW4.js +74 -0
  194. package/dist/run-plan-6C77GWG4.js +38 -0
  195. package/dist/{run-plan-T4ZYOZGA.js → run-plan-PAARQSI4.js} +7 -7
  196. package/dist/run-query-KJDH544C.js +47 -0
  197. package/dist/run-query-T4QW2Z5O.js +46 -0
  198. package/dist/{run-test-suite-L4MZ7G4D.js → run-test-suite-SQJELEZX.js} +6 -6
  199. package/dist/run-test-suite-STB6NUJM.js +25 -0
  200. package/dist/{run-workflow-DFIS23AP.js → run-workflow-D6PQNBI6.js} +4 -4
  201. package/dist/run-workflow-SP32SO5M.js +36 -0
  202. package/dist/search-traces-HCBGOG6R.js +170 -0
  203. package/dist/search-traces-HSTQ6WTQ.js +171 -0
  204. package/dist/{set-model-provider-XRNMFOVR.js → set-model-provider-GF7NXUJA.js} +4 -4
  205. package/dist/set-model-provider-KDQ7C4EZ.js +33 -0
  206. package/dist/{update-agent-DUWNYBNA.js → update-agent-DWG43T2P.js} +4 -4
  207. package/dist/update-agent-MXNOBS4H.js +20 -0
  208. package/dist/update-dashboard-widget-CJKJLCDY.js +71 -0
  209. package/dist/update-dashboard-widget-CYQDS2NR.js +70 -0
  210. package/dist/update-dataset-DQH3OHOU.js +22 -0
  211. package/dist/{update-dataset-YSOMDCOB.js → update-dataset-SQZ6T4WX.js} +6 -6
  212. package/dist/{update-dataset-record-DEA2ITYR.js → update-dataset-record-3AT4IPBC.js} +4 -4
  213. package/dist/update-dataset-record-7PLDGTMR.js +19 -0
  214. package/dist/update-evaluator-4LFTKFPT.js +24 -0
  215. package/dist/{update-evaluator-NH7Q5GWV.js → update-evaluator-IGTX6PX4.js} +4 -4
  216. package/dist/{update-project-Q26FBSYH.js → update-project-PLJIOZS6.js} +4 -4
  217. package/dist/update-project-QVHSJRUP.js +24 -0
  218. package/dist/update-prompt-GDKRYJI3.js +140 -0
  219. package/dist/{update-prompt-ZQQ2C3KU.js → update-prompt-MY263DYF.js} +27 -29
  220. package/dist/update-scenario-7KA3JDXA.js +34 -0
  221. package/dist/{update-scenario-WUX34JAR.js → update-scenario-MOHRQKAQ.js} +5 -5
  222. package/package.json +35 -39
  223. package/pyproject.toml +8 -8
  224. package/src/config.d.ts +4 -0
  225. package/src/config.ts +20 -29
  226. package/src/create-mcp-server.ts +755 -609
  227. package/src/documentation-fetch.ts +21 -20
  228. package/src/http-security.ts +166 -135
  229. package/src/http-server.ts +189 -224
  230. package/src/index.ts +14 -16
  231. package/src/langwatch-api-agents.ts +18 -21
  232. package/src/langwatch-api-annotations.ts +11 -12
  233. package/src/langwatch-api-api-keys.ts +8 -9
  234. package/src/langwatch-api-boards.ts +46 -0
  235. package/src/langwatch-api-dashboard-widgets.ts +112 -0
  236. package/src/langwatch-api-dashboards.ts +13 -10
  237. package/src/langwatch-api-datasets.ts +98 -32
  238. package/src/langwatch-api-evaluators.ts +13 -19
  239. package/src/langwatch-api-model-providers.ts +4 -4
  240. package/src/langwatch-api-monitors.ts +14 -18
  241. package/src/langwatch-api-projects.ts +27 -9
  242. package/src/langwatch-api-query.ts +114 -0
  243. package/src/langwatch-api-run-plans.ts +12 -29
  244. package/src/langwatch-api-scenarios.ts +9 -11
  245. package/src/langwatch-api-secrets.ts +31 -24
  246. package/src/langwatch-api-simulation-runs.ts +6 -8
  247. package/src/langwatch-api-test-suites.ts +14 -33
  248. package/src/langwatch-api-triggers.ts +96 -34
  249. package/src/langwatch-api-workflows.ts +9 -9
  250. package/src/langwatch-api.ts +108 -124
  251. package/src/public-http-request.ts +21 -12
  252. package/src/schemas/analytics-metrics.ts +3 -15
  253. package/src/schemas/create-dataset.ts +3 -4
  254. package/src/schemas/run-plan.ts +13 -29
  255. package/src/schemas/suite-fields.ts +6 -23
  256. package/src/schemas/triggers.ts +365 -0
  257. package/src/tools/add-dashboard-widget.ts +63 -0
  258. package/src/tools/archive-project.ts +2 -4
  259. package/src/tools/archive-run-plan.ts +2 -4
  260. package/src/tools/archive-scenario.ts +2 -4
  261. package/src/tools/archive-test-suite.ts +2 -4
  262. package/src/tools/assign-prompt-tag.ts +6 -2
  263. package/src/tools/create-agent.ts +1 -1
  264. package/src/tools/create-annotation.ts +7 -2
  265. package/src/tools/create-api-key.ts +4 -6
  266. package/src/tools/create-dashboard.ts +1 -1
  267. package/src/tools/create-dataset-records.ts +4 -8
  268. package/src/tools/create-dataset.ts +5 -7
  269. package/src/tools/create-evaluator.ts +3 -5
  270. package/src/tools/create-project.ts +2 -2
  271. package/src/tools/create-prompt-tag.ts +2 -4
  272. package/src/tools/create-prompt.ts +9 -18
  273. package/src/tools/create-scenario.ts +5 -7
  274. package/src/tools/create-test-suite.ts +4 -9
  275. package/src/tools/create-trigger.ts +55 -0
  276. package/src/tools/dashboard-exists.ts +25 -0
  277. package/src/tools/dashboard-widget-queries.ts +32 -0
  278. package/src/tools/delete-agent.ts +1 -1
  279. package/src/tools/delete-annotation.ts +1 -1
  280. package/src/tools/delete-dashboard.ts +1 -1
  281. package/src/tools/delete-dataset-records.ts +4 -8
  282. package/src/tools/delete-dataset.ts +3 -7
  283. package/src/tools/delete-evaluator.ts +4 -6
  284. package/src/tools/delete-prompt-tag.ts +2 -4
  285. package/src/tools/delete-workflow.ts +1 -1
  286. package/src/tools/discover-evaluator-schema.ts +34 -25
  287. package/src/tools/discover-scenario-schema.ts +41 -21
  288. package/src/tools/discover-schema.ts +149 -27
  289. package/src/tools/experiment-run-status.ts +2 -8
  290. package/src/tools/format-dataset-mutation.ts +5 -10
  291. package/src/tools/format-run-plan.ts +7 -21
  292. package/src/tools/format-scenario.ts +2 -4
  293. package/src/tools/format-suite-details.ts +6 -12
  294. package/src/tools/get-agent.ts +15 -6
  295. package/src/tools/get-analytics.ts +52 -46
  296. package/src/tools/get-dashboard.ts +1 -1
  297. package/src/tools/get-dataset.ts +34 -16
  298. package/src/tools/get-evaluator.ts +4 -11
  299. package/src/tools/get-experiment-results.ts +219 -186
  300. package/src/tools/get-project.ts +2 -4
  301. package/src/tools/get-prompt.ts +63 -84
  302. package/src/tools/get-run-plan.ts +4 -6
  303. package/src/tools/get-scenario.ts +4 -6
  304. package/src/tools/get-simulation-run.ts +27 -24
  305. package/src/tools/get-test-suite.ts +2 -5
  306. package/src/tools/get-trace.ts +16 -17
  307. package/src/tools/get-workflow.ts +1 -1
  308. package/src/tools/list-agents.ts +1 -1
  309. package/src/tools/list-annotations.ts +8 -5
  310. package/src/tools/list-api-keys.ts +7 -2
  311. package/src/tools/list-dashboard-boards.ts +28 -0
  312. package/src/tools/list-dashboards.ts +1 -1
  313. package/src/tools/list-dataset-records.ts +8 -6
  314. package/src/tools/list-datasets.ts +12 -23
  315. package/src/tools/list-evaluators.ts +4 -8
  316. package/src/tools/list-experiment-runs.ts +5 -9
  317. package/src/tools/list-experiments.ts +5 -11
  318. package/src/tools/list-model-providers.ts +3 -5
  319. package/src/tools/list-projects.ts +2 -4
  320. package/src/tools/list-prompt-tags.ts +3 -3
  321. package/src/tools/list-prompts.ts +4 -8
  322. package/src/tools/list-run-plans.ts +2 -2
  323. package/src/tools/list-scenarios.ts +14 -15
  324. package/src/tools/list-simulation-runs.ts +8 -4
  325. package/src/tools/list-test-suites.ts +1 -1
  326. package/src/tools/list-trigger-fires.ts +17 -0
  327. package/src/tools/list-triggers.ts +39 -0
  328. package/src/tools/list-workflows.ts +2 -4
  329. package/src/tools/rename-prompt-tag.ts +1 -1
  330. package/src/tools/rename-test-suite.ts +2 -5
  331. package/src/tools/report-issue.ts +5 -9
  332. package/src/tools/rerun-run-plan.ts +2 -5
  333. package/src/tools/revoke-api-key.ts +2 -4
  334. package/src/tools/run-agent.ts +29 -26
  335. package/src/tools/run-experiment.ts +36 -37
  336. package/src/tools/run-plan.ts +4 -7
  337. package/src/tools/run-query.ts +47 -0
  338. package/src/tools/run-test-suite.ts +4 -7
  339. package/src/tools/run-workflow.ts +2 -5
  340. package/src/tools/search-traces.ts +161 -47
  341. package/src/tools/set-model-provider.ts +4 -6
  342. package/src/tools/test-agent.ts +2 -2
  343. package/src/tools/update-agent.ts +1 -1
  344. package/src/tools/update-dashboard-widget.ts +97 -0
  345. package/src/tools/update-dataset-record.ts +1 -1
  346. package/src/tools/update-dataset.ts +5 -7
  347. package/src/tools/update-evaluator.ts +3 -5
  348. package/src/tools/update-project.ts +1 -1
  349. package/src/tools/update-prompt.ts +44 -59
  350. package/src/tools/update-scenario.ts +5 -7
  351. package/src/tools/update-test-suite.ts +6 -11
  352. package/src/utils/date-parsing.ts +1 -1
  353. package/src/utils/escape-markdown.ts +3 -8
  354. package/src/utils/format-evaluations.ts +6 -6
  355. package/src/utils/markdown-table.ts +58 -0
  356. package/src/utils/trace-id-shape.ts +20 -0
  357. package/tsconfig.app.json +24 -0
  358. package/tsconfig.json +14 -12
  359. package/vitest.integration.config.js +10 -0
  360. package/vitest.scenario.config.js +9 -0
  361. package/.eslintrc.cjs +0 -36
  362. package/dist/chunk-AN4OUA4D.js +0 -86
  363. package/dist/chunk-GELJ2MQR.js +0 -226
  364. package/dist/chunk-K37ZNPXK.js +0 -24
  365. package/dist/config.d.ts +0 -17
  366. package/dist/create-mcp-server.d.ts +0 -10
  367. package/dist/delete-evaluator-RKYC4TNB.js +0 -16
  368. package/dist/discover-evaluator-schema-ESSHQGPY.js +0 -1912
  369. package/dist/get-analytics-R6AZD3AW.js +0 -73
  370. package/dist/get-dataset-AKTYSQDI.js +0 -49
  371. package/dist/index.d.ts +0 -2
  372. package/dist/langwatch-api-secrets-OEBD6GBU.js +0 -41
  373. package/dist/langwatch-api-triggers-5E6DDGX6.js +0 -31
  374. package/dist/run-agent-CAGWGTYT.js +0 -72
  375. package/dist/search-traces-4XTJYXIK.js +0 -79
  376. package/src/__tests__/agent-tools.unit.test.ts +0 -257
  377. package/src/__tests__/all-tools.integration.test.ts +0 -1872
  378. package/src/__tests__/config.unit.test.ts +0 -202
  379. package/src/__tests__/datasets.integration.test.ts +0 -620
  380. package/src/__tests__/datasets.unit.test.ts +0 -244
  381. package/src/__tests__/date-parsing.unit.test.ts +0 -78
  382. package/src/__tests__/discover-evaluator-schema.unit.test.ts +0 -89
  383. package/src/__tests__/discover-schema.unit.test.ts +0 -118
  384. package/src/__tests__/docker.integration.test.ts +0 -318
  385. package/src/__tests__/documentation-fetch-security.integration.test.ts +0 -182
  386. package/src/__tests__/evaluator-tools.unit.test.ts +0 -262
  387. package/src/__tests__/experiment-list-tools.unit.test.ts +0 -157
  388. package/src/__tests__/experiment-status.unit.test.ts +0 -194
  389. package/src/__tests__/feature-map-tool-drift.unit.test.ts +0 -93
  390. package/src/__tests__/get-experiment-results.unit.test.ts +0 -313
  391. package/src/__tests__/http-security-origin.unit.test.ts +0 -97
  392. package/src/__tests__/http-security-sessions.unit.test.ts +0 -209
  393. package/src/__tests__/http-security-verifier.unit.test.ts +0 -248
  394. package/src/__tests__/http-server-allocation.integration.test.ts +0 -279
  395. package/src/__tests__/http-server-origin.integration.test.ts +0 -169
  396. package/src/__tests__/http-server-session-auth.integration.test.ts +0 -341
  397. package/src/__tests__/http-transport.integration.test.ts +0 -395
  398. package/src/__tests__/integration.integration.test.ts +0 -284
  399. package/src/__tests__/langwatch-api.unit.test.ts +0 -661
  400. package/src/__tests__/model-provider-tools.unit.test.ts +0 -190
  401. package/src/__tests__/project-api-key-tools.unit.test.ts +0 -451
  402. package/src/__tests__/prompt-response-types.unit.test.ts +0 -51
  403. package/src/__tests__/prompts.integration.test.ts +0 -736
  404. package/src/__tests__/public-http-request-security.integration.test.ts +0 -118
  405. package/src/__tests__/report-issue.integration.test.ts +0 -147
  406. package/src/__tests__/run-plan-tools.unit.test.ts +0 -565
  407. package/src/__tests__/scenario-tools.integration.test.ts +0 -286
  408. package/src/__tests__/scenario-tools.unit.test.ts +0 -207
  409. package/src/__tests__/schemas.unit.test.ts +0 -85
  410. package/src/__tests__/simulation-run-tools.unit.test.ts +0 -116
  411. package/src/__tests__/support/http-server-harness.ts +0 -138
  412. package/src/__tests__/test-suite-tools.unit.test.ts +0 -615
  413. package/src/__tests__/tools.integration.test.ts +0 -896
  414. package/src/schemas/filter-fields.ts +0 -119
  415. package/src/schemas/index.ts +0 -9
@@ -1,736 +0,0 @@
1
- import { describe, it, expect, vi, beforeEach } from "vitest";
2
- import { handleGetPrompt } from "../tools/get-prompt.js";
3
- import { handleUpdatePrompt } from "../tools/update-prompt.js";
4
- import {
5
- getPrompt,
6
- getPromptVersions,
7
- updatePrompt,
8
- type PromptDetailResponse,
9
- type PromptVersion,
10
- } from "../langwatch-api.js";
11
-
12
- // Partial mock (spread over the real module) rather than a full replacement:
13
- // Scenario 11 dynamically imports create-mcp-server.js, which re-exports other
14
- // langwatch-api.js members (e.g. LangWatchApiError) at tool-registration time.
15
- // Only the prompt read/write functions need to be fakes for these tests.
16
- vi.mock("../langwatch-api.js", async (importOriginal) => {
17
- const actual = await importOriginal<typeof import("../langwatch-api.js")>();
18
- return {
19
- ...actual,
20
- getPrompt: vi.fn(),
21
- getPromptVersions: vi.fn(),
22
- updatePrompt: vi.fn(),
23
- };
24
- });
25
-
26
- const mockGetPrompt = vi.mocked(getPrompt);
27
- const mockGetPromptVersions = vi.mocked(getPromptVersions);
28
- const mockUpdatePrompt = vi.mocked(updatePrompt);
29
-
30
- beforeEach(() => {
31
- vi.clearAllMocks();
32
- // The versions listing is only a fallback; default to "no versions found"
33
- // so tests exercising the top-level commitMessage match stay honest.
34
- mockGetPromptVersions.mockResolvedValue([]);
35
- });
36
-
37
- /**
38
- * Fixtures below mirror the real `GET /api/prompts/:id` contract
39
- * (apiResponsePromptWithVersionDataSchema in
40
- * platform/app/src/app/api/prompts/[[...route]]/schemas/outputs.ts):
41
- * the returned version's data is flattened to the top level, `parameters`
42
- * is an object map (runtimeParametersSchema, defaulting to {}), `tags` is
43
- * an array of { name, versionId } objects, and there is NO nested
44
- * `versions` array — version history lives behind GET /:id/versions.
45
- */
46
-
47
- describe("handleGetPrompt()", () => {
48
- describe("given the API returns the latest version with full data", () => {
49
- describe("when the returned version has every renderable field set", () => {
50
- /** @scenario "Rendering every field that changes how the prompt is called" */
51
- it("renders headings and readable text for parameters, inputs, outputs, model, temperature, maxTokens, and responseFormat", async () => {
52
- const fixture: PromptDetailResponse = {
53
- id: "prompt_1",
54
- handle: "my-prompt",
55
- version: 3,
56
- versionId: "ver_rich001",
57
- model: "gpt-5-mini",
58
- temperature: 0.7,
59
- maxTokens: 512,
60
- responseFormat: { type: "json_schema", name: "answer_schema" },
61
- parameters: { reasoning_effort: "low" },
62
- inputs: [{ identifier: "question", type: "str" }],
63
- outputs: [{ identifier: "answer", type: "str" }],
64
- messages: [{ role: "user", content: "{{question}}" }],
65
- tags: [],
66
- };
67
- mockGetPrompt.mockResolvedValue(fixture);
68
-
69
- const result = await handleGetPrompt({ idOrHandle: "my-prompt" });
70
-
71
- expect(result).toMatch(/parameters/i);
72
- expect(result).toMatch(/inputs/i);
73
- expect(result).toMatch(/outputs/i);
74
- expect(result).toMatch(/model/i);
75
- expect(result).toMatch(/temperature/i);
76
- expect(result).toMatch(/max ?tokens/i);
77
- expect(result).toMatch(/response ?format/i);
78
- expect(result).toContain("reasoning_effort");
79
- expect(result).toContain("question");
80
- expect(result).toContain("str");
81
- expect(result).toContain("answer");
82
- expect(result).not.toContain("[object Object]");
83
- });
84
- });
85
-
86
- });
87
- describe("when the returned version has none of those fields set", () => {
88
- /** @scenario "Omitting headings for fields absent from the API response" */
89
- it("renders no heading for parameters, inputs, outputs, model, temperature, maxTokens, or responseFormat", async () => {
90
- const fixture: PromptDetailResponse = {
91
- id: "prompt_1",
92
- handle: "my-prompt",
93
- version: 1,
94
- versionId: "ver_bare001",
95
- commitMessage: "Initial version",
96
- // The API defaults parameters to {} — an empty map, not an array.
97
- parameters: {},
98
- tags: [],
99
- };
100
- mockGetPrompt.mockResolvedValue(fixture);
101
-
102
- const result = await handleGetPrompt({ idOrHandle: "my-prompt" });
103
-
104
- expect(result).not.toMatch(/#+\s*parameters/i);
105
- expect(result).not.toMatch(/#+\s*inputs/i);
106
- expect(result).not.toMatch(/#+\s*outputs/i);
107
- expect(result).not.toMatch(/#+\s*temperature/i);
108
- expect(result).not.toMatch(/#+\s*max ?tokens/i);
109
- expect(result).not.toMatch(/#+\s*response ?format/i);
110
- expect(result).not.toContain("[object Object]");
111
- expect(result).not.toContain("**Parameters**");
112
- });
113
- });
114
-
115
- describe("when a pinned older version has every renderable field set", () => {
116
- /** @scenario "Rendering the pinned version's fields when version pins an older one" */
117
- it("renders the pinned version's fields, not the latest version's", async () => {
118
- // Pinning version=2 makes the API return that version's data at the
119
- // top level.
120
- const fixture: PromptDetailResponse = {
121
- id: "prompt_1",
122
- handle: "my-prompt",
123
- version: 2,
124
- versionId: "ver_older002",
125
- model: "gpt-5-mini",
126
- temperature: 0.5,
127
- maxTokens: 256,
128
- responseFormat: { type: "json_schema", name: "answer_schema" },
129
- parameters: { seed: 42 },
130
- inputs: [{ identifier: "question", type: "str" }],
131
- outputs: [{ identifier: "answer", type: "str" }],
132
- messages: [{ role: "user", content: "{{question}}" }],
133
- tags: [],
134
- };
135
- mockGetPrompt.mockResolvedValue(fixture);
136
-
137
- const result = await handleGetPrompt({ idOrHandle: "my-prompt", version: 2 });
138
-
139
- expect(mockGetPrompt).toHaveBeenCalledWith(
140
- "my-prompt",
141
- expect.objectContaining({ version: 2 })
142
- );
143
- expect(result).toMatch(/parameters/i);
144
- expect(result).toMatch(/inputs/i);
145
- expect(result).toMatch(/outputs/i);
146
- expect(result).toMatch(/temperature/i);
147
- expect(result).toMatch(/max ?tokens/i);
148
- expect(result).toMatch(/response ?format/i);
149
- expect(result).toContain("question");
150
- expect(result).toContain("str");
151
- expect(result).toContain("answer");
152
- expect(result).not.toContain("[object Object]");
153
- });
154
- });
155
-
156
- describe("when the returned version is tagged with multiple deployment tags", () => {
157
- /** @scenario "Listing tags currently assigned to the returned version" */
158
- it("lists production and staging as deployments of the returned version", async () => {
159
- const fixture: PromptDetailResponse = {
160
- id: "prompt_1",
161
- handle: "my-prompt",
162
- version: 1,
163
- versionId: "ver_tags001",
164
- tags: [
165
- { name: "production", versionId: "ver_tags001" },
166
- { name: "staging", versionId: "ver_tags001" },
167
- { name: "latest", versionId: "ver_tags001" },
168
- ],
169
- };
170
- mockGetPrompt.mockResolvedValue(fixture);
171
-
172
- const result = await handleGetPrompt({ idOrHandle: "my-prompt" });
173
-
174
- expect(result).toContain("production");
175
- expect(result).toContain("staging");
176
- });
177
- });
178
-
179
- describe("when the returned version has no tags of its own but production points at an older version", () => {
180
- /** @scenario "Never implying a tag is undeployed everywhere when it is only absent from this version" */
181
- it("lists production against the version it points to, without claiming it is undeployed", async () => {
182
- const fixture: PromptDetailResponse = {
183
- id: "prompt_1",
184
- handle: "my-prompt",
185
- version: 2,
186
- versionId: "ver_v2",
187
- tags: [
188
- { name: "latest", versionId: "ver_v2" },
189
- { name: "production", versionId: "ver_v1" },
190
- ],
191
- };
192
- mockGetPrompt.mockResolvedValue(fixture);
193
-
194
- const result = await handleGetPrompt({ idOrHandle: "my-prompt" });
195
-
196
- const deploymentsSection = result.split("## Deployments")[1] ?? "";
197
- expect(deploymentsSection).toContain("production");
198
- expect(deploymentsSection).toContain("ver_v1");
199
- // The tag points at another version, so it must not be marked as
200
- // assigned to the returned one.
201
- expect(deploymentsSection).not.toMatch(/production.*\(this version\)/);
202
- expect(result).not.toMatch(/undeployed/i);
203
- expect(result).not.toMatch(/not\s+deployed\s+anywhere/i);
204
- });
205
- });
206
-
207
- describe("when the returned version is tagged only with the built-in latest tag", () => {
208
- /** @scenario "Rendering an empty deployments section when the version carries only the built-in latest tag" */
209
- it("renders an empty deployments section with no claim the prompt is undeployed", async () => {
210
- const fixture: PromptDetailResponse = {
211
- id: "prompt_1",
212
- handle: "my-prompt",
213
- version: 1,
214
- versionId: "ver_v1",
215
- tags: [{ name: "latest", versionId: "ver_v1" }],
216
- };
217
- mockGetPrompt.mockResolvedValue(fixture);
218
-
219
- const result = await handleGetPrompt({ idOrHandle: "my-prompt" });
220
-
221
- expect(result).toMatch(/deployments?/i);
222
- expect(result).not.toContain("- latest");
223
- expect(result).not.toMatch(/not deployed anywhere/i);
224
- });
225
- });
226
-
227
- describe("when the returned version is tagged with a custom tag", () => {
228
- /** @scenario "Listing a custom tag as a deployment" */
229
- it("lists canary as a deployment of the returned version", async () => {
230
- const fixture: PromptDetailResponse = {
231
- id: "prompt_1",
232
- handle: "my-prompt",
233
- version: 1,
234
- versionId: "ver_v1",
235
- tags: [{ name: "canary", versionId: "ver_v1" }],
236
- };
237
- mockGetPrompt.mockResolvedValue(fixture);
238
-
239
- const result = await handleGetPrompt({ idOrHandle: "my-prompt" });
240
-
241
- expect(result).toContain("canary");
242
- });
243
- });
244
-
245
- describe("when a prompt version exists", () => {
246
- /** @scenario "Including the returned version's versionId" */
247
- it("includes the returned version's versionId", async () => {
248
- const fixture: PromptDetailResponse = {
249
- id: "prompt_1",
250
- handle: "my-prompt",
251
- version: 1,
252
- versionId: "ver_xyz789",
253
- tags: [],
254
- };
255
- mockGetPrompt.mockResolvedValue(fixture);
256
-
257
- const result = await handleGetPrompt({ idOrHandle: "my-prompt" });
258
-
259
- expect(result).toContain("ver_xyz789");
260
- });
261
- });
262
-
263
- describe("when the API returns unexpected shapes for parameters and tags", () => {
264
- /** @scenario "Rendering every field that changes how the prompt is called" */
265
- it("does not crash on an array parameters shape or string tags", async () => {
266
- // Defensive: the pre-fix code crashed with "fields is not iterable"
267
- // on the real object-map parameters; the fix must survive both the
268
- // real shape and the legacy/assumed ones.
269
- const fixture = {
270
- id: "prompt_1",
271
- handle: "my-prompt",
272
- version: 1,
273
- versionId: "ver_v1",
274
- parameters: [{ identifier: "temperature", type: "float" }],
275
- tags: ["production", "latest"],
276
- } as unknown as PromptDetailResponse;
277
- mockGetPrompt.mockResolvedValue(fixture);
278
-
279
- const result = await handleGetPrompt({ idOrHandle: "my-prompt" });
280
-
281
- expect(result).toContain("temperature");
282
- expect(result).toContain("production");
283
- expect(result).not.toContain("[object Object]");
284
- });
285
- });
286
-
287
- describe("when called with format json", () => {
288
- /** @scenario "Requesting the unabridged API payload via format json" */
289
- it("returns a response that parses as the full platform_get_prompt API payload", async () => {
290
- const fixture: PromptDetailResponse = {
291
- id: "prompt_1",
292
- handle: "my-prompt",
293
- version: 1,
294
- versionId: "ver_v1",
295
- model: "gpt-5-mini",
296
- parameters: {},
297
- tags: [],
298
- };
299
- mockGetPrompt.mockResolvedValue(fixture);
300
-
301
- const result = await handleGetPrompt({
302
- idOrHandle: "my-prompt",
303
- format: "json",
304
- });
305
-
306
- const parsed = JSON.parse(result);
307
- expect(parsed).toEqual(fixture);
308
- });
309
- });
310
-
311
- describe("when called without a format argument", () => {
312
- /** @scenario "Defaulting to the digest format when format is omitted" */
313
- it("returns the rendered digest, not the raw API payload", async () => {
314
- const fixture: PromptDetailResponse = {
315
- id: "prompt_1",
316
- handle: "my-prompt",
317
- version: 1,
318
- versionId: "ver_v1",
319
- model: "gpt-5-mini",
320
- tags: [],
321
- };
322
- mockGetPrompt.mockResolvedValue(fixture);
323
-
324
- const result = await handleGetPrompt({ idOrHandle: "my-prompt" });
325
-
326
- expect(result).not.toEqual(JSON.stringify(fixture));
327
- expect(() => JSON.parse(result)).toThrow();
328
- expect(result).toMatch(/^# Prompt:/);
329
- });
330
- });
331
- });
332
-
333
- describe("MCP server platform_get_prompt tool registration", () => {
334
- describe("when inspecting the registered tool's input schema and description", () => {
335
- /** @scenario "Documenting the format parameter on the registered tool schema" */
336
- it("exposes a format parameter accepting digest or json and documents it in the description", async () => {
337
- const { createMcpServer } = await import("../create-mcp-server.js");
338
- const server = createMcpServer();
339
- const registeredTools = (
340
- server as unknown as {
341
- _registeredTools: Record<
342
- string,
343
- { description?: string; inputSchema?: { shape?: Record<string, unknown> } }
344
- >;
345
- }
346
- )._registeredTools;
347
-
348
- const tool = registeredTools["platform_get_prompt"];
349
- expect(tool).toBeDefined();
350
-
351
- const shapeKeys = Object.keys(tool?.inputSchema?.shape ?? {});
352
- expect(shapeKeys).toContain("format");
353
- expect(tool?.description ?? "").toMatch(/format/i);
354
- });
355
- });
356
- });
357
-
358
- /**
359
- * Write-path spec (issue #5666 AC5-9). After updatePrompt succeeds, the tool
360
- * re-fetches the prompt via getPrompt to derive authoritative state (the
361
- * mutation response alone does not carry applied tags). The GET response is
362
- * the prompt's latest version flattened to the top level, so the new version
363
- * is identified by matching the request's commitMessage against the
364
- * top-level commitMessage (falling back to GET /:id/versions), and
365
- * deployment state comes from the tags whose versionId points at it (the
366
- * built-in "latest" tag is never a deployment). On updatePrompt failure with
367
- * tags requested, the tool re-fetches and matches the same way to detect the
368
- * committed-but-untagged version (the platform commits the version before
369
- * assigning tags).
370
- */
371
-
372
- /** No single output line may pair a version number with a deployment tag name. */
373
- function expectNoLineMixesVersionAndTag(result: string, tags: string[]) {
374
- for (const line of result.split("\n")) {
375
- const hasTag = tags.some((t) => line.includes(t));
376
- if (hasTag) {
377
- expect(line).not.toMatch(/\bv\d+\b|\bversion\b\W*\d+/i);
378
- }
379
- }
380
- }
381
-
382
- describe("handleUpdatePrompt()", () => {
383
- describe("when the server applies different tags than the request asked for", () => {
384
- /** @scenario "Deriving tag and deployment state from the server response, not the request" */
385
- it("reflects the tags the server applied, not the request's tags", async () => {
386
- mockUpdatePrompt.mockResolvedValue({
387
- id: "prompt_1",
388
- handle: "my-prompt",
389
- });
390
- mockGetPrompt.mockResolvedValue({
391
- id: "prompt_1",
392
- handle: "my-prompt",
393
- version: 4,
394
- versionId: "ver_new004",
395
- commitMessage: "Update greeting",
396
- tags: [
397
- { name: "staging", versionId: "ver_new004" },
398
- { name: "latest", versionId: "ver_new004" },
399
- ],
400
- });
401
-
402
- const result = await handleUpdatePrompt({
403
- idOrHandle: "my-prompt",
404
- commitMessage: "Update greeting",
405
- tags: ["production"],
406
- });
407
-
408
- expect(result).toContain("staging");
409
- // The request asked for production but the server did not apply it;
410
- // echoing the request's tags is exactly the bug under test.
411
- expect(result).not.toContain("production");
412
- });
413
- });
414
-
415
- describe("when an update without tags lands on a prompt with existing deployments", () => {
416
- /** @scenario "Reporting a new version as not deployed and existing deployments as untouched" */
417
- it("states the new version is not deployed and production/staging were left untouched", async () => {
418
- mockUpdatePrompt.mockResolvedValue({
419
- id: "prompt_1",
420
- handle: "my-prompt",
421
- });
422
- mockGetPrompt.mockResolvedValue({
423
- id: "prompt_1",
424
- handle: "my-prompt",
425
- version: 6,
426
- versionId: "ver_new006",
427
- commitMessage: "Tweak wording",
428
- tags: [
429
- { name: "latest", versionId: "ver_new006" },
430
- { name: "production", versionId: "ver_old005" },
431
- { name: "staging", versionId: "ver_old005" },
432
- ],
433
- });
434
-
435
- const result = await handleUpdatePrompt({
436
- idOrHandle: "my-prompt",
437
- commitMessage: "Tweak wording",
438
- });
439
-
440
- expect(result).toMatch(/not deployed/i);
441
- expect(result).toMatch(/production/);
442
- expect(result).toMatch(/staging/);
443
- expect(result).toMatch(/untouched|unchanged|still point/i);
444
- expectNoLineMixesVersionAndTag(result, ["production", "staging"]);
445
- });
446
- });
447
-
448
- describe("when a requested tag was actually assigned by the server", () => {
449
- /** @scenario "Using the word deployed only when a tag was actually assigned" */
450
- it("says the new version is deployed to production", async () => {
451
- mockUpdatePrompt.mockResolvedValue({
452
- id: "prompt_1",
453
- handle: "my-prompt",
454
- });
455
- mockGetPrompt.mockResolvedValue({
456
- id: "prompt_1",
457
- handle: "my-prompt",
458
- version: 7,
459
- versionId: "ver_new007",
460
- commitMessage: "Ship it",
461
- tags: [
462
- { name: "production", versionId: "ver_new007" },
463
- { name: "latest", versionId: "ver_new007" },
464
- ],
465
- });
466
-
467
- const result = await handleUpdatePrompt({
468
- idOrHandle: "my-prompt",
469
- commitMessage: "Ship it",
470
- tags: ["production"],
471
- });
472
-
473
- expect(result).toMatch(/deployed/i);
474
- expect(result).toContain("production");
475
- expectNoLineMixesVersionAndTag(result, ["production"]);
476
- });
477
- });
478
-
479
- describe("when the requested tag did not previously exist on the prompt", () => {
480
- /** @scenario "Assigning a tag that did not previously exist on the prompt" */
481
- it("says the new version is deployed to canary", async () => {
482
- mockUpdatePrompt.mockResolvedValue({
483
- id: "prompt_1",
484
- handle: "my-prompt",
485
- });
486
- mockGetPrompt.mockResolvedValue({
487
- id: "prompt_1",
488
- handle: "my-prompt",
489
- version: 2,
490
- versionId: "ver_new002",
491
- commitMessage: "First canary",
492
- tags: [
493
- { name: "canary", versionId: "ver_new002" },
494
- { name: "latest", versionId: "ver_new002" },
495
- ],
496
- });
497
-
498
- const result = await handleUpdatePrompt({
499
- idOrHandle: "my-prompt",
500
- commitMessage: "First canary",
501
- tags: ["canary"],
502
- });
503
-
504
- expect(result).toMatch(/deployed/i);
505
- expect(result).toContain("canary");
506
- });
507
- });
508
-
509
- describe("when the server response carries only the built-in latest tag", () => {
510
- /** @scenario "Never presenting the built-in latest tag as a deployment after an update" */
511
- it("does not list latest as a deployment", async () => {
512
- mockUpdatePrompt.mockResolvedValue({
513
- id: "prompt_1",
514
- handle: "my-prompt",
515
- });
516
- mockGetPrompt.mockResolvedValue({
517
- id: "prompt_1",
518
- handle: "my-prompt",
519
- version: 3,
520
- versionId: "ver_new003",
521
- commitMessage: "Plain update",
522
- tags: [{ name: "latest", versionId: "ver_new003" }],
523
- });
524
-
525
- const result = await handleUpdatePrompt({
526
- idOrHandle: "my-prompt",
527
- commitMessage: "Plain update",
528
- });
529
-
530
- expect(result).not.toMatch(/deployed to.*latest/i);
531
- expect(result).not.toMatch(/\*\*Tags\*\*.*latest/);
532
- });
533
- });
534
-
535
- describe("when the update creates a new version", () => {
536
- /** @scenario "Including the new version's versionId" */
537
- it("includes the new version's versionId", async () => {
538
- mockUpdatePrompt.mockResolvedValue({
539
- id: "prompt_1",
540
- handle: "my-prompt",
541
- });
542
- mockGetPrompt.mockResolvedValue({
543
- id: "prompt_1",
544
- handle: "my-prompt",
545
- version: 9,
546
- versionId: "ver_new009",
547
- commitMessage: "Add disclaimer",
548
- tags: [{ name: "latest", versionId: "ver_new009" }],
549
- });
550
-
551
- const result = await handleUpdatePrompt({
552
- idOrHandle: "my-prompt",
553
- commitMessage: "Add disclaimer",
554
- });
555
-
556
- expect(result).toContain("ver_new009");
557
- });
558
- });
559
-
560
- describe("when the re-fetched top level does not match but the versions listing does", () => {
561
- /** @scenario "Including the new version's versionId" */
562
- it("identifies the new version via the versions listing fallback", async () => {
563
- mockUpdatePrompt.mockResolvedValue({
564
- id: "prompt_1",
565
- handle: "my-prompt",
566
- });
567
- // A concurrent update landed after ours: the top level shows a newer
568
- // commit, but GET /:id/versions still lists ours.
569
- mockGetPrompt.mockResolvedValue({
570
- id: "prompt_1",
571
- handle: "my-prompt",
572
- version: 13,
573
- versionId: "ver_racer013",
574
- commitMessage: "Someone else's commit",
575
- tags: [{ name: "latest", versionId: "ver_racer013" }],
576
- });
577
- const listedVersions: PromptVersion[] = [
578
- { version: 13, versionId: "ver_racer013", commitMessage: "Someone else's commit" },
579
- { version: 12, versionId: "ver_mine012", commitMessage: "Add disclaimer" },
580
- ];
581
- mockGetPromptVersions.mockResolvedValue(listedVersions);
582
-
583
- const result = await handleUpdatePrompt({
584
- idOrHandle: "my-prompt",
585
- commitMessage: "Add disclaimer",
586
- });
587
-
588
- expect(result).toContain("ver_mine012");
589
- expect(result).not.toMatch(/could not be identified/i);
590
- });
591
- });
592
-
593
- describe("when the new version cannot be identified after a successful update", () => {
594
- /** @scenario "Signalling when the new version cannot be identified after a successful update" */
595
- it("states that version and deployment details are unavailable instead of silently omitting them", async () => {
596
- mockUpdatePrompt.mockResolvedValue({
597
- id: "prompt_1",
598
- handle: "my-prompt",
599
- });
600
- mockGetPrompt.mockResolvedValue({
601
- id: "prompt_1",
602
- handle: "my-prompt",
603
- version: 9,
604
- versionId: "ver_stale009",
605
- commitMessage: "Some earlier commit",
606
- tags: [{ name: "latest", versionId: "ver_stale009" }],
607
- });
608
- mockGetPromptVersions.mockResolvedValue([
609
- { version: 9, versionId: "ver_stale009", commitMessage: "Some earlier commit" },
610
- ]);
611
-
612
- const result = await handleUpdatePrompt({
613
- idOrHandle: "my-prompt",
614
- commitMessage: "Unfindable commit",
615
- });
616
-
617
- expect(result).toContain("Prompt updated successfully!");
618
- expect(result).toMatch(/could not be identified/i);
619
- expect(result).toMatch(/version and deployment details are unavailable/i);
620
- expect(result).not.toContain("**Version**:");
621
- expect(result).not.toContain("**Deployed to**:");
622
- expect(result).not.toContain("**Deployment**:");
623
- });
624
- });
625
-
626
- describe("when tag assignment fails but the version was committed", () => {
627
- /** @scenario "Reporting a version as created but untagged when tag assignment fails and a matching version is found" */
628
- it("reports the version as created and untagged, with its versionId and the failed tag", async () => {
629
- const { LangWatchApiError } = await import("../langwatch-api.js");
630
- mockUpdatePrompt.mockRejectedValue(
631
- new LangWatchApiError("Tag assignment rejected", 422, "{}")
632
- );
633
- mockGetPrompt.mockResolvedValue({
634
- id: "prompt_1",
635
- handle: "my-prompt",
636
- version: 8,
637
- versionId: "ver_orphan8",
638
- commitMessage: "Risky change",
639
- tags: [{ name: "latest", versionId: "ver_orphan8" }],
640
- });
641
-
642
- const result = await handleUpdatePrompt({
643
- idOrHandle: "my-prompt",
644
- commitMessage: "Risky change",
645
- tags: ["production"],
646
- });
647
-
648
- expect(result).toMatch(/created/i);
649
- expect(result).toMatch(/untagged|not.*(tagged|deployed)/i);
650
- expect(result).toContain("ver_orphan8");
651
- expect(result).toContain("production");
652
- });
653
- });
654
-
655
- describe("when tag assignment fails and no matching version exists", () => {
656
- /** @scenario "Reporting a plain failure when tag assignment fails and no matching version is found" */
657
- it("reports a plain failure with no versionId", async () => {
658
- const { LangWatchApiError } = await import("../langwatch-api.js");
659
- mockUpdatePrompt.mockRejectedValue(
660
- new LangWatchApiError("Tag assignment rejected", 422, "{}")
661
- );
662
- mockGetPrompt.mockResolvedValue({
663
- id: "prompt_1",
664
- handle: "my-prompt",
665
- version: 8,
666
- versionId: "ver_other08",
667
- commitMessage: "Unrelated commit",
668
- tags: [{ name: "latest", versionId: "ver_other08" }],
669
- });
670
- mockGetPromptVersions.mockResolvedValue([
671
- { version: 8, versionId: "ver_other08", commitMessage: "Unrelated commit" },
672
- ]);
673
-
674
- const result = await handleUpdatePrompt({
675
- idOrHandle: "my-prompt",
676
- commitMessage: "Risky change",
677
- tags: ["production"],
678
- });
679
-
680
- expect(result).toMatch(/fail/i);
681
- expect(result).not.toContain("ver_other08");
682
- });
683
- });
684
-
685
- describe("when the update succeeds but the confirmation read fails", () => {
686
- /** @scenario "Reporting success without details when the confirmation read fails" */
687
- it("still reports success with a note that details are unavailable, instead of rejecting", async () => {
688
- const { LangWatchApiError } = await import("../langwatch-api.js");
689
- mockUpdatePrompt.mockResolvedValue({
690
- id: "prompt_1",
691
- handle: "my-prompt",
692
- });
693
- mockGetPrompt.mockRejectedValue(
694
- new LangWatchApiError("boom", 500, "{}")
695
- );
696
-
697
- const result = await handleUpdatePrompt({
698
- idOrHandle: "my-prompt",
699
- commitMessage: "Safe change",
700
- });
701
-
702
- expect(result).toContain("Prompt updated successfully!");
703
- expect(result).toContain("prompt_1");
704
- expect(result).toContain("my-prompt");
705
- expect(result).toMatch(/confirmation read failed/i);
706
- expect(result).toMatch(/version and deployment details are unavailable/i);
707
- expect(result).not.toContain("**Version**:");
708
- expect(result).not.toContain("**Deployed to**:");
709
- });
710
- });
711
-
712
- describe("when tag assignment fails and the confirmation read also fails", () => {
713
- /** @scenario "Preserving the tag-assignment failure when the confirmation read fails" */
714
- it("reports the tag failure without claiming whether a version was created", async () => {
715
- const { LangWatchApiError } = await import("../langwatch-api.js");
716
- mockUpdatePrompt.mockRejectedValue(
717
- new LangWatchApiError("Tag assignment rejected", 422, "{}")
718
- );
719
- mockGetPrompt.mockRejectedValue(
720
- new LangWatchApiError("boom", 500, "{}")
721
- );
722
-
723
- const result = await handleUpdatePrompt({
724
- idOrHandle: "my-prompt",
725
- commitMessage: "Risky change",
726
- tags: ["production"],
727
- });
728
-
729
- expect(result).toMatch(/fail/i);
730
- expect(result).toContain("production");
731
- expect(result).toMatch(/confirmation read failed/i);
732
- expect(result).not.toMatch(/version was created(?!\s+could not be confirmed)/i);
733
- expect(result).toMatch(/could not be confirmed/i);
734
- });
735
- });
736
- });