@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.
- package/.gitignore +7 -0
- package/.npmignore +41 -0
- package/CHANGELOG.md +192 -201
- package/CONTRIBUTING.md +10 -0
- package/README.md +70 -68
- package/dist/add-dashboard-widget-4ZC5UN73.js +50 -0
- package/dist/add-dashboard-widget-ACC6QYBG.js +49 -0
- package/dist/archive-project-KWFM3I5Z.js +19 -0
- package/dist/{archive-project-W3IKI7UL.js → archive-project-SPVHXK6A.js} +4 -4
- package/dist/{archive-run-plan-474OKHHU.js → archive-run-plan-65QN3XRX.js} +4 -4
- package/dist/archive-run-plan-Z4SYE6BP.js +15 -0
- package/dist/{archive-scenario-V5UNYK26.js → archive-scenario-A6QV5LOW.js} +4 -4
- package/dist/archive-scenario-FXEYQNEH.js +19 -0
- package/dist/archive-test-suite-5ADQ3KPQ.js +15 -0
- package/dist/{archive-test-suite-O7FLDMXK.js → archive-test-suite-NKZDZY3D.js} +4 -4
- package/dist/assign-prompt-tag-5IUZXJ2T.js +23 -0
- package/dist/{assign-prompt-tag-Q46WRB6P.js → assign-prompt-tag-HBZLBIFD.js} +8 -4
- package/dist/{chunk-OJLYYJWI.js → chunk-3OCG3QPU.js} +7 -10
- package/dist/chunk-3YLN3ATJ.js +241 -0
- package/dist/chunk-5FNCQRCX.js +91 -0
- package/dist/{chunk-UF6XIGYR.js → chunk-6YZUOD34.js} +2 -5
- package/dist/{chunk-WVI5CJ6S.js → chunk-7AQLPVFL.js} +2 -2
- package/dist/chunk-7VNSRCXO.js +129 -0
- package/dist/{chunk-G4ZAZTZW.js → chunk-B4J3PJLX.js} +11 -10
- package/dist/{chunk-ZXKLPC2E.js → chunk-BIIKE3EI.js} +1 -1
- package/dist/{chunk-37SMPMQS.js → chunk-DMM3JQ42.js} +7 -2
- package/dist/{chunk-NUFPTHKU.js → chunk-FOKVY3BK.js} +1 -1
- package/dist/{chunk-WT4PO4A3.js → chunk-H6WGMG2T.js} +10 -7
- package/dist/{chunk-EYXMLE5X.js → chunk-HGELNIVY.js} +56 -47
- package/dist/{chunk-UJPXFA26.js → chunk-HHMNZAPD.js} +101 -74
- package/dist/{chunk-RVAE2MEA.js → chunk-I7ATIZED.js} +7 -7
- package/dist/{chunk-S4GVX36W.js → chunk-ILZDO25L.js} +7 -10
- package/dist/{chunk-QRO35SOM.js → chunk-J7PQKBLD.js} +652 -528
- package/dist/{chunk-SR6QPRIE.js → chunk-JRJPH75H.js} +7 -13
- package/dist/chunk-K2I7Z3LZ.js +28 -0
- package/dist/{chunk-UFOYFYI7.js → chunk-NKMTIHIJ.js} +6 -9
- package/dist/{chunk-HV4DWKBI.js → chunk-O7EMWEVJ.js} +2 -4
- package/dist/{chunk-56OGTS24.js → chunk-PTDR56Z3.js} +4 -11
- package/dist/{chunk-JR6DJZOU.js → chunk-RABYBFX5.js} +4 -10
- package/dist/chunk-SUPUUC4W.js +64 -0
- package/dist/{chunk-JTQJRNFJ.js → chunk-SXFC3FNH.js} +4 -4
- package/dist/{chunk-CKNAH5TG.js → chunk-U24EACC4.js} +3 -10
- package/dist/chunk-UNJM3BIL.js +21 -0
- package/dist/{chunk-QAMP3CGR.js → chunk-UTJCWXPZ.js} +4 -4
- package/dist/chunk-UWLE5T2F.js +312 -0
- package/dist/{chunk-6DJDEXET.js → chunk-VMWJD732.js} +6 -12
- package/dist/{chunk-7E4FZIYJ.js → chunk-XUIJYNRJ.js} +1 -1
- package/dist/chunk-Y34DHR5K.js +67 -0
- package/dist/chunk-YCARO42Z.js +516 -0
- package/dist/config-EAKW6F26.js +16 -0
- package/dist/config.cjs +109 -0
- package/dist/config.js +5 -4
- package/dist/{create-agent-JFJ2K6X3.js → create-agent-AYZ3LB3V.js} +4 -4
- package/dist/create-agent-S5ZBCEFT.js +23 -0
- package/dist/create-annotation-LHNCLHYP.js +29 -0
- package/dist/{create-annotation-EUXXQ6NS.js → create-annotation-VUSXULQK.js} +10 -5
- package/dist/{create-api-key-BGBB76JX.js → create-api-key-NNYKARMG.js} +5 -7
- package/dist/create-api-key-T4KSNPKB.js +24 -0
- package/dist/create-dashboard-DHVFMEU4.js +18 -0
- package/dist/{create-dashboard-Z76AIPLS.js → create-dashboard-WTKKS5Z6.js} +4 -4
- package/dist/{create-dataset-FD5VAXU4.js → create-dataset-2MC4SD4O.js} +6 -6
- package/dist/create-dataset-LUP5PVDM.js +22 -0
- package/dist/create-dataset-records-NSKBZP3G.js +20 -0
- package/dist/{create-dataset-records-WQUXXOHI.js → create-dataset-records-W4GLN2FV.js} +5 -7
- package/dist/{create-evaluator-6NV33Z52.js → create-evaluator-643G6LXL.js} +4 -4
- package/dist/create-evaluator-HMTYO5SS.js +27 -0
- package/dist/create-mcp-server.cjs +9513 -0
- package/dist/create-mcp-server.js +2647 -10
- package/dist/create-project-6N7PIYRY.js +44 -0
- package/dist/{create-project-ZQ7TFIO2.js → create-project-KAIC46UY.js} +4 -4
- package/dist/{create-prompt-BS6QRCUU.js → create-prompt-E67LXIYT.js} +5 -7
- package/dist/create-prompt-PKJ7EDRR.js +37 -0
- package/dist/create-prompt-tag-4YO3FSJ3.js +17 -0
- package/dist/{create-prompt-tag-FCQ5ELSQ.js → create-prompt-tag-OPQUEXWB.js} +3 -3
- package/dist/create-scenario-5PJBMAXO.js +33 -0
- package/dist/{create-scenario-DRPTPXU3.js → create-scenario-UEM77OE7.js} +5 -5
- package/dist/{create-test-suite-ZIZCCFJ4.js → create-test-suite-4IPXVSFQ.js} +6 -6
- package/dist/create-test-suite-SZEPVXSE.js +36 -0
- package/dist/create-trigger-5HZSCRAN.js +49 -0
- package/dist/create-trigger-PRELMT6J.js +48 -0
- package/dist/delete-agent-JUIZWAXG.js +18 -0
- package/dist/{delete-agent-SA4JMUZF.js → delete-agent-QMEK3XHK.js} +4 -4
- package/dist/delete-annotation-HY3O2OGR.js +17 -0
- package/dist/{delete-annotation-X3KKK7QY.js → delete-annotation-VKIQWUYW.js} +4 -4
- package/dist/delete-dashboard-76N66XXL.js +18 -0
- package/dist/{delete-dashboard-LYMRPF27.js → delete-dashboard-JVIVHNJM.js} +4 -4
- package/dist/delete-dataset-HQCR66MG.js +18 -0
- package/dist/{delete-dataset-W6EZ44HY.js → delete-dataset-WKRUW4FP.js} +5 -7
- package/dist/{delete-dataset-records-7CNOTALB.js → delete-dataset-records-C2IQFFVQ.js} +5 -7
- package/dist/delete-dataset-records-SDUHQLLM.js +19 -0
- package/dist/delete-evaluator-4SNSCLEE.js +16 -0
- package/dist/delete-evaluator-QP3QHTLN.js +15 -0
- package/dist/delete-prompt-tag-NZ4JQMYG.js +17 -0
- package/dist/{delete-prompt-tag-QB3OCZUV.js → delete-prompt-tag-YYQ4VMAD.js} +3 -3
- package/dist/{delete-workflow-NG2VXYYM.js → delete-workflow-5J46T73T.js} +4 -4
- package/dist/delete-workflow-JSWABL5F.js +17 -0
- package/dist/discover-evaluator-schema-UHWA5HVL.js +120 -0
- package/dist/discover-evaluator-schema-XKCLLCB3.js +118 -0
- package/dist/discover-scenario-schema-NOJY6S7E.js +137 -0
- package/dist/{discover-scenario-schema-W7KBXA4J.js → discover-scenario-schema-OOFIQKOG.js} +37 -17
- package/dist/{discover-schema-L6QDPON3.js → discover-schema-55YJBFGQ.js} +181 -186
- package/dist/discover-schema-A33SLMXW.js +442 -0
- package/dist/get-agent-LJQX7OF4.js +69 -0
- package/dist/{get-agent-7THF7HCU.js → get-agent-OTIYUFZA.js} +15 -11
- package/dist/get-analytics-DCS72353.js +80 -0
- package/dist/get-analytics-IQZKJHLW.js +79 -0
- package/dist/get-dashboard-5NYJEIX2.js +29 -0
- package/dist/{get-dashboard-P4WWVHFU.js → get-dashboard-QMTYDQAZ.js} +4 -4
- package/dist/get-dataset-ALSP7RTX.js +64 -0
- package/dist/get-dataset-X4B5JDG2.js +63 -0
- package/dist/{get-evaluator-RZ44UIN6.js → get-evaluator-ISMC4LLI.js} +4 -4
- package/dist/get-evaluator-XEUZGZCH.js +47 -0
- package/dist/{get-project-QSQEAB33.js → get-project-5VKROSDH.js} +4 -4
- package/dist/get-project-RM6FRCBK.js +26 -0
- package/dist/{get-prompt-GVKXF5NL.js → get-prompt-OWXJZHQQ.js} +53 -58
- package/dist/get-prompt-W52DY36E.js +94 -0
- package/dist/{get-run-plan-NY7J5GRE.js → get-run-plan-BE3M2AUX.js} +7 -9
- package/dist/get-run-plan-RUXQ6PAX.js +58 -0
- package/dist/get-scenario-RNM7BAX2.js +37 -0
- package/dist/{get-scenario-Q3LLQE4T.js → get-scenario-YM4ODWW6.js} +5 -5
- package/dist/get-simulation-run-6GIATQE5.js +69 -0
- package/dist/{get-simulation-run-WINEYDMK.js → get-simulation-run-LCFSMPGQ.js} +26 -22
- package/dist/{get-test-suite-4HKA7OKS.js → get-test-suite-GC2O7YH6.js} +5 -5
- package/dist/get-test-suite-P5LJ6VLF.js +48 -0
- package/dist/get-trace-KBRVV4NN.js +45 -0
- package/dist/{get-trace-RCUV7N4O.js → get-trace-SEF3FMR5.js} +11 -13
- package/dist/get-workflow-7PVED7JE.js +25 -0
- package/dist/{get-workflow-EEOC7Y45.js → get-workflow-YXULW4GC.js} +4 -4
- package/dist/{http-security-EU3CHTY7.js → http-security-DNLBNQZL.js} +2 -2
- package/dist/{http-server-WCDUARTH.js → http-server-IR4QOS43.js} +4071 -347
- package/dist/index.js +58671 -37983
- package/dist/langwatch-api-annotations-6SA4OB4C.js +17 -0
- package/dist/{langwatch-api-annotations-6DHVZWUK.js → langwatch-api-annotations-TBHLCQ5R.js} +4 -4
- package/dist/langwatch-api-dashboards-6EO2VVZW.js +17 -0
- package/dist/{langwatch-api-dashboards-2KJL2M42.js → langwatch-api-dashboards-NHU5BZD7.js} +4 -4
- package/dist/langwatch-api-monitors-CBF3FGIM.js +47 -0
- package/dist/{langwatch-api-monitors-6ISFJ7WX.js → langwatch-api-monitors-YNWA2M5Y.js} +11 -16
- package/dist/langwatch-api-secrets-ADTOJELP.js +56 -0
- package/dist/langwatch-api-secrets-RNFO4LF2.js +55 -0
- package/dist/langwatch-api-triggers-KQDQC6L5.js +22 -0
- package/dist/langwatch-api-triggers-QS7HDIVW.js +23 -0
- package/dist/{langwatch-api-workflows-6UJL5U4Z.js → langwatch-api-workflows-OKC4FXIF.js} +4 -4
- package/dist/langwatch-api-workflows-XGJE6SR5.js +15 -0
- package/dist/{list-agents-AMVDQPQL.js → list-agents-HVYWEQMI.js} +4 -4
- package/dist/list-agents-RBAOCF53.js +42 -0
- package/dist/list-annotations-HJH4NBLD.js +38 -0
- package/dist/{list-annotations-6PR5NLIC.js → list-annotations-J4UAHSUN.js} +10 -5
- package/dist/{list-api-keys-GVPNBIHQ.js → list-api-keys-7UTJRZDG.js} +10 -5
- package/dist/list-api-keys-A2PMV435.js +42 -0
- package/dist/list-dashboard-boards-OPWROHMG.js +33 -0
- package/dist/list-dashboard-boards-U2GJ7GC5.js +34 -0
- package/dist/{list-dashboards-Z3BZKDGF.js → list-dashboards-ROSYFLDQ.js} +4 -4
- package/dist/list-dashboards-RQ635ZU7.js +32 -0
- package/dist/{list-dataset-records-O67HZ35Y.js → list-dataset-records-M5TKA6LK.js} +14 -10
- package/dist/list-dataset-records-WFM7F33C.js +43 -0
- package/dist/{list-datasets-WXZL6UMP.js → list-datasets-B5X4P7BQ.js} +9 -13
- package/dist/list-datasets-THUKQHPX.js +56 -0
- package/dist/{list-evaluators-PAX72WCN.js → list-evaluators-MYH2OVCI.js} +5 -7
- package/dist/list-evaluators-NLVWF6K7.js +32 -0
- package/dist/{list-model-providers-VVLZMLCX.js → list-model-providers-3JUGP4YI.js} +4 -4
- package/dist/list-model-providers-H3XKORFO.js +35 -0
- package/dist/{list-projects-AQVI6SUK.js → list-projects-6EJ5YYEW.js} +5 -7
- package/dist/list-projects-YNNSRMLC.js +40 -0
- package/dist/{list-prompt-tags-WZFBJOHG.js → list-prompt-tags-NVFQO6WX.js} +3 -3
- package/dist/list-prompt-tags-XUQTTAH4.js +23 -0
- package/dist/{list-prompts-BQOHRHYT.js → list-prompts-HSIMMHTF.js} +4 -6
- package/dist/list-prompts-IRI6AIXO.js +30 -0
- package/dist/{list-run-plans-UBAU2DGH.js → list-run-plans-PX2XI6EK.js} +5 -5
- package/dist/list-run-plans-QALLCN2K.js +45 -0
- package/dist/{list-scenarios-MS4IUIVM.js → list-scenarios-QZRPVHS4.js} +13 -11
- package/dist/list-scenarios-SSJQEMJP.js +45 -0
- package/dist/list-simulation-runs-KNOLGMDV.js +53 -0
- package/dist/{list-simulation-runs-D6YF4ZOU.js → list-simulation-runs-OEADLLQR.js} +12 -8
- package/dist/list-test-suites-ANAQBLDG.js +34 -0
- package/dist/{list-test-suites-L7R2O6DR.js → list-test-suites-N4WVLBKK.js} +4 -4
- package/dist/list-trigger-fires-JV3WFYYG.js +19 -0
- package/dist/list-trigger-fires-WTW4DVIW.js +20 -0
- package/dist/list-triggers-SQNIG35Q.js +45 -0
- package/dist/list-triggers-V4Y356AK.js +46 -0
- package/dist/{list-workflows-6MV4XJHF.js → list-workflows-IMSX7SXU.js} +5 -7
- package/dist/list-workflows-ZHTX5LCL.js +33 -0
- package/dist/{rename-prompt-tag-K5LQ4CY6.js → rename-prompt-tag-IQI7MHOI.js} +3 -3
- package/dist/rename-prompt-tag-PA2BRIE6.js +18 -0
- package/dist/{rename-test-suite-NTYHAO5X.js → rename-test-suite-4J7UX5S2.js} +4 -4
- package/dist/rename-test-suite-NNVLMLKY.js +20 -0
- package/dist/report-issue-AS34ZMJF.js +898 -0
- package/dist/{report-issue-RHZZ33IS.js → report-issue-DCZFLGDO.js} +81 -67
- package/dist/{rerun-run-plan-MUDWJBE4.js → rerun-run-plan-TG2V4WM4.js} +5 -5
- package/dist/rerun-run-plan-ZI5OY3VA.js +21 -0
- package/dist/revoke-api-key-3J3ZA4UB.js +15 -0
- package/dist/{revoke-api-key-6LAZ3QZZ.js → revoke-api-key-SFMEFWJC.js} +4 -4
- package/dist/run-agent-W4VJP72Z.js +75 -0
- package/dist/run-agent-YNV6ZHW4.js +74 -0
- package/dist/run-plan-6C77GWG4.js +38 -0
- package/dist/{run-plan-T4ZYOZGA.js → run-plan-PAARQSI4.js} +7 -7
- package/dist/run-query-KJDH544C.js +47 -0
- package/dist/run-query-T4QW2Z5O.js +46 -0
- package/dist/{run-test-suite-L4MZ7G4D.js → run-test-suite-SQJELEZX.js} +6 -6
- package/dist/run-test-suite-STB6NUJM.js +25 -0
- package/dist/{run-workflow-DFIS23AP.js → run-workflow-D6PQNBI6.js} +4 -4
- package/dist/run-workflow-SP32SO5M.js +36 -0
- package/dist/search-traces-HCBGOG6R.js +170 -0
- package/dist/search-traces-HSTQ6WTQ.js +171 -0
- package/dist/{set-model-provider-XRNMFOVR.js → set-model-provider-GF7NXUJA.js} +4 -4
- package/dist/set-model-provider-KDQ7C4EZ.js +33 -0
- package/dist/{update-agent-DUWNYBNA.js → update-agent-DWG43T2P.js} +4 -4
- package/dist/update-agent-MXNOBS4H.js +20 -0
- package/dist/update-dashboard-widget-CJKJLCDY.js +71 -0
- package/dist/update-dashboard-widget-CYQDS2NR.js +70 -0
- package/dist/update-dataset-DQH3OHOU.js +22 -0
- package/dist/{update-dataset-YSOMDCOB.js → update-dataset-SQZ6T4WX.js} +6 -6
- package/dist/{update-dataset-record-DEA2ITYR.js → update-dataset-record-3AT4IPBC.js} +4 -4
- package/dist/update-dataset-record-7PLDGTMR.js +19 -0
- package/dist/update-evaluator-4LFTKFPT.js +24 -0
- package/dist/{update-evaluator-NH7Q5GWV.js → update-evaluator-IGTX6PX4.js} +4 -4
- package/dist/{update-project-Q26FBSYH.js → update-project-PLJIOZS6.js} +4 -4
- package/dist/update-project-QVHSJRUP.js +24 -0
- package/dist/update-prompt-GDKRYJI3.js +140 -0
- package/dist/{update-prompt-ZQQ2C3KU.js → update-prompt-MY263DYF.js} +27 -29
- package/dist/update-scenario-7KA3JDXA.js +34 -0
- package/dist/{update-scenario-WUX34JAR.js → update-scenario-MOHRQKAQ.js} +5 -5
- package/package.json +35 -39
- package/pyproject.toml +8 -8
- package/src/config.d.ts +4 -0
- package/src/config.ts +20 -29
- package/src/create-mcp-server.ts +755 -609
- package/src/documentation-fetch.ts +21 -20
- package/src/http-security.ts +166 -135
- package/src/http-server.ts +189 -224
- package/src/index.ts +14 -16
- package/src/langwatch-api-agents.ts +18 -21
- package/src/langwatch-api-annotations.ts +11 -12
- package/src/langwatch-api-api-keys.ts +8 -9
- package/src/langwatch-api-boards.ts +46 -0
- package/src/langwatch-api-dashboard-widgets.ts +112 -0
- package/src/langwatch-api-dashboards.ts +13 -10
- package/src/langwatch-api-datasets.ts +98 -32
- package/src/langwatch-api-evaluators.ts +13 -19
- package/src/langwatch-api-model-providers.ts +4 -4
- package/src/langwatch-api-monitors.ts +14 -18
- package/src/langwatch-api-projects.ts +27 -9
- package/src/langwatch-api-query.ts +114 -0
- package/src/langwatch-api-run-plans.ts +12 -29
- package/src/langwatch-api-scenarios.ts +9 -11
- package/src/langwatch-api-secrets.ts +31 -24
- package/src/langwatch-api-simulation-runs.ts +6 -8
- package/src/langwatch-api-test-suites.ts +14 -33
- package/src/langwatch-api-triggers.ts +96 -34
- package/src/langwatch-api-workflows.ts +9 -9
- package/src/langwatch-api.ts +108 -124
- package/src/public-http-request.ts +21 -12
- package/src/schemas/analytics-metrics.ts +3 -15
- package/src/schemas/create-dataset.ts +3 -4
- package/src/schemas/run-plan.ts +13 -29
- package/src/schemas/suite-fields.ts +6 -23
- package/src/schemas/triggers.ts +365 -0
- package/src/tools/add-dashboard-widget.ts +63 -0
- package/src/tools/archive-project.ts +2 -4
- package/src/tools/archive-run-plan.ts +2 -4
- package/src/tools/archive-scenario.ts +2 -4
- package/src/tools/archive-test-suite.ts +2 -4
- package/src/tools/assign-prompt-tag.ts +6 -2
- package/src/tools/create-agent.ts +1 -1
- package/src/tools/create-annotation.ts +7 -2
- package/src/tools/create-api-key.ts +4 -6
- package/src/tools/create-dashboard.ts +1 -1
- package/src/tools/create-dataset-records.ts +4 -8
- package/src/tools/create-dataset.ts +5 -7
- package/src/tools/create-evaluator.ts +3 -5
- package/src/tools/create-project.ts +2 -2
- package/src/tools/create-prompt-tag.ts +2 -4
- package/src/tools/create-prompt.ts +9 -18
- package/src/tools/create-scenario.ts +5 -7
- package/src/tools/create-test-suite.ts +4 -9
- package/src/tools/create-trigger.ts +55 -0
- package/src/tools/dashboard-exists.ts +25 -0
- package/src/tools/dashboard-widget-queries.ts +32 -0
- package/src/tools/delete-agent.ts +1 -1
- package/src/tools/delete-annotation.ts +1 -1
- package/src/tools/delete-dashboard.ts +1 -1
- package/src/tools/delete-dataset-records.ts +4 -8
- package/src/tools/delete-dataset.ts +3 -7
- package/src/tools/delete-evaluator.ts +4 -6
- package/src/tools/delete-prompt-tag.ts +2 -4
- package/src/tools/delete-workflow.ts +1 -1
- package/src/tools/discover-evaluator-schema.ts +34 -25
- package/src/tools/discover-scenario-schema.ts +41 -21
- package/src/tools/discover-schema.ts +149 -27
- package/src/tools/experiment-run-status.ts +2 -8
- package/src/tools/format-dataset-mutation.ts +5 -10
- package/src/tools/format-run-plan.ts +7 -21
- package/src/tools/format-scenario.ts +2 -4
- package/src/tools/format-suite-details.ts +6 -12
- package/src/tools/get-agent.ts +15 -6
- package/src/tools/get-analytics.ts +52 -46
- package/src/tools/get-dashboard.ts +1 -1
- package/src/tools/get-dataset.ts +34 -16
- package/src/tools/get-evaluator.ts +4 -11
- package/src/tools/get-experiment-results.ts +219 -186
- package/src/tools/get-project.ts +2 -4
- package/src/tools/get-prompt.ts +63 -84
- package/src/tools/get-run-plan.ts +4 -6
- package/src/tools/get-scenario.ts +4 -6
- package/src/tools/get-simulation-run.ts +27 -24
- package/src/tools/get-test-suite.ts +2 -5
- package/src/tools/get-trace.ts +16 -17
- package/src/tools/get-workflow.ts +1 -1
- package/src/tools/list-agents.ts +1 -1
- package/src/tools/list-annotations.ts +8 -5
- package/src/tools/list-api-keys.ts +7 -2
- package/src/tools/list-dashboard-boards.ts +28 -0
- package/src/tools/list-dashboards.ts +1 -1
- package/src/tools/list-dataset-records.ts +8 -6
- package/src/tools/list-datasets.ts +12 -23
- package/src/tools/list-evaluators.ts +4 -8
- package/src/tools/list-experiment-runs.ts +5 -9
- package/src/tools/list-experiments.ts +5 -11
- package/src/tools/list-model-providers.ts +3 -5
- package/src/tools/list-projects.ts +2 -4
- package/src/tools/list-prompt-tags.ts +3 -3
- package/src/tools/list-prompts.ts +4 -8
- package/src/tools/list-run-plans.ts +2 -2
- package/src/tools/list-scenarios.ts +14 -15
- package/src/tools/list-simulation-runs.ts +8 -4
- package/src/tools/list-test-suites.ts +1 -1
- package/src/tools/list-trigger-fires.ts +17 -0
- package/src/tools/list-triggers.ts +39 -0
- package/src/tools/list-workflows.ts +2 -4
- package/src/tools/rename-prompt-tag.ts +1 -1
- package/src/tools/rename-test-suite.ts +2 -5
- package/src/tools/report-issue.ts +5 -9
- package/src/tools/rerun-run-plan.ts +2 -5
- package/src/tools/revoke-api-key.ts +2 -4
- package/src/tools/run-agent.ts +29 -26
- package/src/tools/run-experiment.ts +36 -37
- package/src/tools/run-plan.ts +4 -7
- package/src/tools/run-query.ts +47 -0
- package/src/tools/run-test-suite.ts +4 -7
- package/src/tools/run-workflow.ts +2 -5
- package/src/tools/search-traces.ts +161 -47
- package/src/tools/set-model-provider.ts +4 -6
- package/src/tools/test-agent.ts +2 -2
- package/src/tools/update-agent.ts +1 -1
- package/src/tools/update-dashboard-widget.ts +97 -0
- package/src/tools/update-dataset-record.ts +1 -1
- package/src/tools/update-dataset.ts +5 -7
- package/src/tools/update-evaluator.ts +3 -5
- package/src/tools/update-project.ts +1 -1
- package/src/tools/update-prompt.ts +44 -59
- package/src/tools/update-scenario.ts +5 -7
- package/src/tools/update-test-suite.ts +6 -11
- package/src/utils/date-parsing.ts +1 -1
- package/src/utils/escape-markdown.ts +3 -8
- package/src/utils/format-evaluations.ts +6 -6
- package/src/utils/markdown-table.ts +58 -0
- package/src/utils/trace-id-shape.ts +20 -0
- package/tsconfig.app.json +24 -0
- package/tsconfig.json +14 -12
- package/vitest.integration.config.js +10 -0
- package/vitest.scenario.config.js +9 -0
- package/.eslintrc.cjs +0 -36
- package/dist/chunk-AN4OUA4D.js +0 -86
- package/dist/chunk-GELJ2MQR.js +0 -226
- package/dist/chunk-K37ZNPXK.js +0 -24
- package/dist/config.d.ts +0 -17
- package/dist/create-mcp-server.d.ts +0 -10
- package/dist/delete-evaluator-RKYC4TNB.js +0 -16
- package/dist/discover-evaluator-schema-ESSHQGPY.js +0 -1912
- package/dist/get-analytics-R6AZD3AW.js +0 -73
- package/dist/get-dataset-AKTYSQDI.js +0 -49
- package/dist/index.d.ts +0 -2
- package/dist/langwatch-api-secrets-OEBD6GBU.js +0 -41
- package/dist/langwatch-api-triggers-5E6DDGX6.js +0 -31
- package/dist/run-agent-CAGWGTYT.js +0 -72
- package/dist/search-traces-4XTJYXIK.js +0 -79
- package/src/__tests__/agent-tools.unit.test.ts +0 -257
- package/src/__tests__/all-tools.integration.test.ts +0 -1872
- package/src/__tests__/config.unit.test.ts +0 -202
- package/src/__tests__/datasets.integration.test.ts +0 -620
- package/src/__tests__/datasets.unit.test.ts +0 -244
- package/src/__tests__/date-parsing.unit.test.ts +0 -78
- package/src/__tests__/discover-evaluator-schema.unit.test.ts +0 -89
- package/src/__tests__/discover-schema.unit.test.ts +0 -118
- package/src/__tests__/docker.integration.test.ts +0 -318
- package/src/__tests__/documentation-fetch-security.integration.test.ts +0 -182
- package/src/__tests__/evaluator-tools.unit.test.ts +0 -262
- package/src/__tests__/experiment-list-tools.unit.test.ts +0 -157
- package/src/__tests__/experiment-status.unit.test.ts +0 -194
- package/src/__tests__/feature-map-tool-drift.unit.test.ts +0 -93
- package/src/__tests__/get-experiment-results.unit.test.ts +0 -313
- package/src/__tests__/http-security-origin.unit.test.ts +0 -97
- package/src/__tests__/http-security-sessions.unit.test.ts +0 -209
- package/src/__tests__/http-security-verifier.unit.test.ts +0 -248
- package/src/__tests__/http-server-allocation.integration.test.ts +0 -279
- package/src/__tests__/http-server-origin.integration.test.ts +0 -169
- package/src/__tests__/http-server-session-auth.integration.test.ts +0 -341
- package/src/__tests__/http-transport.integration.test.ts +0 -395
- package/src/__tests__/integration.integration.test.ts +0 -284
- package/src/__tests__/langwatch-api.unit.test.ts +0 -661
- package/src/__tests__/model-provider-tools.unit.test.ts +0 -190
- package/src/__tests__/project-api-key-tools.unit.test.ts +0 -451
- package/src/__tests__/prompt-response-types.unit.test.ts +0 -51
- package/src/__tests__/prompts.integration.test.ts +0 -736
- package/src/__tests__/public-http-request-security.integration.test.ts +0 -118
- package/src/__tests__/report-issue.integration.test.ts +0 -147
- package/src/__tests__/run-plan-tools.unit.test.ts +0 -565
- package/src/__tests__/scenario-tools.integration.test.ts +0 -286
- package/src/__tests__/scenario-tools.unit.test.ts +0 -207
- package/src/__tests__/schemas.unit.test.ts +0 -85
- package/src/__tests__/simulation-run-tools.unit.test.ts +0 -116
- package/src/__tests__/support/http-server-harness.ts +0 -138
- package/src/__tests__/test-suite-tools.unit.test.ts +0 -615
- package/src/__tests__/tools.integration.test.ts +0 -896
- package/src/schemas/filter-fields.ts +0 -119
- package/src/schemas/index.ts +0 -9
|
@@ -0,0 +1,898 @@
|
|
|
1
|
+
import {
|
|
2
|
+
getConfig
|
|
3
|
+
} from "./chunk-H6WGMG2T.js";
|
|
4
|
+
|
|
5
|
+
// ../../packages/redaction/src/piiEntities.ts
|
|
6
|
+
var ESSENTIAL_PII_ENTITIES = [
|
|
7
|
+
"EMAIL_ADDRESS",
|
|
8
|
+
"IP_ADDRESS",
|
|
9
|
+
"CREDIT_CARD",
|
|
10
|
+
"IBAN_CODE",
|
|
11
|
+
"CRYPTO",
|
|
12
|
+
"PHONE_NUMBER",
|
|
13
|
+
"US_SSN",
|
|
14
|
+
"US_ITIN",
|
|
15
|
+
"US_PASSPORT",
|
|
16
|
+
"US_BANK_NUMBER",
|
|
17
|
+
"US_DRIVER_LICENSE",
|
|
18
|
+
"MEDICAL_LICENSE",
|
|
19
|
+
"UK_NHS",
|
|
20
|
+
"SG_NRIC_FIN",
|
|
21
|
+
"AU_ABN",
|
|
22
|
+
"AU_TFN",
|
|
23
|
+
"IN_PAN",
|
|
24
|
+
"IN_AADHAAR",
|
|
25
|
+
"BR_CPF"
|
|
26
|
+
];
|
|
27
|
+
var PRESIDIO_STRICT_ENTITIES = [
|
|
28
|
+
"CREDIT_CARD",
|
|
29
|
+
"CRYPTO",
|
|
30
|
+
"EMAIL_ADDRESS",
|
|
31
|
+
"IBAN_CODE",
|
|
32
|
+
"IP_ADDRESS",
|
|
33
|
+
"LOCATION",
|
|
34
|
+
"PERSON",
|
|
35
|
+
"PHONE_NUMBER",
|
|
36
|
+
"MEDICAL_LICENSE",
|
|
37
|
+
"US_BANK_NUMBER",
|
|
38
|
+
"US_DRIVER_LICENSE",
|
|
39
|
+
"US_ITIN",
|
|
40
|
+
"US_PASSPORT",
|
|
41
|
+
"US_SSN",
|
|
42
|
+
"UK_NHS",
|
|
43
|
+
"SG_NRIC_FIN",
|
|
44
|
+
"AU_ABN",
|
|
45
|
+
"AU_ACN",
|
|
46
|
+
"AU_TFN",
|
|
47
|
+
"AU_MEDICARE",
|
|
48
|
+
"IN_PAN",
|
|
49
|
+
"IN_AADHAAR",
|
|
50
|
+
"IN_VEHICLE_REGISTRATION",
|
|
51
|
+
"IN_VOTER",
|
|
52
|
+
"IN_PASSPORT"
|
|
53
|
+
];
|
|
54
|
+
var STRICT_ONLY_PII_ENTITIES = PRESIDIO_STRICT_ENTITIES.filter(
|
|
55
|
+
(entity) => !new Set(ESSENTIAL_PII_ENTITIES).has(entity)
|
|
56
|
+
);
|
|
57
|
+
|
|
58
|
+
// ../../packages/redaction/src/markers.ts
|
|
59
|
+
var SECRET_MARKER = "[SECRET]";
|
|
60
|
+
function formatPiiMarker(entity) {
|
|
61
|
+
return `[${entity}]`;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// ../../packages/redaction/src/secrets.ts
|
|
65
|
+
var SECRETS_REDACTION_MARKER = SECRET_MARKER;
|
|
66
|
+
var REPLACEMENT = SECRETS_REDACTION_MARKER;
|
|
67
|
+
var MAX_SCAN_LENGTH = 25e4;
|
|
68
|
+
var ENTROPY_SAMPLE_LENGTH = 256;
|
|
69
|
+
function shannonEntropyBits(value) {
|
|
70
|
+
const sample = value.length > ENTROPY_SAMPLE_LENGTH ? value.slice(0, ENTROPY_SAMPLE_LENGTH) : value;
|
|
71
|
+
const counts = /* @__PURE__ */ new Map();
|
|
72
|
+
for (const char of sample) {
|
|
73
|
+
counts.set(char, (counts.get(char) ?? 0) + 1);
|
|
74
|
+
}
|
|
75
|
+
let entropy = 0;
|
|
76
|
+
for (const count of counts.values()) {
|
|
77
|
+
const probability = count / sample.length;
|
|
78
|
+
entropy -= probability * Math.log2(probability);
|
|
79
|
+
}
|
|
80
|
+
return entropy;
|
|
81
|
+
}
|
|
82
|
+
function countCharClasses(value) {
|
|
83
|
+
let lower = 0;
|
|
84
|
+
let upper = 0;
|
|
85
|
+
let digit = 0;
|
|
86
|
+
for (let index = 0; index < value.length; index++) {
|
|
87
|
+
const code = value.charCodeAt(index);
|
|
88
|
+
if (code >= 97 && code <= 122) lower++;
|
|
89
|
+
else if (code >= 65 && code <= 90) upper++;
|
|
90
|
+
else if (code >= 48 && code <= 57) digit++;
|
|
91
|
+
}
|
|
92
|
+
return { lower, upper, digit };
|
|
93
|
+
}
|
|
94
|
+
var TOKEN_START = String.raw`(?<![A-Za-z0-9_-])`;
|
|
95
|
+
var TOKEN_END = String.raw`(?![A-Za-z0-9_-])`;
|
|
96
|
+
var VENDOR_KEY_PATTERNS = [
|
|
97
|
+
// LangWatch's own token prefixes, minted by modules/api-key/contract/src/api-key.tokens.ts.
|
|
98
|
+
// The 3-body-char floor keeps a bare prefix (printed alone in docs/errors) from matching as a
|
|
99
|
+
// key. Duplicated here rather than imported so this package stays dependency-free; a test pins
|
|
100
|
+
// them to the constants so the two cannot drift.
|
|
101
|
+
String.raw`(?:sk|ik|pat|vk)-lw-[A-Za-z0-9_-]{3,}`,
|
|
102
|
+
// GitLab personal, project, deploy, runner and agent tokens.
|
|
103
|
+
String.raw`gl(?:pat|rt|dt|soat|ptt|cbt|imt|agent|ffct)-[A-Za-z0-9_-]{20,}`,
|
|
104
|
+
String.raw`npm_[A-Za-z0-9]{36}`,
|
|
105
|
+
// Google OAuth client secret.
|
|
106
|
+
String.raw`GOCSPX-[A-Za-z0-9_-]{20,}`,
|
|
107
|
+
String.raw`mb_[A-Za-z0-9+/=]{40,}`,
|
|
108
|
+
String.raw`dckr_pat_[A-Za-z0-9_-]{20,}`,
|
|
109
|
+
// Shopify admin, storefront, custom and private app tokens.
|
|
110
|
+
String.raw`shp(?:at|ss|ca|pa)_[0-9a-fA-F]{32}`,
|
|
111
|
+
String.raw`SG\.[A-Za-z0-9_-]{16,}\.[A-Za-z0-9_-]{16,}`,
|
|
112
|
+
String.raw`hf_[A-Za-z0-9]{30,}`,
|
|
113
|
+
String.raw`gsk_[A-Za-z0-9]{40,}`,
|
|
114
|
+
String.raw`pplx-[A-Za-z0-9]{30,}`,
|
|
115
|
+
String.raw`nvapi-[A-Za-z0-9_-]{40,}`,
|
|
116
|
+
String.raw`r8_[A-Za-z0-9]{30,}`,
|
|
117
|
+
String.raw`xai-[A-Za-z0-9]{40,}`,
|
|
118
|
+
// Notion integration tokens, current and legacy.
|
|
119
|
+
String.raw`ntn_[A-Za-z0-9]{30,}`,
|
|
120
|
+
String.raw`secret_[A-Za-z0-9]{40,}`,
|
|
121
|
+
String.raw`dop_v1_[0-9a-f]{64}`,
|
|
122
|
+
String.raw`figd_[A-Za-z0-9_-]{30,}`,
|
|
123
|
+
String.raw`ATATT[A-Za-z0-9_=-]{100,}`,
|
|
124
|
+
String.raw`sq0(?:atp|csp)-[A-Za-z0-9_-]{20,}`,
|
|
125
|
+
String.raw`EAAG[A-Za-z0-9]{60,}`,
|
|
126
|
+
String.raw`key-[0-9a-f]{32}`,
|
|
127
|
+
String.raw`re_[A-Za-z0-9_-]{20,}`,
|
|
128
|
+
String.raw`phx_[A-Za-z0-9]{30,}`,
|
|
129
|
+
String.raw`lin_api_[A-Za-z0-9]{30,}`,
|
|
130
|
+
String.raw`sl\.[A-Za-z0-9_-]{60,}`,
|
|
131
|
+
String.raw`ya29\.[A-Za-z0-9_-]{20,}`,
|
|
132
|
+
String.raw`sbp_[0-9a-f]{40,}`,
|
|
133
|
+
String.raw`sntry(?:s|u)_[A-Za-z0-9_.-]{30,}`,
|
|
134
|
+
String.raw`fw_[A-Za-z0-9]{20,}`,
|
|
135
|
+
String.raw`gl(?:c|sa)_[A-Za-z0-9]{30,}`,
|
|
136
|
+
String.raw`NRAK-[A-Za-z0-9]{20,}`,
|
|
137
|
+
String.raw`PMAK-[A-Za-z0-9]{20,}-[A-Za-z0-9]{20,}`,
|
|
138
|
+
String.raw`dp\.(?:pt|st|ct|sa)\.[A-Za-z0-9_-]{20,}`,
|
|
139
|
+
// Airtable personal access token: patXXXXXXXXXXXXXX.<64 hex>.
|
|
140
|
+
String.raw`pat[A-Za-z0-9]{14}\.[0-9a-f]{64}`,
|
|
141
|
+
// Telegram bot token: <numeric bot id>:AA<35-char body>.
|
|
142
|
+
String.raw`[0-9]{8,10}:AA[A-Za-z0-9_-]{33}`
|
|
143
|
+
];
|
|
144
|
+
var VENDOR_KEY_REGEX = new RegExp(
|
|
145
|
+
`${TOKEN_START}(?:${VENDOR_KEY_PATTERNS.join("|")})${TOKEN_END}`,
|
|
146
|
+
"g"
|
|
147
|
+
);
|
|
148
|
+
var SHAPED_TOKEN_MIN_BODY = 26;
|
|
149
|
+
var SHAPED_TOKEN_MAX_BODY = 120;
|
|
150
|
+
var SHAPED_TOKEN_MIN_ENTROPY = 3.9;
|
|
151
|
+
function isKeyShapedBody(body) {
|
|
152
|
+
if (body.length < SHAPED_TOKEN_MIN_BODY || body.length > SHAPED_TOKEN_MAX_BODY) {
|
|
153
|
+
return false;
|
|
154
|
+
}
|
|
155
|
+
const { lower, upper, digit } = countCharClasses(body);
|
|
156
|
+
if (lower < 2 || upper < 2 || digit < 2) return false;
|
|
157
|
+
return shannonEntropyBits(body) >= SHAPED_TOKEN_MIN_ENTROPY;
|
|
158
|
+
}
|
|
159
|
+
var DIGEST_PREFIXES = /* @__PURE__ */ new Set([
|
|
160
|
+
"sha1",
|
|
161
|
+
"sha224",
|
|
162
|
+
"sha256",
|
|
163
|
+
"sha384",
|
|
164
|
+
"sha512",
|
|
165
|
+
"sha3",
|
|
166
|
+
"md4",
|
|
167
|
+
"md5",
|
|
168
|
+
"blake2b",
|
|
169
|
+
"blake2s",
|
|
170
|
+
"blake3",
|
|
171
|
+
"crc32",
|
|
172
|
+
"xxh3",
|
|
173
|
+
"xxh64",
|
|
174
|
+
"base32",
|
|
175
|
+
"base58",
|
|
176
|
+
"base64",
|
|
177
|
+
"hex",
|
|
178
|
+
"uuid",
|
|
179
|
+
"urn",
|
|
180
|
+
"cid",
|
|
181
|
+
"etag",
|
|
182
|
+
"hash",
|
|
183
|
+
"digest",
|
|
184
|
+
"checksum",
|
|
185
|
+
"integrity"
|
|
186
|
+
]);
|
|
187
|
+
var HEX_BODY_CREDENTIAL_SEGMENTS = [
|
|
188
|
+
"live",
|
|
189
|
+
"test",
|
|
190
|
+
"prod",
|
|
191
|
+
"sk",
|
|
192
|
+
"pk",
|
|
193
|
+
"key",
|
|
194
|
+
"secret",
|
|
195
|
+
"token"
|
|
196
|
+
];
|
|
197
|
+
var IDENTIFIER_PREFIXES = /* @__PURE__ */ new Set([
|
|
198
|
+
"commit",
|
|
199
|
+
"sha",
|
|
200
|
+
"sha1",
|
|
201
|
+
"sha256",
|
|
202
|
+
"md5",
|
|
203
|
+
"hash",
|
|
204
|
+
"digest",
|
|
205
|
+
"trace",
|
|
206
|
+
"span",
|
|
207
|
+
"id",
|
|
208
|
+
"uuid",
|
|
209
|
+
"rev",
|
|
210
|
+
"blob",
|
|
211
|
+
"tree",
|
|
212
|
+
"etag",
|
|
213
|
+
"checksum"
|
|
214
|
+
]);
|
|
215
|
+
var RECORD_ID_PREFIXES = /* @__PURE__ */ new Set([
|
|
216
|
+
"project",
|
|
217
|
+
"provider",
|
|
218
|
+
"card",
|
|
219
|
+
"eval",
|
|
220
|
+
"monitor",
|
|
221
|
+
"scenario",
|
|
222
|
+
"ses",
|
|
223
|
+
"sess",
|
|
224
|
+
"session",
|
|
225
|
+
"thread",
|
|
226
|
+
"conv",
|
|
227
|
+
"langyconv",
|
|
228
|
+
"span",
|
|
229
|
+
"trace",
|
|
230
|
+
"run",
|
|
231
|
+
"msg",
|
|
232
|
+
"task",
|
|
233
|
+
"job",
|
|
234
|
+
"step",
|
|
235
|
+
"node",
|
|
236
|
+
"team",
|
|
237
|
+
"org",
|
|
238
|
+
"user",
|
|
239
|
+
"call",
|
|
240
|
+
"req",
|
|
241
|
+
"resp",
|
|
242
|
+
"chatcmpl",
|
|
243
|
+
"toolu",
|
|
244
|
+
"asst",
|
|
245
|
+
"file",
|
|
246
|
+
"batch",
|
|
247
|
+
"evt",
|
|
248
|
+
"acct",
|
|
249
|
+
"cus",
|
|
250
|
+
"sub"
|
|
251
|
+
]);
|
|
252
|
+
var PUBLIC_KEY_PREFIXES = /* @__PURE__ */ new Set(["phc"]);
|
|
253
|
+
function isNonCredentialPrefix(prefix) {
|
|
254
|
+
const lower = prefix.toLowerCase();
|
|
255
|
+
return DIGEST_PREFIXES.has(lower) || IDENTIFIER_PREFIXES.has(lower) || RECORD_ID_PREFIXES.has(lower) || PUBLIC_KEY_PREFIXES.has(lower);
|
|
256
|
+
}
|
|
257
|
+
var HEX_BODY_MIN = 24;
|
|
258
|
+
var HEX_BODY_MAX = 128;
|
|
259
|
+
var PLACEHOLDER_VALUE_REGEX = /^(?:x+|\*+|\.+|-+|_+|0+|(?:your|my|our|insert|replace|example|sample|dummy|fake|placeholder|changeme|redacted|removed|hidden|none|null|nil|undefined|todo|tbd|fixme)[a-z0-9_\- ]*)$/i;
|
|
260
|
+
var ENV_REFERENCE_REGEX = /^(?:\$[A-Za-z_][A-Za-z0-9_]*|[A-Z][A-Z0-9]*(?:_[A-Z0-9]+)+)$/;
|
|
261
|
+
var CODE_EXPRESSION_REGEX = /^[A-Za-z_$][\w$]*(?:\.[A-Za-z_$][\w$]*)+$/;
|
|
262
|
+
var PATH_LIKE_REGEX = /^[~.]{0,2}\/|\/[^/\s]*\.[a-z]{1,5}$/;
|
|
263
|
+
var CONTEXT_VALUE_MIN_LENGTH = 16;
|
|
264
|
+
var CONTEXT_VALUE_MIN_ENTROPY = 2.9;
|
|
265
|
+
function isCredentialValue(value) {
|
|
266
|
+
if (value.length < CONTEXT_VALUE_MIN_LENGTH) return false;
|
|
267
|
+
if (PLACEHOLDER_VALUE_REGEX.test(value)) return false;
|
|
268
|
+
if (ENV_REFERENCE_REGEX.test(value)) return false;
|
|
269
|
+
if (CODE_EXPRESSION_REGEX.test(value)) return false;
|
|
270
|
+
if (PATH_LIKE_REGEX.test(value)) return false;
|
|
271
|
+
if (/^[a-z][a-z0-9+.-]*:\/\//i.test(value)) return false;
|
|
272
|
+
if (value.includes(SECRETS_REDACTION_MARKER)) return false;
|
|
273
|
+
return shannonEntropyBits(value) >= CONTEXT_VALUE_MIN_ENTROPY;
|
|
274
|
+
}
|
|
275
|
+
var VERSION_STRING_REGEX = /^v?\d+(?:[._-]\d+)+/;
|
|
276
|
+
var LOOSE_VALUE_MIN_LENGTH = 20;
|
|
277
|
+
var LOOSE_VALUE_MIN_ENTROPY = 3.4;
|
|
278
|
+
function isKeyMaterial(value) {
|
|
279
|
+
if (value.length < LOOSE_VALUE_MIN_LENGTH) return false;
|
|
280
|
+
if (!isCredentialValue(value)) return false;
|
|
281
|
+
if (VERSION_STRING_REGEX.test(value)) return false;
|
|
282
|
+
if (shannonEntropyBits(value) < LOOSE_VALUE_MIN_ENTROPY) return false;
|
|
283
|
+
if (/^[0-9a-f]{32,}$/i.test(value)) return true;
|
|
284
|
+
if (/^[A-Z2-7]{32,}={0,6}$/.test(value)) return true;
|
|
285
|
+
const { lower, upper, digit } = countCharClasses(value);
|
|
286
|
+
return digit >= 2 && (lower >= 2 || upper >= 2);
|
|
287
|
+
}
|
|
288
|
+
var CREDENTIAL_QUALIFIERS = /* @__PURE__ */ new Set([
|
|
289
|
+
"master",
|
|
290
|
+
"encryption",
|
|
291
|
+
"signing",
|
|
292
|
+
"private",
|
|
293
|
+
"access",
|
|
294
|
+
"api",
|
|
295
|
+
"auth",
|
|
296
|
+
"secret",
|
|
297
|
+
"refresh",
|
|
298
|
+
"session",
|
|
299
|
+
"bearer",
|
|
300
|
+
"verification",
|
|
301
|
+
"webhook",
|
|
302
|
+
"client",
|
|
303
|
+
"service",
|
|
304
|
+
"personal",
|
|
305
|
+
"root",
|
|
306
|
+
"admin"
|
|
307
|
+
]);
|
|
308
|
+
var QUALIFIER_ALTERNATION = [...CREDENTIAL_QUALIFIERS].join("|");
|
|
309
|
+
var CREDENTIAL_KEYWORD = String.raw`(?:x[_.\- ]?)?(?:` + String.raw`(?:${QUALIFIER_ALTERNATION})[_.\- ]?(?:api[_.\- ]?)?key` + String.raw`|(?:${QUALIFIER_ALTERNATION})?[_.\- ]?(?:api[_.\- ]?)?` + String.raw`(?:token|secret|password|passwd|pwd|credentials?|authorization|cookie)` + String.raw`)`;
|
|
310
|
+
function isBasicAuthPayload(value) {
|
|
311
|
+
if (value.endsWith("=")) return true;
|
|
312
|
+
const { lower, upper, digit } = countCharClasses(value);
|
|
313
|
+
return digit > 0 || lower > 0 && upper > 0;
|
|
314
|
+
}
|
|
315
|
+
var VALUE_RULES = [
|
|
316
|
+
{
|
|
317
|
+
id: "pem_private_key",
|
|
318
|
+
description: "PEM private key block",
|
|
319
|
+
regex: /-----BEGIN (?:RSA |EC |DSA |OPENSSH |PGP |ENCRYPTED )?PRIVATE KEY(?: BLOCK)?-----[\s\S]*?-----END (?:RSA |EC |DSA |OPENSSH |PGP |ENCRYPTED )?PRIVATE KEY(?: BLOCK)?-----/g
|
|
320
|
+
},
|
|
321
|
+
{
|
|
322
|
+
// PuTTY's own private-key format, which carries no PEM armour. The body
|
|
323
|
+
// runs to the end of the file, so it is clamped at the next blank line
|
|
324
|
+
// rather than allowed to swallow the rest of the payload.
|
|
325
|
+
id: "putty_private_key",
|
|
326
|
+
description: "PuTTY private key file",
|
|
327
|
+
regex: /PuTTY-User-Key-File-\d+:[\s\S]*?(?:\n\s*\n|$)/g
|
|
328
|
+
},
|
|
329
|
+
{
|
|
330
|
+
// A kubeconfig embeds the client certificate and its key as base64. The
|
|
331
|
+
// key is the credential; the certificate is redacted with it because
|
|
332
|
+
// together they are a working login.
|
|
333
|
+
id: "kubeconfig_client_credentials",
|
|
334
|
+
description: "Embedded kubeconfig client key or certificate",
|
|
335
|
+
regex: /\b(client-(?:key|certificate)-data:\s*)[A-Za-z0-9+/=]{40,}/g,
|
|
336
|
+
render: (_m, prefix) => `${prefix}${REPLACEMENT}`
|
|
337
|
+
},
|
|
338
|
+
{
|
|
339
|
+
id: "aws_access_key_id",
|
|
340
|
+
description: "AWS access key id",
|
|
341
|
+
regex: /\b(?:AKIA|ASIA|AGPA|AIDA|AROA|ANPA|ANVA)[0-9A-Z]{16}\b/g
|
|
342
|
+
},
|
|
343
|
+
{
|
|
344
|
+
id: "github_token",
|
|
345
|
+
description: "GitHub token",
|
|
346
|
+
regex: /\b(?:gh[posru]_[A-Za-z0-9]{36,}|github_pat_[A-Za-z0-9_]{60,})\b/g
|
|
347
|
+
},
|
|
348
|
+
{
|
|
349
|
+
// Provider secret keys share the `sk-` namespace: OpenAI (`sk-proj-...`,
|
|
350
|
+
// legacy `sk-...`), Anthropic (`sk-ant-...`), LangWatch (`sk-lw-...`), and
|
|
351
|
+
// others. The body is base64url, so it includes `_` and `-` and has no inner
|
|
352
|
+
// word boundary; matching the whole token and stopping at the next non-key
|
|
353
|
+
// char catches modern keys a `[A-Za-z0-9]+\b` rule misses.
|
|
354
|
+
id: "provider_api_key",
|
|
355
|
+
description: "Provider API key (sk-...)",
|
|
356
|
+
regex: /\bsk-[A-Za-z0-9_-]{20,}(?![A-Za-z0-9_-])/g
|
|
357
|
+
},
|
|
358
|
+
{
|
|
359
|
+
id: "stripe_secret_key",
|
|
360
|
+
description: "Stripe secret key",
|
|
361
|
+
regex: /\b[rs]k_(?:live|test)_[A-Za-z0-9]{16,}\b/g
|
|
362
|
+
},
|
|
363
|
+
{
|
|
364
|
+
id: "slack_token",
|
|
365
|
+
description: "Slack token",
|
|
366
|
+
regex: /\bxox[abposr]-[A-Za-z0-9-]{10,}\b/g
|
|
367
|
+
},
|
|
368
|
+
{
|
|
369
|
+
id: "google_api_key",
|
|
370
|
+
description: "Google API key",
|
|
371
|
+
regex: /\bAIza[0-9A-Za-z_-]{35}\b/g
|
|
372
|
+
},
|
|
373
|
+
{
|
|
374
|
+
id: "vendor_api_key",
|
|
375
|
+
description: "Developer-service API key (GitLab, npm, Docker Hub, Shopify, SendGrid, Hugging Face, Groq, Notion, Atlassian and others)",
|
|
376
|
+
regex: VENDOR_KEY_REGEX
|
|
377
|
+
},
|
|
378
|
+
{
|
|
379
|
+
id: "jwt",
|
|
380
|
+
description: "JSON Web Token",
|
|
381
|
+
regex: /\beyJ[A-Za-z0-9_-]{8,}\.[A-Za-z0-9_-]{8,}\.[A-Za-z0-9_-]{8,}\b/g
|
|
382
|
+
},
|
|
383
|
+
{
|
|
384
|
+
id: "url_credentials",
|
|
385
|
+
description: "Password embedded in a connection URL",
|
|
386
|
+
// scheme://user:password@host -> keep everything but the password. The scheme sits in a
|
|
387
|
+
// lookbehind so the engine anchors on the literal `:` instead of scanning from every
|
|
388
|
+
// lowercase letter: unanchored this cost 6.2ms on a 200KB payload (more than every other
|
|
389
|
+
// rule combined); anchored, 0.09ms. The scheme stays outside the match and untouched.
|
|
390
|
+
regex: /(?<=[a-z][a-z0-9+.-]{0,30})(:\/\/[^\s:@/]+:)([^\s:@/]+)(@)/gi,
|
|
391
|
+
// The same scheme the lookbehind reads, so a reported match still spans the
|
|
392
|
+
// whole URL rather than starting at the colon.
|
|
393
|
+
precededBy: /[a-z][a-z0-9+.-]{0,30}$/i,
|
|
394
|
+
render: (...[, prefix = "", , at = ""]) => `${prefix}${REPLACEMENT}${at}`
|
|
395
|
+
},
|
|
396
|
+
{
|
|
397
|
+
id: "bearer_token",
|
|
398
|
+
description: "Bearer authorization token",
|
|
399
|
+
regex: /\b(Bearer\s+)[A-Za-z0-9._~+/-]{10,}=*/gi,
|
|
400
|
+
render: (_m, prefix) => `${prefix}${REPLACEMENT}`
|
|
401
|
+
},
|
|
402
|
+
{
|
|
403
|
+
// The other Authorization schemes. Unlike `Bearer` these are ordinary
|
|
404
|
+
// words (`Token`, `OAuth`, `Splunk`), so they are only a credential cue
|
|
405
|
+
// inside an actual Authorization header: a bare "token acme_live_abcd1234"
|
|
406
|
+
// in prose is a sentence, not a header, and matching it redacted one.
|
|
407
|
+
id: "authorization_scheme_token",
|
|
408
|
+
description: "Non-Bearer authorization scheme token",
|
|
409
|
+
regex: /\b(Authorization:\s*(?:Token|SSWS|GenieKey|Splunk|OAuth)\s+)[A-Za-z0-9._~+/-]{10,}=*/gi,
|
|
410
|
+
render: (_m, prefix) => `${prefix}${REPLACEMENT}`
|
|
411
|
+
},
|
|
412
|
+
{
|
|
413
|
+
id: "basic_auth_credentials",
|
|
414
|
+
description: "Basic authorization credentials",
|
|
415
|
+
regex: /\b(Basic\s+)([A-Za-z0-9+/]{16,}={0,2})(?![A-Za-z0-9+/=])/g,
|
|
416
|
+
accept: (groups) => isBasicAuthPayload(groups[2] ?? ""),
|
|
417
|
+
render: (_m, prefix) => `${prefix}${REPLACEMENT}`
|
|
418
|
+
},
|
|
419
|
+
{
|
|
420
|
+
// The all-hex sibling of the shape rule below, which cannot accept a hex
|
|
421
|
+
// body without also accepting every digest and trace id in the transcript.
|
|
422
|
+
// A credential segment in the middle of the token is what makes the
|
|
423
|
+
// difference, so this rule requires one and refuses identifier prefixes on
|
|
424
|
+
// top of it.
|
|
425
|
+
id: "prefixed_hex_api_key",
|
|
426
|
+
description: "API key with a vendor prefix and an all-hex body",
|
|
427
|
+
regex: new RegExp(
|
|
428
|
+
`${TOKEN_START}([A-Za-z][A-Za-z0-9]{1,11})_(?:${HEX_BODY_CREDENTIAL_SEGMENTS.join("|")})_([0-9a-f]{${HEX_BODY_MIN},${HEX_BODY_MAX}})${TOKEN_END}`,
|
|
429
|
+
"gi"
|
|
430
|
+
),
|
|
431
|
+
accept: (groups) => !IDENTIFIER_PREFIXES.has((groups[1] ?? "").toLowerCase()),
|
|
432
|
+
precondition: (text) => text.includes("_")
|
|
433
|
+
},
|
|
434
|
+
{
|
|
435
|
+
// The layer that catches a vendor nobody has heard of: a short prefix, a separator and a
|
|
436
|
+
// high-entropy body, needing no vendor knowledge. The prefix may be upper, lower or mixed
|
|
437
|
+
// case since plenty of vendors mint `LW_…` or `Xy_…`; that also makes it the shape of a
|
|
438
|
+
// screaming-snake env var NAME, but the length floor and character-mix gate turn those away.
|
|
439
|
+
// A declined match consumes the text it spanned, so in principle an outer match could hide
|
|
440
|
+
// secret material further inside the same token. It cannot in practice: a body containing
|
|
441
|
+
// key material inherits that material's entropy and mix, so the outer match is accepted and
|
|
442
|
+
// the secret is redacted along with its prefix.
|
|
443
|
+
id: "shaped_api_key",
|
|
444
|
+
description: "High-entropy API key with a vendor-style prefix",
|
|
445
|
+
regex: new RegExp(
|
|
446
|
+
`${TOKEN_START}([A-Za-z][A-Za-z0-9]{1,11})[_-]([A-Za-z0-9_+/-]{${SHAPED_TOKEN_MIN_BODY},})${TOKEN_END}`,
|
|
447
|
+
"g"
|
|
448
|
+
),
|
|
449
|
+
accept: (groups) => !isNonCredentialPrefix(groups[1] ?? "") && isKeyShapedBody(groups[2] ?? ""),
|
|
450
|
+
precondition: (text) => text.includes("_") || text.includes("-")
|
|
451
|
+
},
|
|
452
|
+
{
|
|
453
|
+
// The layer that needs no shape at all: the text says what the value is.
|
|
454
|
+
// Up to two filler words are tolerated between the keyword and the
|
|
455
|
+
// separator, because people write "key now:" and "token here =" as often as
|
|
456
|
+
// they write "key:".
|
|
457
|
+
id: "sensitive_assignment",
|
|
458
|
+
description: "Value assigned to a credential-named field",
|
|
459
|
+
// `key is <value>` was rejected as a strict separator: on real traces it caught zero
|
|
460
|
+
// credentials while being the sole source of prose false-positives. Whitespace is accepted
|
|
461
|
+
// instead on a higher bar — the value must look like key material (see `isKeyMaterial`) —
|
|
462
|
+
// which is what keeps `Authorization <token>` and `key <token>` while rejecting prose.
|
|
463
|
+
// The value class excludes backslash: span content arrives JSON-encoded, so a literal `\n`
|
|
464
|
+
// is two characters, and letting a value run through one carried it across logical lines and
|
|
465
|
+
// past the `$VAR`/code-expression guards — `api_key = $OPENAI_API_KEY` was kept with a real
|
|
466
|
+
// newline and redacted with an escaped one.
|
|
467
|
+
regex: new RegExp(
|
|
468
|
+
`((?:^|[\\W_])(?:${CREDENTIAL_KEYWORD})(?:\\s+[A-Za-z]{1,8}){0,2}["'\`]?(?:\\s*[:=]{1,2}\\s*|[ \\t?-]+)["'\`]?)([^\\s"'\`,;<>(){}\\[\\]\\\\]{${CONTEXT_VALUE_MIN_LENGTH},})`,
|
|
469
|
+
"gi"
|
|
470
|
+
),
|
|
471
|
+
accept: (groups) => {
|
|
472
|
+
const introduction = groups[1] ?? "";
|
|
473
|
+
const value = groups[2] ?? "";
|
|
474
|
+
const strict = /[:=]\s*["'`]?$/.test(introduction);
|
|
475
|
+
return strict ? isCredentialValue(value) : isKeyMaterial(value);
|
|
476
|
+
},
|
|
477
|
+
render: (_m, introduction) => `${introduction}${REPLACEMENT}`
|
|
478
|
+
}
|
|
479
|
+
];
|
|
480
|
+
var BUILTIN_SECRET_RULES = VALUE_RULES.map(({ id, description }) => ({ id, description }));
|
|
481
|
+
var SENSITIVE_KEY_REGEX = /(?:^|[._-])(?:password|passwd|pwd|secret|api[_-]?key|apikey|access[_-]?token|auth[_-]?token|authorization|auth|bearer|credentials?|private[_-]?key|client[_-]?secret|db[_-]?password|connection[_-]?string|session[_-]?token|refresh[_-]?token|set[_-]?cookie|cookie|x-api-key)(?:$|[._-])/i;
|
|
482
|
+
var CREDENTIAL_NOUNS = /* @__PURE__ */ new Set([
|
|
483
|
+
"password",
|
|
484
|
+
"passwd",
|
|
485
|
+
"pwd",
|
|
486
|
+
"secret",
|
|
487
|
+
"authorization",
|
|
488
|
+
"auth",
|
|
489
|
+
"bearer",
|
|
490
|
+
"credential",
|
|
491
|
+
"credentials",
|
|
492
|
+
"cookie"
|
|
493
|
+
]);
|
|
494
|
+
function tokenizeAttributeName(name) {
|
|
495
|
+
return name.replace(/([a-z0-9])([A-Z])/g, "$1 $2").replace(/([A-Z]+)([A-Z][a-z])/g, "$1 $2").split(/[^A-Za-z0-9]+/).filter(Boolean).map((token) => token.toLowerCase());
|
|
496
|
+
}
|
|
497
|
+
function namesACredential(name) {
|
|
498
|
+
const tokens = tokenizeAttributeName(name);
|
|
499
|
+
return tokens.some((token, index) => {
|
|
500
|
+
if (CREDENTIAL_NOUNS.has(token)) return true;
|
|
501
|
+
if (token !== "key" && token !== "token") return false;
|
|
502
|
+
const previous = tokens[index - 1];
|
|
503
|
+
return previous !== void 0 && CREDENTIAL_QUALIFIERS.has(previous);
|
|
504
|
+
});
|
|
505
|
+
}
|
|
506
|
+
function isSensitiveAttributeKey(key) {
|
|
507
|
+
return SENSITIVE_KEY_REGEX.test(key) || namesACredential(key);
|
|
508
|
+
}
|
|
509
|
+
var VALUE_BOUNDARY = /["'`]/;
|
|
510
|
+
function keptLengthAtBoundary(match) {
|
|
511
|
+
const index = match.search(VALUE_BOUNDARY);
|
|
512
|
+
return index === -1 ? match.length : index;
|
|
513
|
+
}
|
|
514
|
+
var CUSTOM_VALUE_BOUNDARY = /[\s"'`<>]/;
|
|
515
|
+
function keptLengthForCustomPattern(match) {
|
|
516
|
+
const index = match.search(CUSTOM_VALUE_BOUNDARY);
|
|
517
|
+
return index === -1 ? match.length : index;
|
|
518
|
+
}
|
|
519
|
+
function replacementFor(rule, args) {
|
|
520
|
+
const full = args[0] ?? "";
|
|
521
|
+
if (rule.accept && !rule.accept(args)) return null;
|
|
522
|
+
if (rule.render) return rule.render(...args);
|
|
523
|
+
const kept = keptLengthAtBoundary(full);
|
|
524
|
+
if (kept === 0) return null;
|
|
525
|
+
return REPLACEMENT + full.slice(kept);
|
|
526
|
+
}
|
|
527
|
+
function sliceForScan(text) {
|
|
528
|
+
if (text.length <= MAX_SCAN_LENGTH) return [text];
|
|
529
|
+
const slices = [];
|
|
530
|
+
let start = 0;
|
|
531
|
+
while (start < text.length) {
|
|
532
|
+
const end = sliceEndAfter(text, start);
|
|
533
|
+
slices.push(text.slice(start, end));
|
|
534
|
+
start = end;
|
|
535
|
+
}
|
|
536
|
+
return slices;
|
|
537
|
+
}
|
|
538
|
+
function sliceEndAfter(text, start) {
|
|
539
|
+
const target = Math.min(start + MAX_SCAN_LENGTH, text.length);
|
|
540
|
+
if (target >= text.length) return text.length;
|
|
541
|
+
let end;
|
|
542
|
+
const lastSpace = text.slice(start, target).search(/\s(?=\S*$)/);
|
|
543
|
+
if (lastSpace > 0) {
|
|
544
|
+
end = start + lastSpace;
|
|
545
|
+
} else {
|
|
546
|
+
const lookahead = text.slice(target, target + SAFE_CUT_LOOKAHEAD);
|
|
547
|
+
const next = lookahead.search(/\s/);
|
|
548
|
+
end = next === -1 ? Math.min(target + SAFE_CUT_LOOKAHEAD, text.length) : target + next;
|
|
549
|
+
}
|
|
550
|
+
const begin = text.lastIndexOf(PEM_BEGIN, end);
|
|
551
|
+
const close = text.indexOf(PEM_END, begin);
|
|
552
|
+
if (begin >= start && close >= end) {
|
|
553
|
+
end = close + PEM_END.length;
|
|
554
|
+
}
|
|
555
|
+
return end;
|
|
556
|
+
}
|
|
557
|
+
var SAFE_CUT_LOOKAHEAD = 4096;
|
|
558
|
+
var PEM_BEGIN = "-----BEGIN";
|
|
559
|
+
var PEM_END = "-----END";
|
|
560
|
+
function redactSecretsInText({
|
|
561
|
+
text,
|
|
562
|
+
customPatterns = [],
|
|
563
|
+
skipRuleIds
|
|
564
|
+
}) {
|
|
565
|
+
if (typeof text !== "string" || text.length === 0) {
|
|
566
|
+
return { text, redactedCount: 0 };
|
|
567
|
+
}
|
|
568
|
+
const skipped = toSkipSet(skipRuleIds);
|
|
569
|
+
if (text.length > MAX_SCAN_LENGTH) {
|
|
570
|
+
let total = 0;
|
|
571
|
+
const pieces = sliceForScan(text).map((slice) => {
|
|
572
|
+
const scanned = redactOneSlice(slice, customPatterns, skipped);
|
|
573
|
+
total += scanned.redactedCount;
|
|
574
|
+
return scanned.text;
|
|
575
|
+
});
|
|
576
|
+
return { text: pieces.join(""), redactedCount: total };
|
|
577
|
+
}
|
|
578
|
+
return redactOneSlice(text, customPatterns, skipped);
|
|
579
|
+
}
|
|
580
|
+
function toSkipSet(skipRuleIds) {
|
|
581
|
+
if (!skipRuleIds || skipRuleIds.length === 0) return null;
|
|
582
|
+
return new Set(skipRuleIds);
|
|
583
|
+
}
|
|
584
|
+
function redactOneSlice(text, customPatterns, skipped) {
|
|
585
|
+
let redactedCount = 0;
|
|
586
|
+
let result = text;
|
|
587
|
+
for (const rule of VALUE_RULES) {
|
|
588
|
+
if (skipped?.has(rule.id)) continue;
|
|
589
|
+
if (rule.precondition && !rule.precondition(result)) continue;
|
|
590
|
+
result = result.replace(rule.regex, (...args) => {
|
|
591
|
+
const replacement = replacementFor(rule, args);
|
|
592
|
+
if (replacement === null) return args[0] ?? "";
|
|
593
|
+
redactedCount++;
|
|
594
|
+
return replacement;
|
|
595
|
+
});
|
|
596
|
+
}
|
|
597
|
+
for (const pattern of customPatterns) {
|
|
598
|
+
result = result.replace(pattern, (...args) => {
|
|
599
|
+
const full = args[0] ?? "";
|
|
600
|
+
const kept = keptLengthForCustomPattern(full);
|
|
601
|
+
if (kept === 0) return full;
|
|
602
|
+
redactedCount++;
|
|
603
|
+
return REPLACEMENT + full.slice(kept);
|
|
604
|
+
});
|
|
605
|
+
}
|
|
606
|
+
return { text: result, redactedCount };
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
// ../../packages/redaction/src/sessionReport.ts
|
|
610
|
+
var EMAIL_MARKER = formatPiiMarker("EMAIL_ADDRESS");
|
|
611
|
+
var PHONE_MARKER = formatPiiMarker("PHONE_NUMBER");
|
|
612
|
+
var CARD_MARKER = formatPiiMarker("CREDIT_CARD");
|
|
613
|
+
var IP_MARKER = formatPiiMarker("IP_ADDRESS");
|
|
614
|
+
var ENV_SECRET_NAME_REGEX = /(?:^|[._-])(?:key|token|secret|password|passwd|pwd|credentials?|auth)(?:$|[._-])/i;
|
|
615
|
+
var MIN_ENV_SECRET_LENGTH = 8;
|
|
616
|
+
function collectSensitiveEnvValues(env) {
|
|
617
|
+
const values = /* @__PURE__ */ new Set();
|
|
618
|
+
for (const [name, value] of Object.entries(env)) {
|
|
619
|
+
if (!value) continue;
|
|
620
|
+
const trimmed = value.trim();
|
|
621
|
+
if (trimmed.length < MIN_ENV_SECRET_LENGTH) continue;
|
|
622
|
+
if (!ENV_SECRET_NAME_REGEX.test(name)) continue;
|
|
623
|
+
values.add(trimmed);
|
|
624
|
+
}
|
|
625
|
+
return [...values].toSorted((a, b) => b.length - a.length);
|
|
626
|
+
}
|
|
627
|
+
var EMAIL_REGEX = /[A-Za-z0-9._%+-]{1,64}@[A-Za-z0-9.-]{1,253}\.[A-Za-z]{2,24}/g;
|
|
628
|
+
var INTL_PHONE_REGEX = /(?<![\w.+-])\+[1-9]\d{0,2}(?:[\s.-]?(?:\(\d{1,4}\)[\s.-]?)?\d{1,4}){1,5}(?!\d)/g;
|
|
629
|
+
var NATIONAL_PHONE_REGEX = /(?<!\d)(?:\(\d{3}\)[\s.-]?|\d{3}[.-])\d{3}[.-]\d{4}(?!\d)/g;
|
|
630
|
+
var CARD_REGEX = /(?<![\d.-])\d(?:[ -]?\d){12,18}(?![\d.-])/g;
|
|
631
|
+
var IPV4_REGEX = /\b(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)\.){3}(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)\b/g;
|
|
632
|
+
var countDigits = (value) => value.replace(/\D/g, "").length;
|
|
633
|
+
function passesLuhn(digits) {
|
|
634
|
+
let sum = 0;
|
|
635
|
+
let double = false;
|
|
636
|
+
for (let i = digits.length - 1; i >= 0; i--) {
|
|
637
|
+
let d = digits.charCodeAt(i) - 48;
|
|
638
|
+
if (double) {
|
|
639
|
+
d *= 2;
|
|
640
|
+
if (d > 9) d -= 9;
|
|
641
|
+
}
|
|
642
|
+
sum += d;
|
|
643
|
+
double = !double;
|
|
644
|
+
}
|
|
645
|
+
return sum % 10 === 0;
|
|
646
|
+
}
|
|
647
|
+
function isCardNumber(candidate) {
|
|
648
|
+
const digits = candidate.replace(/[ -]/g, "");
|
|
649
|
+
if (!/^\d+$/.test(digits)) return false;
|
|
650
|
+
const separated = /[ -]/.test(candidate);
|
|
651
|
+
if (!separated && (digits.length < 14 || digits.length > 16)) return false;
|
|
652
|
+
if (digits.length < 13 || digits.length > 19) return false;
|
|
653
|
+
return passesLuhn(digits);
|
|
654
|
+
}
|
|
655
|
+
function isPrivateOrLocalIp(ip) {
|
|
656
|
+
const parts = ip.split(".").map(Number);
|
|
657
|
+
const [a = 0, b = 0] = parts;
|
|
658
|
+
if (a === 0 || a === 10 || a === 127) return true;
|
|
659
|
+
if (a === 192 && b === 168) return true;
|
|
660
|
+
if (a === 172 && b >= 16 && b <= 31) return true;
|
|
661
|
+
if (a === 169 && b === 254) return true;
|
|
662
|
+
if (a === 100 && b >= 64 && b <= 127) return true;
|
|
663
|
+
if (a >= 224) return true;
|
|
664
|
+
return false;
|
|
665
|
+
}
|
|
666
|
+
var SLICE_TARGET = 2e5;
|
|
667
|
+
function sliceLongText(text) {
|
|
668
|
+
if (text.length <= SLICE_TARGET) return [text];
|
|
669
|
+
const slices = [];
|
|
670
|
+
let start = 0;
|
|
671
|
+
while (start < text.length) {
|
|
672
|
+
let end = Math.min(start + SLICE_TARGET, text.length);
|
|
673
|
+
if (end < text.length) {
|
|
674
|
+
const newline = text.lastIndexOf("\n", end);
|
|
675
|
+
if (newline > start + SLICE_TARGET / 2) end = newline;
|
|
676
|
+
}
|
|
677
|
+
slices.push(text.slice(start, end));
|
|
678
|
+
start = end;
|
|
679
|
+
}
|
|
680
|
+
return slices;
|
|
681
|
+
}
|
|
682
|
+
function redactPiiPatterns(text) {
|
|
683
|
+
let redactedCount = 0;
|
|
684
|
+
let result = text.replace(EMAIL_REGEX, () => {
|
|
685
|
+
redactedCount++;
|
|
686
|
+
return EMAIL_MARKER;
|
|
687
|
+
});
|
|
688
|
+
result = result.replace(CARD_REGEX, (match) => {
|
|
689
|
+
if (!isCardNumber(match)) return match;
|
|
690
|
+
redactedCount++;
|
|
691
|
+
return CARD_MARKER;
|
|
692
|
+
});
|
|
693
|
+
result = result.replace(INTL_PHONE_REGEX, (match) => {
|
|
694
|
+
const digits = countDigits(match);
|
|
695
|
+
if (digits < 7 || digits > 15) return match;
|
|
696
|
+
redactedCount++;
|
|
697
|
+
return PHONE_MARKER;
|
|
698
|
+
});
|
|
699
|
+
result = result.replace(NATIONAL_PHONE_REGEX, () => {
|
|
700
|
+
redactedCount++;
|
|
701
|
+
return PHONE_MARKER;
|
|
702
|
+
});
|
|
703
|
+
result = result.replace(IPV4_REGEX, (match) => {
|
|
704
|
+
if (isPrivateOrLocalIp(match)) return match;
|
|
705
|
+
redactedCount++;
|
|
706
|
+
return IP_MARKER;
|
|
707
|
+
});
|
|
708
|
+
return { text: result, redactedCount };
|
|
709
|
+
}
|
|
710
|
+
function scrubEnvLiterals(text, envValues) {
|
|
711
|
+
let result = text;
|
|
712
|
+
let redactedCount = 0;
|
|
713
|
+
for (const value of envValues) {
|
|
714
|
+
if (!result.includes(value)) continue;
|
|
715
|
+
const parts = result.split(value);
|
|
716
|
+
redactedCount += parts.length - 1;
|
|
717
|
+
result = parts.join(SECRET_MARKER);
|
|
718
|
+
}
|
|
719
|
+
return { text: result, redactedCount };
|
|
720
|
+
}
|
|
721
|
+
function redactReportText({
|
|
722
|
+
text,
|
|
723
|
+
envValues = []
|
|
724
|
+
}) {
|
|
725
|
+
if (typeof text !== "string" || text.length === 0) {
|
|
726
|
+
return { text, redactedCount: 0 };
|
|
727
|
+
}
|
|
728
|
+
const envPass = scrubEnvLiterals(text, envValues);
|
|
729
|
+
let redactedCount = envPass.redactedCount;
|
|
730
|
+
const pieces = sliceLongText(envPass.text).map((slice) => {
|
|
731
|
+
const secrets = redactSecretsInText({ text: slice });
|
|
732
|
+
redactedCount += secrets.redactedCount;
|
|
733
|
+
const pii = redactPiiPatterns(secrets.text);
|
|
734
|
+
redactedCount += pii.redactedCount;
|
|
735
|
+
return pii.text;
|
|
736
|
+
});
|
|
737
|
+
return { text: pieces.join(""), redactedCount };
|
|
738
|
+
}
|
|
739
|
+
function redactJsonValue(value, envValues, count) {
|
|
740
|
+
if (typeof value === "string") {
|
|
741
|
+
const result = redactReportText({ text: value, envValues });
|
|
742
|
+
count.redacted += result.redactedCount;
|
|
743
|
+
return result.text;
|
|
744
|
+
}
|
|
745
|
+
if (Array.isArray(value)) {
|
|
746
|
+
return value.map((item) => redactJsonValue(item, envValues, count));
|
|
747
|
+
}
|
|
748
|
+
if (value && typeof value === "object") {
|
|
749
|
+
const out = {};
|
|
750
|
+
for (const [key, entry] of Object.entries(value)) {
|
|
751
|
+
if (isSensitiveAttributeKey(key) && entry !== null && entry !== void 0) {
|
|
752
|
+
out[key] = SECRET_MARKER;
|
|
753
|
+
count.redacted++;
|
|
754
|
+
continue;
|
|
755
|
+
}
|
|
756
|
+
out[key] = redactJsonValue(entry, envValues, count);
|
|
757
|
+
}
|
|
758
|
+
return out;
|
|
759
|
+
}
|
|
760
|
+
return value;
|
|
761
|
+
}
|
|
762
|
+
function redactSessionJsonl({
|
|
763
|
+
jsonl,
|
|
764
|
+
envValues = []
|
|
765
|
+
}) {
|
|
766
|
+
let redactedCount = 0;
|
|
767
|
+
const lines = jsonl.split("\n").map((line) => {
|
|
768
|
+
if (line.trim().length === 0) return line;
|
|
769
|
+
try {
|
|
770
|
+
const parsed = JSON.parse(line);
|
|
771
|
+
const count = { redacted: 0 };
|
|
772
|
+
const redacted = redactJsonValue(parsed, envValues, count);
|
|
773
|
+
redactedCount += count.redacted;
|
|
774
|
+
return JSON.stringify(redacted);
|
|
775
|
+
} catch {
|
|
776
|
+
const result = redactReportText({ text: line, envValues });
|
|
777
|
+
redactedCount += result.redactedCount;
|
|
778
|
+
return result.text;
|
|
779
|
+
}
|
|
780
|
+
});
|
|
781
|
+
return { text: lines.join("\n"), redactedCount };
|
|
782
|
+
}
|
|
783
|
+
function truncateJsonlToByteBudget({
|
|
784
|
+
jsonl,
|
|
785
|
+
maxBytes
|
|
786
|
+
}) {
|
|
787
|
+
const encoder = new TextEncoder();
|
|
788
|
+
if (encoder.encode(jsonl).length <= maxBytes) {
|
|
789
|
+
return { text: jsonl, truncated: false };
|
|
790
|
+
}
|
|
791
|
+
const lines = jsonl.split("\n");
|
|
792
|
+
const kept = [];
|
|
793
|
+
let used = 0;
|
|
794
|
+
for (let i = lines.length - 1; i >= 0; i--) {
|
|
795
|
+
const line = lines[i] ?? "";
|
|
796
|
+
const size = encoder.encode(line).length + 1;
|
|
797
|
+
if (used + size > maxBytes) break;
|
|
798
|
+
kept.unshift(line);
|
|
799
|
+
used += size;
|
|
800
|
+
}
|
|
801
|
+
return { text: kept.join("\n"), truncated: true };
|
|
802
|
+
}
|
|
803
|
+
var REDACTION_AUDIT_URL = "https://github.com/langwatch/langwatch/blob/main/packages/redaction/src/sessionReport.ts";
|
|
804
|
+
|
|
805
|
+
// src/tools/report-issue.ts
|
|
806
|
+
var MAX_SESSION_BYTES = 8 * 1024 * 1024;
|
|
807
|
+
var REQUEST_TIMEOUT_MS = 6e4;
|
|
808
|
+
function redactReport(params) {
|
|
809
|
+
const envValues = collectSensitiveEnvValues(process.env);
|
|
810
|
+
let redactedCount = 0;
|
|
811
|
+
const titleResult = redactReportText({ text: params.title, envValues });
|
|
812
|
+
redactedCount += titleResult.redactedCount;
|
|
813
|
+
let summary;
|
|
814
|
+
if (params.summary) {
|
|
815
|
+
const result = redactReportText({ text: params.summary, envValues });
|
|
816
|
+
summary = result.text;
|
|
817
|
+
redactedCount += result.redactedCount;
|
|
818
|
+
}
|
|
819
|
+
let sessionData;
|
|
820
|
+
let sessionTruncated = false;
|
|
821
|
+
if (params.session_content) {
|
|
822
|
+
const redacted = redactSessionJsonl({
|
|
823
|
+
jsonl: params.session_content,
|
|
824
|
+
envValues
|
|
825
|
+
});
|
|
826
|
+
redactedCount += redacted.redactedCount;
|
|
827
|
+
const truncated = truncateJsonlToByteBudget({
|
|
828
|
+
jsonl: redacted.text,
|
|
829
|
+
maxBytes: MAX_SESSION_BYTES
|
|
830
|
+
});
|
|
831
|
+
sessionData = truncated.text;
|
|
832
|
+
sessionTruncated = truncated.truncated;
|
|
833
|
+
}
|
|
834
|
+
return {
|
|
835
|
+
title: titleResult.text,
|
|
836
|
+
summary,
|
|
837
|
+
sessionData,
|
|
838
|
+
sessionTruncated,
|
|
839
|
+
redactedCount
|
|
840
|
+
};
|
|
841
|
+
}
|
|
842
|
+
async function deliverReport(report, params) {
|
|
843
|
+
const config = getConfig();
|
|
844
|
+
const endpoint = config.endpoint.replace(/\/+$/, "");
|
|
845
|
+
const response = await fetch(`${endpoint}/api/v1/bug-reports`, {
|
|
846
|
+
method: "POST",
|
|
847
|
+
headers: {
|
|
848
|
+
"content-type": "application/json",
|
|
849
|
+
...config.apiKey ? { authorization: `Bearer ${config.apiKey}` } : {}
|
|
850
|
+
},
|
|
851
|
+
body: JSON.stringify({
|
|
852
|
+
source: "mcp",
|
|
853
|
+
kind: report.sessionData ? "full_session" : "summary",
|
|
854
|
+
title: report.title,
|
|
855
|
+
...report.summary ? { summary: report.summary } : {},
|
|
856
|
+
...report.sessionData ? { sessionData: report.sessionData, sessionTruncated: report.sessionTruncated } : {},
|
|
857
|
+
...params.agent ? { agent: params.agent } : {},
|
|
858
|
+
...params.contact_email ? { contactEmail: params.contact_email } : {}
|
|
859
|
+
}),
|
|
860
|
+
signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS)
|
|
861
|
+
}).catch(() => {
|
|
862
|
+
throw new Error(
|
|
863
|
+
`Could not reach ${endpoint} to deliver the report. Retry in a moment; if it keeps failing, email support@langwatch.ai with the same content.`
|
|
864
|
+
);
|
|
865
|
+
});
|
|
866
|
+
if (!response.ok) {
|
|
867
|
+
const body = (await response.text().catch(() => "")).slice(0, 300);
|
|
868
|
+
throw new Error(
|
|
869
|
+
`The reports endpoint answered HTTP ${response.status}: ${body}. Retry in a moment; if it keeps failing, email support@langwatch.ai.`
|
|
870
|
+
);
|
|
871
|
+
}
|
|
872
|
+
const { id } = await response.json();
|
|
873
|
+
return id;
|
|
874
|
+
}
|
|
875
|
+
async function handleReportIssue(params) {
|
|
876
|
+
if (!params.user_approved) {
|
|
877
|
+
throw new Error(
|
|
878
|
+
`This sends a report to the LangWatch team, so the user must approve it first. Ask the user: "Can I send this issue report to LangWatch to help them fix it?" If they agree, call again with user_approved=true. Secrets and personal data are redacted locally before sending; audit the rules at ${REDACTION_AUDIT_URL}`
|
|
879
|
+
);
|
|
880
|
+
}
|
|
881
|
+
if (!params.summary?.trim() && !params.session_content?.trim()) {
|
|
882
|
+
throw new Error(
|
|
883
|
+
"Nothing to report: pass a summary (what you tried, verbatim errors, what you had to figure out), session_content, or both."
|
|
884
|
+
);
|
|
885
|
+
}
|
|
886
|
+
const report = redactReport(params);
|
|
887
|
+
const id = await deliverReport(report, params);
|
|
888
|
+
return [
|
|
889
|
+
`Report sent to the LangWatch team (${id}). Thank you!`,
|
|
890
|
+
`Redacted ${report.redactedCount} sensitive value${report.redactedCount === 1 ? "" : "s"} locally before sending.`,
|
|
891
|
+
...report.sessionTruncated ? ["The session content was truncated to the most recent activity to fit the upload limit."] : [],
|
|
892
|
+
"Reports like this directly shape what gets fixed next."
|
|
893
|
+
].join(" ");
|
|
894
|
+
}
|
|
895
|
+
export {
|
|
896
|
+
handleReportIssue
|
|
897
|
+
};
|
|
898
|
+
//# sourceMappingURL=report-issue-AS34ZMJF.js.map
|