@warlock.js/ai-tools 4.8.1 → 4.9.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 (67) hide show
  1. package/cjs/index.cjs +15 -15
  2. package/cjs/index.cjs.map +1 -1
  3. package/esm/contracts/http.type.d.mts +1 -1
  4. package/esm/contracts/http.type.d.mts.map +1 -1
  5. package/esm/contracts/mcp.type.d.mts +1 -1
  6. package/esm/contracts/mcp.type.d.mts.map +1 -1
  7. package/esm/contracts/utility.type.d.mts +1 -1
  8. package/esm/contracts/utility.type.d.mts.map +1 -1
  9. package/esm/contracts/web.type.d.mts +1 -1
  10. package/esm/contracts/web.type.d.mts.map +1 -1
  11. package/esm/errors.d.mts +1 -1
  12. package/esm/errors.d.mts.map +1 -1
  13. package/esm/errors.mjs +1 -1
  14. package/esm/errors.mjs.map +1 -1
  15. package/esm/http/http-request.d.mts +1 -1
  16. package/esm/http/http-request.d.mts.map +1 -1
  17. package/esm/http/http-request.mjs +1 -1
  18. package/esm/http/http-request.mjs.map +1 -1
  19. package/esm/mcp/client.mjs +1 -1
  20. package/esm/mcp/client.mjs.map +1 -1
  21. package/esm/mcp/index.d.mts +1 -1
  22. package/esm/mcp/index.d.mts.map +1 -1
  23. package/esm/mcp/index.mjs +1 -1
  24. package/esm/mcp/index.mjs.map +1 -1
  25. package/esm/mcp/json-schema-to-standard.d.mts +1 -1
  26. package/esm/mcp/json-schema-to-standard.d.mts.map +1 -1
  27. package/esm/mcp/json-schema-to-standard.mjs +1 -1
  28. package/esm/mcp/json-schema-to-standard.mjs.map +1 -1
  29. package/esm/mcp/serve.d.mts +1 -1
  30. package/esm/mcp/serve.d.mts.map +1 -1
  31. package/esm/mcp/serve.mjs +1 -1
  32. package/esm/mcp/serve.mjs.map +1 -1
  33. package/esm/mcp/transport.d.mts +1 -1
  34. package/esm/mcp/transport.d.mts.map +1 -1
  35. package/esm/mcp/transport.mjs +1 -1
  36. package/esm/mcp/transport.mjs.map +1 -1
  37. package/esm/mcp/transport.type.d.mts +1 -1
  38. package/esm/mcp/transport.type.d.mts.map +1 -1
  39. package/esm/node_modules/@standard-schema/spec/dist/index.d.mts +1 -1
  40. package/esm/node_modules/@standard-schema/spec/dist/index.d.mts.map +1 -1
  41. package/esm/register.d.mts +1 -1
  42. package/esm/register.d.mts.map +1 -1
  43. package/esm/register.mjs +1 -1
  44. package/esm/register.mjs.map +1 -1
  45. package/esm/schema.mjs +1 -1
  46. package/esm/schema.mjs.map +1 -1
  47. package/esm/utility/calculator.d.mts +1 -1
  48. package/esm/utility/calculator.d.mts.map +1 -1
  49. package/esm/utility/calculator.mjs +1 -1
  50. package/esm/utility/calculator.mjs.map +1 -1
  51. package/esm/utility/date-time.d.mts +1 -1
  52. package/esm/utility/date-time.d.mts.map +1 -1
  53. package/esm/utility/date-time.mjs +1 -1
  54. package/esm/utility/date-time.mjs.map +1 -1
  55. package/esm/utility/schema.mjs +1 -1
  56. package/esm/utility/schema.mjs.map +1 -1
  57. package/esm/web/fetch-url.d.mts +1 -1
  58. package/esm/web/fetch-url.d.mts.map +1 -1
  59. package/esm/web/fetch-url.mjs +1 -1
  60. package/esm/web/fetch-url.mjs.map +1 -1
  61. package/esm/web/schema.mjs +1 -1
  62. package/esm/web/schema.mjs.map +1 -1
  63. package/esm/web/web-search.d.mts +1 -1
  64. package/esm/web/web-search.d.mts.map +1 -1
  65. package/esm/web/web-search.mjs +1 -1
  66. package/esm/web/web-search.mjs.map +1 -1
  67. package/package.json +2 -2
@@ -1,4 +1,4 @@
1
- //#region ../@warlock.js/ai-tools/src/contracts/http.type.d.ts
1
+ //#region ../ai-tools/src/contracts/http.type.d.ts
2
2
  /**
3
3
  * Type contracts for `ai.tools.http` — a guarded HTTP/REST request tool.
4
4
  * Pure declarations only; the factory lives outside `contracts/`.
@@ -1 +1 @@
1
- {"version":3,"file":"http.type.d.mts","names":[],"sources":["../../../../../../../@warlock.js/ai-tools/src/contracts/http.type.ts"],"mappings":";;AAUA;;;;AAAsB;AAOtB;;;KAPY,UAAA;;;;;;UAOK,kBAAA;EAyBL;;;;AAYF;EA/BR,IAAA;EAqC+B;;;;EAhC/B,OAAA;EA0CA;;;;;AAOI;EA1CJ,YAAA,GAAe,UAAA;EAgDiB;;;;EA3ChC,UAAA;EA+CS;EA7CT,OAAA,GAAU,MAAM;EAoDhB;;AAAS;;;EA9CT,SAAA;;;;;;EAMA,QAAA;AAAA;;;;UAMe,gBAAA;;;;;EAKf,MAAA,GAAS,UAAA;;;;;EAKT,GAAA;;EAEA,OAAA,GAAU,MAAM;;;;;EAKhB,IAAA;AAAA;;;;UAMe,iBAAA;;EAEf,MAAA;;EAEA,OAAA,EAAS,MAAM;;;;;EAKf,IAAA;;EAEA,SAAA;AAAA"}
1
+ {"version":3,"file":"http.type.d.mts","names":[],"sources":["../../../../../../../ai-tools/src/contracts/http.type.ts"],"mappings":";;AAUA;;;;AAAsB;AAOtB;;;KAPY,UAAA;;;;;;UAOK,kBAAA;EAyBL;;;;AAYF;EA/BR,IAAA;EAqC+B;;;;EAhC/B,OAAA;EA0CA;;;;;AAOI;EA1CJ,YAAA,GAAe,UAAA;EAgDiB;;;;EA3ChC,UAAA;EA+CS;EA7CT,OAAA,GAAU,MAAM;EAoDhB;;AAAS;;;EA9CT,SAAA;;;;;;EAMA,QAAA;AAAA;;;;UAMe,gBAAA;;;;;EAKf,MAAA,GAAS,UAAA;;;;;EAKT,GAAA;;EAEA,OAAA,GAAU,MAAM;;;;;EAKhB,IAAA;AAAA;;;;UAMe,iBAAA;;EAEf,MAAA;;EAEA,OAAA,EAAS,MAAM;;;;;EAKf,IAAA;;EAEA,SAAA;AAAA"}
@@ -1,6 +1,6 @@
1
1
  import { JsonSchemaTarget, ToolContract } from "@warlock.js/ai";
2
2
 
3
- //#region ../@warlock.js/ai-tools/src/contracts/mcp.type.d.ts
3
+ //#region ../ai-tools/src/contracts/mcp.type.d.ts
4
4
  /**
5
5
  * Type contracts for the Model Context Protocol surface — the
6
6
  * `ai.mcp(server)` client and `ai.mcp.serve(source)` server. Pure
@@ -1 +1 @@
1
- {"version":3,"file":"mcp.type.d.mts","names":[],"sources":["../../../../../../../@warlock.js/ai-tools/src/contracts/mcp.type.ts"],"mappings":";;;;;AAqBA;;;;;;;;;;;;;;AAoBsB;AAOtB;KA3BY,YAAA;mDAGN,IAAA,WA8BJ;EA5BI,OAAA,UA8BM;EA5BN,IAAA;EAkCK;AAAA;AAMX;;EAnCM,GAAA,GAAM,MAAA;AAAA;EAwCD,iCApCL,IAAA,UAsCY;EApCZ,GAAA,UAkCJ;EAhCI,OAAA,GAAU,MAAM;AAAA;;;;AAkCJ;UA3BD,gBAAA;EAmCS;;;;;EA7BxB,UAAA;EA+Bc;EA7Bd,MAAA,IAAU,QAAA;EAqCA;;;;;EA/BV,SAAA;AAAA;;AAiCsB;AAMxB;UAjCiB,SAAA;;;;;EAKf,KAAA,IAAS,OAAA,CAAQ,YAAA;EA0CL;EAxCZ,KAAA,IAAS,OAAA;AAAA;;AAgDsB;AAMjC;;;KA9CY,cAAA;EACN,KAAA,IAAS,YAAA;AAAA,IACX,YAAY;;;;AAgDC;AAYjB;;KApDY,iBAAA;EACN,IAAA;AAAA;EACA,IAAA;EAAc,IAAA;AAAA;;AAqDC;AAKrB;;UApDiB,eAAA;EAqDN;EAnDT,IAAA;EAwDS;;;;;EAlDT,OAAA;EA8CA;;;;;EAxCA,SAAA,GAAY,iBAAA;EA4CI;AAMlB;;;;;;EA1CE,YAAA,GAAe,gBAAgB;AAAA;;;;UAMhB,SAAA;EA+CA;EA7Cf,KAAA,IAAS,OAAA;;EAET,IAAA,IAAQ,OAAO;AAAA;;KAYL,cAAA;;KAGA,SAAA;AAwCZ;;;AAAA,UAnCiB,cAAA;EACf,OAAA,EAAS,cAAA;EACT,EAAA,EAAI,SAAA;EAuCI;EArCR,MAAA;EAqCoB;EAnCpB,MAAA,GAAS,OAAA;AAAA;;;;UAMM,mBAAA;EACf,OAAA,EAAS,cAAA;EA4BT;EA1BA,MAAA;EA0BoB;EAxBpB,MAAA,GAAS,OAAO;AAAA;;;;UAMD,YAAA;EA2Bb;EAzBF,IAAA;EAyBiB;EAvBjB,OAAA;EAsBE;EApBF,IAAA;AAAA;AAqBiB;AAOnB;;AAPmB,UAfF,eAAA;EACf,OAAA,EAAS,cAAA;EACT,EAAA,EAAI,SAAA;EAwBJ;EAtBA,MAAA,GAAS,OAAA;EAwBK;EAtBd,KAAA,GAAQ,YAAA;AAAA;AA+BV;;;AAAA,KAzBY,cAAA,GACR,cAAA,GACA,mBAAA,GACA,eAAA;;;;;AA4BY;UArBC,iBAAA;EA6BiB;EA3BhC,IAAA;EA6BwB;EA3BxB,WAAA;EA2BS;EAzBT,WAAA,GAAc,MAAM;AAAA;AA2Bb;;;;;;AAAA,UAlBQ,eAAA;;EAEf,IAAA;;EAEA,IAAA;;GAEC,KAAA;AAAA;;;;;;UAQc,iBAAA;;EAEf,OAAA,EAAS,eAAe;;EAExB,OAAA;AAAA"}
1
+ {"version":3,"file":"mcp.type.d.mts","names":[],"sources":["../../../../../../../ai-tools/src/contracts/mcp.type.ts"],"mappings":";;;;;AAqBA;;;;;;;;;;;;;;AAoBsB;AAOtB;KA3BY,YAAA;mDAGN,IAAA,WA8BJ;EA5BI,OAAA,UA8BM;EA5BN,IAAA;EAkCK;AAAA;AAMX;;EAnCM,GAAA,GAAM,MAAA;AAAA;EAwCD,iCApCL,IAAA,UAsCY;EApCZ,GAAA,UAkCJ;EAhCI,OAAA,GAAU,MAAM;AAAA;;;;AAkCJ;UA3BD,gBAAA;EAmCS;;;;;EA7BxB,UAAA;EA+Bc;EA7Bd,MAAA,IAAU,QAAA;EAqCA;;;;;EA/BV,SAAA;AAAA;;AAiCsB;AAMxB;UAjCiB,SAAA;;;;;EAKf,KAAA,IAAS,OAAA,CAAQ,YAAA;EA0CL;EAxCZ,KAAA,IAAS,OAAA;AAAA;;AAgDsB;AAMjC;;;KA9CY,cAAA;EACN,KAAA,IAAS,YAAA;AAAA,IACX,YAAY;;;;AAgDC;AAYjB;;KApDY,iBAAA;EACN,IAAA;AAAA;EACA,IAAA;EAAc,IAAA;AAAA;;AAqDC;AAKrB;;UApDiB,eAAA;EAqDN;EAnDT,IAAA;EAwDS;;;;;EAlDT,OAAA;EA8CA;;;;;EAxCA,SAAA,GAAY,iBAAA;EA4CI;AAMlB;;;;;;EA1CE,YAAA,GAAe,gBAAgB;AAAA;;;;UAMhB,SAAA;EA+CA;EA7Cf,KAAA,IAAS,OAAA;;EAET,IAAA,IAAQ,OAAO;AAAA;;KAYL,cAAA;;KAGA,SAAA;AAwCZ;;;AAAA,UAnCiB,cAAA;EACf,OAAA,EAAS,cAAA;EACT,EAAA,EAAI,SAAA;EAuCI;EArCR,MAAA;EAqCoB;EAnCpB,MAAA,GAAS,OAAA;AAAA;;;;UAMM,mBAAA;EACf,OAAA,EAAS,cAAA;EA4BT;EA1BA,MAAA;EA0BoB;EAxBpB,MAAA,GAAS,OAAO;AAAA;;;;UAMD,YAAA;EA2Bb;EAzBF,IAAA;EAyBiB;EAvBjB,OAAA;EAsBE;EApBF,IAAA;AAAA;AAqBiB;AAOnB;;AAPmB,UAfF,eAAA;EACf,OAAA,EAAS,cAAA;EACT,EAAA,EAAI,SAAA;EAwBJ;EAtBA,MAAA,GAAS,OAAA;EAwBK;EAtBd,KAAA,GAAQ,YAAA;AAAA;AA+BV;;;AAAA,KAzBY,cAAA,GACR,cAAA,GACA,mBAAA,GACA,eAAA;;;;;AA4BY;UArBC,iBAAA;EA6BiB;EA3BhC,IAAA;EA6BwB;EA3BxB,WAAA;EA2BS;EAzBT,WAAA,GAAc,MAAM;AAAA;AA2Bb;;;;;;AAAA,UAlBQ,eAAA;;EAEf,IAAA;;EAEA,IAAA;;GAEC,KAAA;AAAA;;;;;;UAQc,iBAAA;;EAEf,OAAA,EAAS,eAAe;;EAExB,OAAA;AAAA"}
@@ -1,4 +1,4 @@
1
- //#region ../@warlock.js/ai-tools/src/contracts/utility.type.d.ts
1
+ //#region ../ai-tools/src/contracts/utility.type.d.ts
2
2
  /**
3
3
  * Type contracts for the utility tools — `ai.tools.calculator` and
4
4
  * `ai.tools.dateTime`. Pure declarations only; the factories live
@@ -1 +1 @@
1
- {"version":3,"file":"utility.type.d.mts","names":[],"sources":["../../../../../../../@warlock.js/ai-tools/src/contracts/utility.type.ts"],"mappings":";;AASA;;;;AAMM;AAMN;;AANM,UANW,iBAAA;EAcL;AAAA;AAMZ;;;EAdE,IAAI;AAAA;AA2BN;;;AAAA,UArBiB,eAAA;EAqBK;EAnBpB,UAAU;AAAA;;;AAoCK;UA9BA,gBAAA;EAqCa;EAnC5B,MAAM;AAAA;;;;;;;;;KAWI,UAAA;;AA4CJ;AAMR;UA7CiB,eAAA;;;AAkDV;;;EA5CL,IAAA;;;;;;EAMA,eAAe;AAAA;;;;;UAOA,aAAA;;EAEf,EAAA,EAAI,UAAU;;EAEd,GAAA;;;;;;EAMA,IAAA;;EAEA,EAAA;;EAEA,MAAA;;EAEA,IAAA;;EAEA,QAAA;;EAEA,MAAA;AAAA;;;;UAMe,cAAA;;;;;EAKf,KAAK;AAAA"}
1
+ {"version":3,"file":"utility.type.d.mts","names":[],"sources":["../../../../../../../ai-tools/src/contracts/utility.type.ts"],"mappings":";;AASA;;;;AAMM;AAMN;;AANM,UANW,iBAAA;EAcL;AAAA;AAMZ;;;EAdE,IAAI;AAAA;AA2BN;;;AAAA,UArBiB,eAAA;EAqBK;EAnBpB,UAAU;AAAA;;;AAoCK;UA9BA,gBAAA;EAqCa;EAnC5B,MAAM;AAAA;;;;;;;;;KAWI,UAAA;;AA4CJ;AAMR;UA7CiB,eAAA;;;AAkDV;;;EA5CL,IAAA;;;;;;EAMA,eAAe;AAAA;;;;;UAOA,aAAA;;EAEf,EAAA,EAAI,UAAU;;EAEd,GAAA;;;;;;EAMA,IAAA;;EAEA,EAAA;;EAEA,MAAA;;EAEA,IAAA;;EAEA,QAAA;;EAEA,MAAA;AAAA;;;;UAMe,cAAA;;;;;EAKf,KAAK;AAAA"}
@@ -1,4 +1,4 @@
1
- //#region ../@warlock.js/ai-tools/src/contracts/web.type.d.ts
1
+ //#region ../ai-tools/src/contracts/web.type.d.ts
2
2
  /**
3
3
  * Type contracts for the web tools — `ai.tools.webSearch` and
4
4
  * `ai.tools.fetchUrl`. Pure declarations only; the factories live
@@ -1 +1 @@
1
- {"version":3,"file":"web.type.d.mts","names":[],"sources":["../../../../../../../@warlock.js/ai-tools/src/contracts/web.type.ts"],"mappings":";;AAaA;;;;AAA0B;AAK1B;;;;;;AAL0B,KAAd,cAAA;;;;UAKK,gBAAA;EA0BA;EAxBf,QAAA,EAAU,cAAc;;;AA4Bd;AAOZ;EA9BE,MAAA;;;;;;;EAOA,UAAA;EA+BK;AAMP;;;;EA/BE,IAAA;AAAA;;;;UAMe,cAAA;EA4CA;EA1Cf,KAAA;;EAEA,UAAU;AAAA;;;;;UAOK,mBAAA;EA8DL;EA5DV,KAAA;EAkEe;EAhEf,GAAA;;EAEA,OAAA;EAgEG;EA9DH,KAAA;AAAA;;;;UAMe,eAAA;EAoEf;EAlEA,OAAA,EAAS,mBAAmB;AAAA;AAoEnB;;;;;;;;;AAAA,KAxDC,eAAA;;;;UAKK,eAAA;;;;;;EAMf,IAAA;;;;;;EAMA,QAAA;;;;;;EAMA,SAAA;;;;;;EAMA,OAAA,GAAU,eAAe;;;;;EAKzB,UAAA;AAAA;;;;UAMe,aAAA;;EAEf,GAAG;AAAA;;;;UAMY,cAAA;;EAEf,GAAA;;EAEA,MAAA;;EAEA,OAAA;;EAEA,SAAA;AAAA"}
1
+ {"version":3,"file":"web.type.d.mts","names":[],"sources":["../../../../../../../ai-tools/src/contracts/web.type.ts"],"mappings":";;AAaA;;;;AAA0B;AAK1B;;;;;;AAL0B,KAAd,cAAA;;;;UAKK,gBAAA;EA0BA;EAxBf,QAAA,EAAU,cAAc;;;AA4Bd;AAOZ;EA9BE,MAAA;;;;;;;EAOA,UAAA;EA+BK;AAMP;;;;EA/BE,IAAA;AAAA;;;;UAMe,cAAA;EA4CA;EA1Cf,KAAA;;EAEA,UAAU;AAAA;;;;;UAOK,mBAAA;EA8DL;EA5DV,KAAA;EAkEe;EAhEf,GAAA;;EAEA,OAAA;EAgEG;EA9DH,KAAA;AAAA;;;;UAMe,eAAA;EAoEf;EAlEA,OAAA,EAAS,mBAAmB;AAAA;AAoEnB;;;;;;;;;AAAA,KAxDC,eAAA;;;;UAKK,eAAA;;;;;;EAMf,IAAA;;;;;;EAMA,QAAA;;;;;;EAMA,SAAA;;;;;;EAMA,OAAA,GAAU,eAAe;;;;;EAKzB,UAAA;AAAA;;;;UAMe,aAAA;;EAEf,GAAG;AAAA;;;;UAMY,cAAA;;EAEf,GAAA;;EAEA,MAAA;;EAEA,OAAA;;EAEA,SAAA;AAAA"}
package/esm/errors.d.mts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { AIError, AIErrorOptions } from "@warlock.js/ai";
2
2
 
3
- //#region ../@warlock.js/ai-tools/src/errors.d.ts
3
+ //#region ../ai-tools/src/errors.d.ts
4
4
  /**
5
5
  * Why the calculator rejected an expression.
6
6
  *
@@ -1 +1 @@
1
- {"version":3,"file":"errors.d.mts","names":[],"sources":["../../../../../../@warlock.js/ai-tools/src/errors.ts"],"mappings":";;;;;AAUA;;;;AAA6B;AAM7B;KANY,iBAAA;;;;;KAMA,sBAAA,GAAyB,cAAA;EAEZ,kDAAvB,IAAA,EAAM,iBAAiB;AAAA;;;;;;;;;;;;;;;;AAyB4C;AAiBrE;;cArBa,eAAA,SAAwB,OAAA;EAqBV;EAAA,SAnBT,IAAA,EAAM,iBAAA;cAEH,OAAA,UAAiB,OAAA,EAAS,sBAAA;AAAA;;;;;;;AA6BxB;AAmBvB;;KA/BY,eAAA;;;;;KAUA,oBAAA,GAAuB,cAAA;EAqBA,iDAnBjC,IAAA,EAAM,eAAe;AAAA;;;;;;AAuB4C;AAoBnE;;;;AAA+B;AAO/B;;;;;cA/Ba,aAAA,SAAsB,OAAA;EAiC3B;EAAA,SA/BU,IAAA,EAAM,eAAA;cAEH,OAAA,UAAiB,OAAA,EAAS,oBAAA;AAAA;AAqD/C;;;;;;;;;;;;AAAA,KAjCY,mBAAA;;;;;AAuC2D;KAhC3D,wBAAA,GAA2B,cAAA;EA0Db,iDAxDxB,IAAA,EAAM,mBAAmB,EAwDD;EAtDxB,MAAA;AAAA;;;;;;;;AAmEoB;AAsBtB;;;;;;;;;;;cAnEa,iBAAA,SAA0B,OAAA;EAuElB;EAAA,SArEH,IAAA,EAAM,mBAAA;EAqEc;EAAA,SAnEpB,MAAA;cAEG,OAAA,UAAiB,OAAA,EAAS,wBAAA;AAAA;;;;AAoFlB;AAM7B;;;;;;;;AAEyB;AAwBzB;;;;KA1FY,cAAA;;;;;KAWA,mBAAA,GAAsB,cAAA;EAiFV,gDA/EtB,IAAA,EAAM,cAAc;AAAA;;;;AAiF+C;;;;;;;;;;;;;;;;cA3DxD,YAAA,SAAqB,OAAA;;WAEhB,IAAA,EAAM,cAAA;cAEH,OAAA,UAAiB,OAAA,EAAS,mBAAA;AAAA;;;;;;;;;;;;KAmBnC,iBAAA;;;;;KAMA,sBAAA,GAAyB,cAAA;kDAEnC,IAAA,EAAM,iBAAiB;AAAA;;;;;;;;;;;;;;;;;;;;;;cAwBZ,eAAA,SAAwB,OAAA;;WAEnB,IAAA,EAAM,iBAAA;cAEH,OAAA,UAAiB,OAAA,EAAS,sBAAA;AAAA"}
1
+ {"version":3,"file":"errors.d.mts","names":[],"sources":["../../../../../../ai-tools/src/errors.ts"],"mappings":";;;;;AAUA;;;;AAA6B;AAM7B;KANY,iBAAA;;;;;KAMA,sBAAA,GAAyB,cAAA;EAEZ,kDAAvB,IAAA,EAAM,iBAAiB;AAAA;;;;;;;;;;;;;;;;AAyB4C;AAiBrE;;cArBa,eAAA,SAAwB,OAAA;EAqBV;EAAA,SAnBT,IAAA,EAAM,iBAAA;cAEH,OAAA,UAAiB,OAAA,EAAS,sBAAA;AAAA;;;;;;;AA6BxB;AAmBvB;;KA/BY,eAAA;;;;;KAUA,oBAAA,GAAuB,cAAA;EAqBA,iDAnBjC,IAAA,EAAM,eAAe;AAAA;;;;;;AAuB4C;AAoBnE;;;;AAA+B;AAO/B;;;;;cA/Ba,aAAA,SAAsB,OAAA;EAiC3B;EAAA,SA/BU,IAAA,EAAM,eAAA;cAEH,OAAA,UAAiB,OAAA,EAAS,oBAAA;AAAA;AAqD/C;;;;;;;;;;;;AAAA,KAjCY,mBAAA;;;;;AAuC2D;KAhC3D,wBAAA,GAA2B,cAAA;EA0Db,iDAxDxB,IAAA,EAAM,mBAAmB,EAwDD;EAtDxB,MAAA;AAAA;;;;;;;;AAmEoB;AAsBtB;;;;;;;;;;;cAnEa,iBAAA,SAA0B,OAAA;EAuElB;EAAA,SArEH,IAAA,EAAM,mBAAA;EAqEc;EAAA,SAnEpB,MAAA;cAEG,OAAA,UAAiB,OAAA,EAAS,wBAAA;AAAA;;;;AAoFlB;AAM7B;;;;;;;;AAEyB;AAwBzB;;;;KA1FY,cAAA;;;;;KAWA,mBAAA,GAAsB,cAAA;EAiFV,gDA/EtB,IAAA,EAAM,cAAc;AAAA;;;;AAiF+C;;;;;;;;;;;;;;;;cA3DxD,YAAA,SAAqB,OAAA;;WAEhB,IAAA,EAAM,cAAA;cAEH,OAAA,UAAiB,OAAA,EAAS,mBAAA;AAAA;;;;;;;;;;;;KAmBnC,iBAAA;;;;;KAMA,sBAAA,GAAyB,cAAA;kDAEnC,IAAA,EAAM,iBAAiB;AAAA;;;;;;;;;;;;;;;;;;;;;;cAwBZ,eAAA,SAAwB,OAAA;;WAEnB,IAAA,EAAM,iBAAA;cAEH,OAAA,UAAiB,OAAA,EAAS,sBAAA;AAAA"}
package/esm/errors.mjs CHANGED
@@ -1,6 +1,6 @@
1
1
  import { AIError } from "@warlock.js/ai";
2
2
 
3
- //#region ../@warlock.js/ai-tools/src/errors.ts
3
+ //#region ../ai-tools/src/errors.ts
4
4
  /**
5
5
  * The `calculator` tool could not evaluate an expression — it was not
6
6
  * valid arithmetic, divided by zero, or overflowed to a non-finite
@@ -1 +1 @@
1
- {"version":3,"file":"errors.mjs","names":[],"sources":["../../../../../../@warlock.js/ai-tools/src/errors.ts"],"sourcesContent":["import { AIError, type AIErrorOptions } from \"@warlock.js/ai\";\n\n/**\n * Why the calculator rejected an expression.\n *\n * - `\"syntax\"` — the expression could not be tokenized or parsed\n * (an unknown character, a misplaced operator, unbalanced parens).\n * - `\"divide-by-zero\"` — evaluation divided (or took a modulo) by zero.\n * - `\"overflow\"` — the computed result was not a finite number.\n */\nexport type CalculatorFailure = \"syntax\" | \"divide-by-zero\" | \"overflow\";\n\n/**\n * Options for {@link CalculatorError} — the structured `type`\n * discriminator so a caller can branch without parsing the message.\n */\nexport type CalculatorErrorOptions = AIErrorOptions & {\n /** Which class of calculator failure occurred. */\n type: CalculatorFailure;\n};\n\n/**\n * The `calculator` tool could not evaluate an expression — it was not\n * valid arithmetic, divided by zero, or overflowed to a non-finite\n * value.\n *\n * **Surface.** Thrown inside the tool handler, where `tool()` wraps it\n * into a `ToolExecutionError` whose message is preserved verbatim and\n * reaches the model as `{ error }` data, so the agent self-corrects\n * rather than crashing. Extends the framework {@link AIError} (category\n * `\"tool\"` via code `TOOL_EXEC_FAILED`) so it flows through the same\n * typed error contract as every other AI error; branch on `error.type`\n * for the specific failure.\n *\n * @example\n * if (error instanceof CalculatorError && error.type === \"divide-by-zero\") {\n * // the expression divided by zero — ask the model to revise it\n * }\n */\nexport class CalculatorError extends AIError {\n /** Which class of calculator failure occurred. */\n public readonly type: CalculatorFailure;\n\n public constructor(message: string, options: CalculatorErrorOptions) {\n super(\"TOOL_EXEC_FAILED\", message, options);\n\n this.name = \"CalculatorError\";\n this.type = options.type;\n }\n}\n\n/**\n * Why the date-time tool rejected a call.\n *\n * - `\"invalid-input\"` — a required field for the chosen `op` was\n * missing or malformed (an unparsable ISO instant, a bad `amount`).\n * - `\"invalid-unit\"` — `unit` was not one of the supported units.\n * - `\"invalid-time-zone\"` — the IANA time zone was not recognized.\n * - `\"unsupported-op\"` — the `op` was not one this tool implements.\n */\nexport type DateTimeFailure =\n | \"invalid-input\"\n | \"invalid-unit\"\n | \"invalid-time-zone\"\n | \"unsupported-op\";\n\n/**\n * Options for {@link DateTimeError} — the structured `type`\n * discriminator so a caller can branch without parsing the message.\n */\nexport type DateTimeErrorOptions = AIErrorOptions & {\n /** Which class of date-time failure occurred. */\n type: DateTimeFailure;\n};\n\n/**\n * The `date_time` tool could not complete a call — a required field was\n * missing or malformed, the unit/time zone was unrecognized, or the\n * operation is unsupported.\n *\n * **Surface.** Thrown inside the tool handler, where `tool()` wraps it\n * into a `ToolExecutionError` whose message reaches the model as\n * `{ error }` data so the agent self-corrects. Extends the framework\n * {@link AIError} (category `\"tool\"` via code `TOOL_EXEC_FAILED`); branch\n * on `error.type` for the specific failure.\n *\n * @example\n * if (error instanceof DateTimeError && error.type === \"invalid-unit\") {\n * // the model passed an unknown unit — re-prompt with the allowed set\n * }\n */\nexport class DateTimeError extends AIError {\n /** Which class of date-time failure occurred. */\n public readonly type: DateTimeFailure;\n\n public constructor(message: string, options: DateTimeErrorOptions) {\n super(\"TOOL_EXEC_FAILED\", message, options);\n\n this.name = \"DateTimeError\";\n this.type = options.type;\n }\n}\n\n/**\n * Why an MCP transport operation failed.\n *\n * - `\"connect\"` — the transport could not be opened (child process\n * failed to spawn, HTTP endpoint unreachable) or the `initialize`\n * handshake failed.\n * - `\"protocol\"` — a malformed / unexpected JSON-RPC message, a\n * response that matched no in-flight request, or a missing field.\n * - `\"timeout\"` — a request exceeded its per-call deadline.\n * - `\"closed\"` — the transport was used after it was closed, or the\n * peer closed it mid-call.\n */\nexport type McpTransportFailure = \"connect\" | \"protocol\" | \"timeout\" | \"closed\";\n\n/**\n * Options for {@link McpTransportError} — the structured `type`\n * discriminator plus an optional JSON-RPC method name for branchable\n * diagnostics without parsing the message.\n */\nexport type McpTransportErrorOptions = AIErrorOptions & {\n /** Which class of transport failure occurred. */\n type: McpTransportFailure;\n /** The JSON-RPC method in flight when the failure occurred, if any. */\n method?: string;\n};\n\n/**\n * The MCP client's transport layer failed — it could not connect, the\n * peer spoke malformed JSON-RPC, a call timed out, or the transport was\n * already closed.\n *\n * **Surface.** Connection / handshake failures surface at\n * agent-construction time (the caller `await`s `client.tools()`). A\n * `tools/call` failure raised mid-run is wrapped by `tool()` into a\n * `ToolExecutionError` and reaches the model as `{ error }` data, so the\n * agent self-corrects rather than crashing. Extends the framework\n * {@link AIError} (category `\"tool\"`, code `TOOL_EXEC_FAILED`) so it\n * flows through the same typed error contract as every other AI error;\n * branch on `error.type` for the specific failure.\n *\n * @example\n * if (error instanceof McpTransportError && error.type === \"timeout\") {\n * // the remote call exceeded its deadline — retry or escalate\n * }\n */\nexport class McpTransportError extends AIError {\n /** Which class of transport failure occurred. */\n public readonly type: McpTransportFailure;\n /** The JSON-RPC method in flight when the failure occurred, if any. */\n public readonly method?: string;\n\n public constructor(message: string, options: McpTransportErrorOptions) {\n super(\"TOOL_EXEC_FAILED\", message, options);\n\n this.name = \"McpTransportError\";\n this.type = options.type;\n this.method = options.method;\n }\n}\n\n/**\n * Why a web tool (`ai.tools.webSearch` / `ai.tools.fetchUrl`) failed\n * before or during a network call.\n *\n * - `\"missing-peer\"` — an optional peer dependency the chosen mode needs\n * (`@mozilla/readability` + `jsdom` for text/markdown extraction, a\n * search provider SDK) is not installed. The message carries a curated\n * `npm install` string for the developer.\n * - `\"missing-key\"` — no API key was supplied via options or the\n * provider's environment variable.\n * - `\"denied-host\"` — the requested URL's host is not in the configured\n * `allowHosts` allowlist (an SSRF guardrail), rejected before any fetch.\n * - `\"invalid-url\"` — the supplied URL could not be parsed, or used a\n * non-`http(s)` scheme.\n * - `\"request-failed\"` — the network call itself failed (DNS, connection\n * reset, timeout) or the provider returned a non-OK status.\n */\nexport type WebToolFailure =\n | \"missing-peer\"\n | \"missing-key\"\n | \"denied-host\"\n | \"invalid-url\"\n | \"request-failed\";\n\n/**\n * Options for {@link WebToolError} — the structured `type` discriminator\n * so a caller can branch without parsing the message.\n */\nexport type WebToolErrorOptions = AIErrorOptions & {\n /** Which class of web-tool failure occurred. */\n type: WebToolFailure;\n};\n\n/**\n * A web tool failed — a missing optional peer, an absent API key, a host\n * rejected by the `allowHosts` guardrail, an unparseable URL, or a failed\n * network call.\n *\n * **Surface.** Thrown from inside a tool's `execute`, so the framework's\n * `tool()` wrapper catches it and surfaces it in the returned `{ error }`\n * field (`invoke()` never throws) — the agent reads the failure as data\n * and self-corrects rather than crashing. Extends the framework\n * {@link AIError} (category `\"tool\"` via code `TOOL_EXEC_FAILED`) so it\n * flows through the same typed error contract as every other AI error;\n * branch on `error.type` for the specific failure.\n *\n * @example\n * const { error } = await fetchTool.invoke({ url: \"http://evil.test\" });\n * if (error instanceof WebToolError && error.type === \"denied-host\") {\n * // the host was not in allowHosts — surfaced before any network call\n * }\n */\nexport class WebToolError extends AIError {\n /** Which class of web-tool failure occurred. */\n public readonly type: WebToolFailure;\n\n public constructor(message: string, options: WebToolErrorOptions) {\n super(\"TOOL_EXEC_FAILED\", message, options);\n\n this.name = \"WebToolError\";\n this.type = options.type;\n }\n}\n\n/**\n * Why an `http_request` call was rejected by its own guardrails, before\n * the network request was ever issued.\n *\n * - `\"method-not-allowed\"` — the model requested an HTTP method that is\n * not on the tool's `allowMethods` allowlist (defaults to `[\"GET\"]`).\n * - `\"host-not-allowed\"` — the resolved request host is not on the\n * tool's `allowHosts` allowlist (an SSRF guardrail).\n * - `\"invalid-url\"` — the supplied URL (or its join with `baseUrl`)\n * could not be parsed into an absolute `http(s)` URL.\n */\nexport type HttpPolicyFailure = \"method-not-allowed\" | \"host-not-allowed\" | \"invalid-url\";\n\n/**\n * Options for {@link HttpPolicyError} — the structured `type`\n * discriminator so a caller can branch without parsing the message.\n */\nexport type HttpPolicyErrorOptions = AIErrorOptions & {\n /** Which class of policy rejection occurred. */\n type: HttpPolicyFailure;\n};\n\n/**\n * The `http_request` tool refused a call its construction-time policy\n * does not permit — a disallowed method, a host outside the allowlist,\n * or an unparseable URL. The rejection happens *before* any network\n * request, so a guarded tool can never be coaxed into reaching an\n * off-allowlist host (an SSRF guardrail).\n *\n * **Surface.** Thrown from inside the tool's `execute`, so the framework's\n * `tool()` wrapper catches it and surfaces it in the returned `{ error }`\n * field (`invoke()` never throws) — the agent reads the typed failure as\n * data and self-corrects rather than crashing. Extends the framework\n * {@link AIError} (category `\"tool\"` via code `TOOL_EXEC_FAILED`) so it\n * flows through the same typed error contract as every other AI error;\n * branch on `error.type` for the specific failure.\n *\n * @example\n * const { error } = await httpTool.invoke({ url: \"https://evil.test\" });\n * if (error instanceof HttpPolicyError && error.type === \"host-not-allowed\") {\n * // the model tried to reach a host outside the configured allowlist\n * }\n */\nexport class HttpPolicyError extends AIError {\n /** Which class of policy rejection occurred. */\n public readonly type: HttpPolicyFailure;\n\n public constructor(message: string, options: HttpPolicyErrorOptions) {\n super(\"TOOL_EXEC_FAILED\", message, options);\n\n this.name = \"HttpPolicyError\";\n this.type = options.type;\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;AAuCA,IAAa,kBAAb,cAAqC,QAAQ;CAI3C,AAAO,YAAY,SAAiB,SAAiC;EACnE,MAAM,oBAAoB,SAAS,OAAO;EAE1C,KAAK,OAAO;EACZ,KAAK,OAAO,QAAQ;CACtB;AACF;;;;;;;;;;;;;;;;;AA0CA,IAAa,gBAAb,cAAmC,QAAQ;CAIzC,AAAO,YAAY,SAAiB,SAA+B;EACjE,MAAM,oBAAoB,SAAS,OAAO;EAE1C,KAAK,OAAO;EACZ,KAAK,OAAO,QAAQ;CACtB;AACF;;;;;;;;;;;;;;;;;;;;AA+CA,IAAa,oBAAb,cAAuC,QAAQ;CAM7C,AAAO,YAAY,SAAiB,SAAmC;EACrE,MAAM,oBAAoB,SAAS,OAAO;EAE1C,KAAK,OAAO;EACZ,KAAK,OAAO,QAAQ;EACpB,KAAK,SAAS,QAAQ;CACxB;AACF;;;;;;;;;;;;;;;;;;;;AAsDA,IAAa,eAAb,cAAkC,QAAQ;CAIxC,AAAO,YAAY,SAAiB,SAA8B;EAChE,MAAM,oBAAoB,SAAS,OAAO;EAE1C,KAAK,OAAO;EACZ,KAAK,OAAO,QAAQ;CACtB;AACF;;;;;;;;;;;;;;;;;;;;;;AA6CA,IAAa,kBAAb,cAAqC,QAAQ;CAI3C,AAAO,YAAY,SAAiB,SAAiC;EACnE,MAAM,oBAAoB,SAAS,OAAO;EAE1C,KAAK,OAAO;EACZ,KAAK,OAAO,QAAQ;CACtB;AACF"}
1
+ {"version":3,"file":"errors.mjs","names":[],"sources":["../../../../../../ai-tools/src/errors.ts"],"sourcesContent":["import { AIError, type AIErrorOptions } from \"@warlock.js/ai\";\n\n/**\n * Why the calculator rejected an expression.\n *\n * - `\"syntax\"` — the expression could not be tokenized or parsed\n * (an unknown character, a misplaced operator, unbalanced parens).\n * - `\"divide-by-zero\"` — evaluation divided (or took a modulo) by zero.\n * - `\"overflow\"` — the computed result was not a finite number.\n */\nexport type CalculatorFailure = \"syntax\" | \"divide-by-zero\" | \"overflow\";\n\n/**\n * Options for {@link CalculatorError} — the structured `type`\n * discriminator so a caller can branch without parsing the message.\n */\nexport type CalculatorErrorOptions = AIErrorOptions & {\n /** Which class of calculator failure occurred. */\n type: CalculatorFailure;\n};\n\n/**\n * The `calculator` tool could not evaluate an expression — it was not\n * valid arithmetic, divided by zero, or overflowed to a non-finite\n * value.\n *\n * **Surface.** Thrown inside the tool handler, where `tool()` wraps it\n * into a `ToolExecutionError` whose message is preserved verbatim and\n * reaches the model as `{ error }` data, so the agent self-corrects\n * rather than crashing. Extends the framework {@link AIError} (category\n * `\"tool\"` via code `TOOL_EXEC_FAILED`) so it flows through the same\n * typed error contract as every other AI error; branch on `error.type`\n * for the specific failure.\n *\n * @example\n * if (error instanceof CalculatorError && error.type === \"divide-by-zero\") {\n * // the expression divided by zero — ask the model to revise it\n * }\n */\nexport class CalculatorError extends AIError {\n /** Which class of calculator failure occurred. */\n public readonly type: CalculatorFailure;\n\n public constructor(message: string, options: CalculatorErrorOptions) {\n super(\"TOOL_EXEC_FAILED\", message, options);\n\n this.name = \"CalculatorError\";\n this.type = options.type;\n }\n}\n\n/**\n * Why the date-time tool rejected a call.\n *\n * - `\"invalid-input\"` — a required field for the chosen `op` was\n * missing or malformed (an unparsable ISO instant, a bad `amount`).\n * - `\"invalid-unit\"` — `unit` was not one of the supported units.\n * - `\"invalid-time-zone\"` — the IANA time zone was not recognized.\n * - `\"unsupported-op\"` — the `op` was not one this tool implements.\n */\nexport type DateTimeFailure =\n | \"invalid-input\"\n | \"invalid-unit\"\n | \"invalid-time-zone\"\n | \"unsupported-op\";\n\n/**\n * Options for {@link DateTimeError} — the structured `type`\n * discriminator so a caller can branch without parsing the message.\n */\nexport type DateTimeErrorOptions = AIErrorOptions & {\n /** Which class of date-time failure occurred. */\n type: DateTimeFailure;\n};\n\n/**\n * The `date_time` tool could not complete a call — a required field was\n * missing or malformed, the unit/time zone was unrecognized, or the\n * operation is unsupported.\n *\n * **Surface.** Thrown inside the tool handler, where `tool()` wraps it\n * into a `ToolExecutionError` whose message reaches the model as\n * `{ error }` data so the agent self-corrects. Extends the framework\n * {@link AIError} (category `\"tool\"` via code `TOOL_EXEC_FAILED`); branch\n * on `error.type` for the specific failure.\n *\n * @example\n * if (error instanceof DateTimeError && error.type === \"invalid-unit\") {\n * // the model passed an unknown unit — re-prompt with the allowed set\n * }\n */\nexport class DateTimeError extends AIError {\n /** Which class of date-time failure occurred. */\n public readonly type: DateTimeFailure;\n\n public constructor(message: string, options: DateTimeErrorOptions) {\n super(\"TOOL_EXEC_FAILED\", message, options);\n\n this.name = \"DateTimeError\";\n this.type = options.type;\n }\n}\n\n/**\n * Why an MCP transport operation failed.\n *\n * - `\"connect\"` — the transport could not be opened (child process\n * failed to spawn, HTTP endpoint unreachable) or the `initialize`\n * handshake failed.\n * - `\"protocol\"` — a malformed / unexpected JSON-RPC message, a\n * response that matched no in-flight request, or a missing field.\n * - `\"timeout\"` — a request exceeded its per-call deadline.\n * - `\"closed\"` — the transport was used after it was closed, or the\n * peer closed it mid-call.\n */\nexport type McpTransportFailure = \"connect\" | \"protocol\" | \"timeout\" | \"closed\";\n\n/**\n * Options for {@link McpTransportError} — the structured `type`\n * discriminator plus an optional JSON-RPC method name for branchable\n * diagnostics without parsing the message.\n */\nexport type McpTransportErrorOptions = AIErrorOptions & {\n /** Which class of transport failure occurred. */\n type: McpTransportFailure;\n /** The JSON-RPC method in flight when the failure occurred, if any. */\n method?: string;\n};\n\n/**\n * The MCP client's transport layer failed — it could not connect, the\n * peer spoke malformed JSON-RPC, a call timed out, or the transport was\n * already closed.\n *\n * **Surface.** Connection / handshake failures surface at\n * agent-construction time (the caller `await`s `client.tools()`). A\n * `tools/call` failure raised mid-run is wrapped by `tool()` into a\n * `ToolExecutionError` and reaches the model as `{ error }` data, so the\n * agent self-corrects rather than crashing. Extends the framework\n * {@link AIError} (category `\"tool\"`, code `TOOL_EXEC_FAILED`) so it\n * flows through the same typed error contract as every other AI error;\n * branch on `error.type` for the specific failure.\n *\n * @example\n * if (error instanceof McpTransportError && error.type === \"timeout\") {\n * // the remote call exceeded its deadline — retry or escalate\n * }\n */\nexport class McpTransportError extends AIError {\n /** Which class of transport failure occurred. */\n public readonly type: McpTransportFailure;\n /** The JSON-RPC method in flight when the failure occurred, if any. */\n public readonly method?: string;\n\n public constructor(message: string, options: McpTransportErrorOptions) {\n super(\"TOOL_EXEC_FAILED\", message, options);\n\n this.name = \"McpTransportError\";\n this.type = options.type;\n this.method = options.method;\n }\n}\n\n/**\n * Why a web tool (`ai.tools.webSearch` / `ai.tools.fetchUrl`) failed\n * before or during a network call.\n *\n * - `\"missing-peer\"` — an optional peer dependency the chosen mode needs\n * (`@mozilla/readability` + `jsdom` for text/markdown extraction, a\n * search provider SDK) is not installed. The message carries a curated\n * `npm install` string for the developer.\n * - `\"missing-key\"` — no API key was supplied via options or the\n * provider's environment variable.\n * - `\"denied-host\"` — the requested URL's host is not in the configured\n * `allowHosts` allowlist (an SSRF guardrail), rejected before any fetch.\n * - `\"invalid-url\"` — the supplied URL could not be parsed, or used a\n * non-`http(s)` scheme.\n * - `\"request-failed\"` — the network call itself failed (DNS, connection\n * reset, timeout) or the provider returned a non-OK status.\n */\nexport type WebToolFailure =\n | \"missing-peer\"\n | \"missing-key\"\n | \"denied-host\"\n | \"invalid-url\"\n | \"request-failed\";\n\n/**\n * Options for {@link WebToolError} — the structured `type` discriminator\n * so a caller can branch without parsing the message.\n */\nexport type WebToolErrorOptions = AIErrorOptions & {\n /** Which class of web-tool failure occurred. */\n type: WebToolFailure;\n};\n\n/**\n * A web tool failed — a missing optional peer, an absent API key, a host\n * rejected by the `allowHosts` guardrail, an unparseable URL, or a failed\n * network call.\n *\n * **Surface.** Thrown from inside a tool's `execute`, so the framework's\n * `tool()` wrapper catches it and surfaces it in the returned `{ error }`\n * field (`invoke()` never throws) — the agent reads the failure as data\n * and self-corrects rather than crashing. Extends the framework\n * {@link AIError} (category `\"tool\"` via code `TOOL_EXEC_FAILED`) so it\n * flows through the same typed error contract as every other AI error;\n * branch on `error.type` for the specific failure.\n *\n * @example\n * const { error } = await fetchTool.invoke({ url: \"http://evil.test\" });\n * if (error instanceof WebToolError && error.type === \"denied-host\") {\n * // the host was not in allowHosts — surfaced before any network call\n * }\n */\nexport class WebToolError extends AIError {\n /** Which class of web-tool failure occurred. */\n public readonly type: WebToolFailure;\n\n public constructor(message: string, options: WebToolErrorOptions) {\n super(\"TOOL_EXEC_FAILED\", message, options);\n\n this.name = \"WebToolError\";\n this.type = options.type;\n }\n}\n\n/**\n * Why an `http_request` call was rejected by its own guardrails, before\n * the network request was ever issued.\n *\n * - `\"method-not-allowed\"` — the model requested an HTTP method that is\n * not on the tool's `allowMethods` allowlist (defaults to `[\"GET\"]`).\n * - `\"host-not-allowed\"` — the resolved request host is not on the\n * tool's `allowHosts` allowlist (an SSRF guardrail).\n * - `\"invalid-url\"` — the supplied URL (or its join with `baseUrl`)\n * could not be parsed into an absolute `http(s)` URL.\n */\nexport type HttpPolicyFailure = \"method-not-allowed\" | \"host-not-allowed\" | \"invalid-url\";\n\n/**\n * Options for {@link HttpPolicyError} — the structured `type`\n * discriminator so a caller can branch without parsing the message.\n */\nexport type HttpPolicyErrorOptions = AIErrorOptions & {\n /** Which class of policy rejection occurred. */\n type: HttpPolicyFailure;\n};\n\n/**\n * The `http_request` tool refused a call its construction-time policy\n * does not permit — a disallowed method, a host outside the allowlist,\n * or an unparseable URL. The rejection happens *before* any network\n * request, so a guarded tool can never be coaxed into reaching an\n * off-allowlist host (an SSRF guardrail).\n *\n * **Surface.** Thrown from inside the tool's `execute`, so the framework's\n * `tool()` wrapper catches it and surfaces it in the returned `{ error }`\n * field (`invoke()` never throws) — the agent reads the typed failure as\n * data and self-corrects rather than crashing. Extends the framework\n * {@link AIError} (category `\"tool\"` via code `TOOL_EXEC_FAILED`) so it\n * flows through the same typed error contract as every other AI error;\n * branch on `error.type` for the specific failure.\n *\n * @example\n * const { error } = await httpTool.invoke({ url: \"https://evil.test\" });\n * if (error instanceof HttpPolicyError && error.type === \"host-not-allowed\") {\n * // the model tried to reach a host outside the configured allowlist\n * }\n */\nexport class HttpPolicyError extends AIError {\n /** Which class of policy rejection occurred. */\n public readonly type: HttpPolicyFailure;\n\n public constructor(message: string, options: HttpPolicyErrorOptions) {\n super(\"TOOL_EXEC_FAILED\", message, options);\n\n this.name = \"HttpPolicyError\";\n this.type = options.type;\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;AAuCA,IAAa,kBAAb,cAAqC,QAAQ;CAI3C,AAAO,YAAY,SAAiB,SAAiC;EACnE,MAAM,oBAAoB,SAAS,OAAO;EAE1C,KAAK,OAAO;EACZ,KAAK,OAAO,QAAQ;CACtB;AACF;;;;;;;;;;;;;;;;;AA0CA,IAAa,gBAAb,cAAmC,QAAQ;CAIzC,AAAO,YAAY,SAAiB,SAA+B;EACjE,MAAM,oBAAoB,SAAS,OAAO;EAE1C,KAAK,OAAO;EACZ,KAAK,OAAO,QAAQ;CACtB;AACF;;;;;;;;;;;;;;;;;;;;AA+CA,IAAa,oBAAb,cAAuC,QAAQ;CAM7C,AAAO,YAAY,SAAiB,SAAmC;EACrE,MAAM,oBAAoB,SAAS,OAAO;EAE1C,KAAK,OAAO;EACZ,KAAK,OAAO,QAAQ;EACpB,KAAK,SAAS,QAAQ;CACxB;AACF;;;;;;;;;;;;;;;;;;;;AAsDA,IAAa,eAAb,cAAkC,QAAQ;CAIxC,AAAO,YAAY,SAAiB,SAA8B;EAChE,MAAM,oBAAoB,SAAS,OAAO;EAE1C,KAAK,OAAO;EACZ,KAAK,OAAO,QAAQ;CACtB;AACF;;;;;;;;;;;;;;;;;;;;;;AA6CA,IAAa,kBAAb,cAAqC,QAAQ;CAI3C,AAAO,YAAY,SAAiB,SAAiC;EACnE,MAAM,oBAAoB,SAAS,OAAO;EAE1C,KAAK,OAAO;EACZ,KAAK,OAAO,QAAQ;CACtB;AACF"}
@@ -1,7 +1,7 @@
1
1
  import { HttpRequestInput, HttpRequestOptions, HttpRequestResult } from "../contracts/http.type.mjs";
2
2
  import { ToolContract } from "@warlock.js/ai";
3
3
 
4
- //#region ../@warlock.js/ai-tools/src/http/http-request.d.ts
4
+ //#region ../ai-tools/src/http/http-request.d.ts
5
5
  /**
6
6
  * Build the agent-facing `http_request` tool — a guarded HTTP/REST client
7
7
  * over the global `fetch`. The `options` bound what the model may do; the
@@ -1 +1 @@
1
- {"version":3,"file":"http-request.d.mts","names":[],"sources":["../../../../../../../@warlock.js/ai-tools/src/http/http-request.ts"],"mappings":";;;;;;;AAwMA;;;;;;;;;;;;;;;AAEmD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAFnC,eAAA,CACd,OAAA,GAAS,kBAAA,GACR,YAAA,CAAa,gBAAA,EAAkB,iBAAA"}
1
+ {"version":3,"file":"http-request.d.mts","names":[],"sources":["../../../../../../../ai-tools/src/http/http-request.ts"],"mappings":";;;;;;;AAwMA;;;;;;;;;;;;;;;AAEmD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAFnC,eAAA,CACd,OAAA,GAAS,kBAAA,GACR,YAAA,CAAa,gBAAA,EAAkB,iBAAA"}
@@ -2,7 +2,7 @@ import { HttpPolicyError } from "../errors.mjs";
2
2
  import { objectSchema, optionalStringEnumField, optionalStringRecordField, passthroughField, stringField } from "../schema.mjs";
3
3
  import { tool } from "@warlock.js/ai";
4
4
 
5
- //#region ../@warlock.js/ai-tools/src/http/http-request.ts
5
+ //#region ../ai-tools/src/http/http-request.ts
6
6
  /** Default tool name exposed to the LLM. */
7
7
  const DEFAULT_NAME = "http_request";
8
8
  /** Default per-request wall-clock timeout, in milliseconds. */
@@ -1 +1 @@
1
- {"version":3,"file":"http-request.mjs","names":[],"sources":["../../../../../../../@warlock.js/ai-tools/src/http/http-request.ts"],"sourcesContent":["import { type ToolContract, tool } from \"@warlock.js/ai\";\nimport type {\n HttpMethod,\n HttpRequestInput,\n HttpRequestOptions,\n HttpRequestResult,\n} from \"../contracts\";\nimport { HttpPolicyError } from \"../errors\";\nimport {\n objectSchema,\n optionalStringEnumField,\n optionalStringRecordField,\n passthroughField,\n stringField,\n} from \"../schema\";\n\n/** Default tool name exposed to the LLM. */\nconst DEFAULT_NAME = \"http_request\";\n\n/** Default per-request wall-clock timeout, in milliseconds. */\nconst DEFAULT_TIMEOUT_MS = 15_000;\n\n/** Default hard cap on response-body bytes before truncation. */\nconst DEFAULT_MAX_BYTES = 1_000_000;\n\n/** The full set of HTTP methods, in the order they appear in {@link HttpMethod}. */\nconst ALL_METHODS: readonly HttpMethod[] = [\"GET\", \"POST\", \"PUT\", \"PATCH\", \"DELETE\"];\n\n/** Methods that conventionally carry no request body — `body` is dropped for these. */\nconst BODYLESS_METHODS: ReadonlySet<HttpMethod> = new Set<HttpMethod>([\"GET\"]);\n\n/**\n * Standard Schema for {@link HttpRequestInput}. `url` is required;\n * `method` is constrained to the canonical HTTP verb set (further\n * narrowed to the tool's `allowMethods` at runtime); `headers` is an\n * optional string-to-string record; `body` is an opaque passthrough the\n * handler serializes based on its runtime type.\n */\nconst httpRequestInputSchema = objectSchema<HttpRequestInput>({\n method: optionalStringEnumField<HttpMethod>(ALL_METHODS),\n url: stringField(),\n headers: optionalStringRecordField(),\n body: passthroughField(),\n});\n\n/**\n * Resolve the request target. With a `baseUrl` configured the model\n * supplies a path joined against it; otherwise the model's `url` must be\n * an absolute `http(s)` URL. Throws a typed {@link HttpPolicyError} of\n * type `\"invalid-url\"` when the result cannot be parsed or is not an\n * `http`/`https` URL — surfaced as `{ error }` data, never a crash.\n */\nfunction resolveUrl(rawUrl: string, baseUrl: string | undefined): URL {\n let resolved: URL;\n\n try {\n // `new URL(input, base)` joins relative paths against `base` and\n // ignores `base` when `input` is already absolute, which is exactly\n // the \"path vs full URL\" behavior the design specifies.\n resolved = baseUrl !== undefined ? new URL(rawUrl, baseUrl) : new URL(rawUrl);\n } catch {\n throw new HttpPolicyError(\n `http_request could not resolve a valid URL from \"${rawUrl}\"` +\n (baseUrl !== undefined ? ` against base \"${baseUrl}\".` : \".\"),\n { type: \"invalid-url\" },\n );\n }\n\n if (resolved.protocol !== \"http:\" && resolved.protocol !== \"https:\") {\n throw new HttpPolicyError(\n `http_request only permits http(s) URLs; got \"${resolved.protocol}\".`,\n { type: \"invalid-url\" },\n );\n }\n\n return resolved;\n}\n\n/**\n * Read a `Response` body, capping at `maxBytes`. Returns the decoded text\n * and whether it was cut off. Streams chunk-by-chunk so an oversized body\n * is abandoned at the cap rather than fully buffered; falls back to\n * `response.text()` (then a post-hoc byte slice) when the body is not a\n * readable stream (e.g. a stubbed `Response` in tests).\n */\nasync function readCappedBody(\n response: Response,\n maxBytes: number,\n): Promise<{ text: string; truncated: boolean }> {\n const body = response.body;\n\n if (!body) {\n return { text: \"\", truncated: false };\n }\n\n const decoder = new TextDecoder();\n const reader = body.getReader();\n let received = 0;\n let truncated = false;\n let text = \"\";\n\n try {\n for (;;) {\n const { done, value } = await reader.read();\n\n if (done) {\n break;\n }\n\n if (!value) {\n continue;\n }\n\n const remaining = maxBytes - received;\n\n if (value.byteLength > remaining) {\n text += decoder.decode(value.subarray(0, remaining), { stream: true });\n received = maxBytes;\n truncated = true;\n break;\n }\n\n text += decoder.decode(value, { stream: true });\n received += value.byteLength;\n }\n } finally {\n // Release the lock and abandon any unread remainder.\n await reader.cancel().catch(() => undefined);\n reader.releaseLock();\n }\n\n text += decoder.decode();\n\n return { text, truncated };\n}\n\n/**\n * Decide whether a response's `content-type` indicates JSON. Matches\n * `application/json` and the `+json` structured-suffix convention\n * (e.g. `application/vnd.api+json`), case-insensitively.\n */\nfunction isJsonContentType(contentType: string | undefined): boolean {\n if (!contentType) {\n return false;\n }\n\n const value = contentType.toLowerCase();\n\n return value.includes(\"application/json\") || value.includes(\"+json\");\n}\n\n/**\n * Build the agent-facing `http_request` tool — a guarded HTTP/REST client\n * over the global `fetch`. The `options` bound what the model may do; the\n * model supplies the per-call URL / method / headers / body within those\n * rails.\n *\n * **Guardrails (all enforced before the network call).**\n * - **Method allowlist** — `allowMethods` (default `[\"GET\"]`). A method\n * outside the list is rejected with a typed\n * {@link HttpPolicyError} (`type: \"method-not-allowed\"`).\n * - **Host allowlist** — when `allowHosts` is set, any other host is\n * rejected (`type: \"host-not-allowed\"`), an SSRF guardrail.\n * - **`baseUrl` join** — when configured, the model passes a path that\n * is resolved against `baseUrl`; otherwise it must pass an absolute\n * `http(s)` URL. An unresolvable URL is rejected\n * (`type: \"invalid-url\"`).\n *\n * **Request shaping.** Static `options.headers` are merged under the\n * per-call `headers` (the per-call value wins). An object `body` is\n * JSON-serialized with a `content-type: application/json` default; a\n * string `body` is sent verbatim; `body` is dropped for bodyless methods\n * (`GET`). The call is bounded by `timeoutMs` (default `15_000`) via an\n * `AbortController`, also wired to `ctx.signal` for cooperative\n * cancellation.\n *\n * **Response shaping.** Headers are returned with lower-cased keys. The\n * body is read up to `maxBytes` (default `1_000_000`) and JSON-parsed\n * when the response `content-type` is JSON, otherwise returned as text;\n * `truncated` is `true` when the body was cut off at the cap (a truncated\n * JSON body is returned as the raw partial string, since it can no longer\n * be parsed).\n *\n * **Errors flow as data.** Every guardrail rejection and network failure\n * is thrown inside `execute`; the framework's `tool()` wrapper catches it\n * and surfaces it in the returned `{ error }` field, so the agent reads\n * the failure and self-corrects rather than crashing.\n *\n * @param options - Construction-time policy bounding the tool.\n * @returns A {@link ToolContract} the agent can call as `http_request`.\n *\n * @example\n * const stripe = httpRequestTool({\n * baseUrl: \"https://api.stripe.com\",\n * allowHosts: [\"api.stripe.com\"],\n * allowMethods: [\"GET\", \"POST\"],\n * headers: { authorization: `Bearer ${process.env.STRIPE_KEY}` },\n * });\n * const { data } = await stripe.invoke({ method: \"GET\", url: \"/v1/charges\" });\n */\nexport function httpRequestTool(\n options: HttpRequestOptions = {},\n): ToolContract<HttpRequestInput, HttpRequestResult> {\n const allowMethods = options.allowMethods ?? [\"GET\"];\n const allowedMethodSet = new Set<HttpMethod>(allowMethods);\n const allowHostSet = options.allowHosts ? new Set(options.allowHosts) : undefined;\n const timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;\n const maxBytes = options.maxBytes ?? DEFAULT_MAX_BYTES;\n const staticHeaders = options.headers;\n\n return tool<HttpRequestInput, HttpRequestResult>({\n name: options.name ?? DEFAULT_NAME,\n description:\n \"Issue an HTTP request and return the status, response headers, and \" +\n \"parsed body. Allowed methods and hosts are restricted by the tool's \" +\n \"configuration; a request outside those rails is rejected before any \" +\n \"network call. Pass an object body to send JSON, or a string to send \" +\n \"it verbatim. The response body is JSON-parsed when the content-type \" +\n \"is JSON, otherwise returned as text, and is capped — `truncated` is \" +\n \"true when the body was cut off.\",\n action: (input) => `Requesting ${input.method ?? \"GET\"} ${input.url}`,\n input: httpRequestInputSchema,\n async execute(input, ctx) {\n const method: HttpMethod = input.method ?? \"GET\";\n\n // 1. Method allowlist — rejected before anything else.\n if (!allowedMethodSet.has(method)) {\n throw new HttpPolicyError(\n `http_request method \"${method}\" is not allowed. ` +\n `Permitted methods: ${[...allowedMethodSet].join(\", \")}.`,\n { type: \"method-not-allowed\" },\n );\n }\n\n // 2. URL resolution (baseUrl join when configured).\n const url = resolveUrl(input.url, options.baseUrl);\n\n // 3. Host allowlist — SSRF guardrail, before the fetch.\n if (allowHostSet && !allowHostSet.has(url.hostname)) {\n throw new HttpPolicyError(\n `http_request host \"${url.hostname}\" is not in the allowlist. ` +\n `Permitted hosts: ${[...allowHostSet].join(\", \")}.`,\n { type: \"host-not-allowed\" },\n );\n }\n\n // 4. Merge headers — static option headers under the per-call ones,\n // so a per-call header overrides a static default of the same name.\n const headers: Record<string, string> = { ...staticHeaders, ...input.headers };\n\n // 5. Shape the body. Dropped for bodyless methods; objects become\n // JSON (with a default content-type); strings are sent verbatim.\n let body: string | undefined;\n\n if (!BODYLESS_METHODS.has(method) && input.body !== undefined) {\n if (typeof input.body === \"string\") {\n body = input.body;\n } else {\n body = JSON.stringify(input.body);\n\n const hasContentType = Object.keys(headers).some(\n (key) => key.toLowerCase() === \"content-type\",\n );\n\n if (!hasContentType) {\n headers[\"content-type\"] = \"application/json\";\n }\n }\n }\n\n // 6. Bound the call by timeout, chained to the caller's signal.\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), timeoutMs);\n\n const onAbort = () => controller.abort();\n\n if (ctx?.signal) {\n if (ctx.signal.aborted) {\n controller.abort();\n } else {\n ctx.signal.addEventListener(\"abort\", onAbort, { once: true });\n }\n }\n\n let response: Response;\n\n try {\n response = await fetch(url, { method, headers, body, signal: controller.signal });\n } finally {\n clearTimeout(timer);\n ctx?.signal?.removeEventListener(\"abort\", onAbort);\n }\n\n // 7. Collect response headers with lower-cased keys.\n const responseHeaders: Record<string, string> = {};\n response.headers.forEach((value, key) => {\n responseHeaders[key.toLowerCase()] = value;\n });\n\n // 8. Read the body up to the cap, then parse-or-pass.\n const { text, truncated } = await readCappedBody(response, maxBytes);\n\n let parsedBody: unknown = text;\n\n // A truncated body can no longer be valid JSON, so only attempt a\n // parse on a complete JSON response; otherwise hand back the raw text.\n if (!truncated && isJsonContentType(responseHeaders[\"content-type\"]) && text.length > 0) {\n try {\n parsedBody = JSON.parse(text);\n } catch {\n // Content-type claimed JSON but the body was not — fall back to\n // the raw text rather than failing the whole call.\n parsedBody = text;\n }\n }\n\n return {\n status: response.status,\n headers: responseHeaders,\n body: parsedBody,\n truncated,\n };\n },\n });\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"http-request.mjs","names":[],"sources":["../../../../../../../ai-tools/src/http/http-request.ts"],"sourcesContent":["import { type ToolContract, tool } from \"@warlock.js/ai\";\nimport type {\n HttpMethod,\n HttpRequestInput,\n HttpRequestOptions,\n HttpRequestResult,\n} from \"../contracts\";\nimport { HttpPolicyError } from \"../errors\";\nimport {\n objectSchema,\n optionalStringEnumField,\n optionalStringRecordField,\n passthroughField,\n stringField,\n} from \"../schema\";\n\n/** Default tool name exposed to the LLM. */\nconst DEFAULT_NAME = \"http_request\";\n\n/** Default per-request wall-clock timeout, in milliseconds. */\nconst DEFAULT_TIMEOUT_MS = 15_000;\n\n/** Default hard cap on response-body bytes before truncation. */\nconst DEFAULT_MAX_BYTES = 1_000_000;\n\n/** The full set of HTTP methods, in the order they appear in {@link HttpMethod}. */\nconst ALL_METHODS: readonly HttpMethod[] = [\"GET\", \"POST\", \"PUT\", \"PATCH\", \"DELETE\"];\n\n/** Methods that conventionally carry no request body — `body` is dropped for these. */\nconst BODYLESS_METHODS: ReadonlySet<HttpMethod> = new Set<HttpMethod>([\"GET\"]);\n\n/**\n * Standard Schema for {@link HttpRequestInput}. `url` is required;\n * `method` is constrained to the canonical HTTP verb set (further\n * narrowed to the tool's `allowMethods` at runtime); `headers` is an\n * optional string-to-string record; `body` is an opaque passthrough the\n * handler serializes based on its runtime type.\n */\nconst httpRequestInputSchema = objectSchema<HttpRequestInput>({\n method: optionalStringEnumField<HttpMethod>(ALL_METHODS),\n url: stringField(),\n headers: optionalStringRecordField(),\n body: passthroughField(),\n});\n\n/**\n * Resolve the request target. With a `baseUrl` configured the model\n * supplies a path joined against it; otherwise the model's `url` must be\n * an absolute `http(s)` URL. Throws a typed {@link HttpPolicyError} of\n * type `\"invalid-url\"` when the result cannot be parsed or is not an\n * `http`/`https` URL — surfaced as `{ error }` data, never a crash.\n */\nfunction resolveUrl(rawUrl: string, baseUrl: string | undefined): URL {\n let resolved: URL;\n\n try {\n // `new URL(input, base)` joins relative paths against `base` and\n // ignores `base` when `input` is already absolute, which is exactly\n // the \"path vs full URL\" behavior the design specifies.\n resolved = baseUrl !== undefined ? new URL(rawUrl, baseUrl) : new URL(rawUrl);\n } catch {\n throw new HttpPolicyError(\n `http_request could not resolve a valid URL from \"${rawUrl}\"` +\n (baseUrl !== undefined ? ` against base \"${baseUrl}\".` : \".\"),\n { type: \"invalid-url\" },\n );\n }\n\n if (resolved.protocol !== \"http:\" && resolved.protocol !== \"https:\") {\n throw new HttpPolicyError(\n `http_request only permits http(s) URLs; got \"${resolved.protocol}\".`,\n { type: \"invalid-url\" },\n );\n }\n\n return resolved;\n}\n\n/**\n * Read a `Response` body, capping at `maxBytes`. Returns the decoded text\n * and whether it was cut off. Streams chunk-by-chunk so an oversized body\n * is abandoned at the cap rather than fully buffered; falls back to\n * `response.text()` (then a post-hoc byte slice) when the body is not a\n * readable stream (e.g. a stubbed `Response` in tests).\n */\nasync function readCappedBody(\n response: Response,\n maxBytes: number,\n): Promise<{ text: string; truncated: boolean }> {\n const body = response.body;\n\n if (!body) {\n return { text: \"\", truncated: false };\n }\n\n const decoder = new TextDecoder();\n const reader = body.getReader();\n let received = 0;\n let truncated = false;\n let text = \"\";\n\n try {\n for (;;) {\n const { done, value } = await reader.read();\n\n if (done) {\n break;\n }\n\n if (!value) {\n continue;\n }\n\n const remaining = maxBytes - received;\n\n if (value.byteLength > remaining) {\n text += decoder.decode(value.subarray(0, remaining), { stream: true });\n received = maxBytes;\n truncated = true;\n break;\n }\n\n text += decoder.decode(value, { stream: true });\n received += value.byteLength;\n }\n } finally {\n // Release the lock and abandon any unread remainder.\n await reader.cancel().catch(() => undefined);\n reader.releaseLock();\n }\n\n text += decoder.decode();\n\n return { text, truncated };\n}\n\n/**\n * Decide whether a response's `content-type` indicates JSON. Matches\n * `application/json` and the `+json` structured-suffix convention\n * (e.g. `application/vnd.api+json`), case-insensitively.\n */\nfunction isJsonContentType(contentType: string | undefined): boolean {\n if (!contentType) {\n return false;\n }\n\n const value = contentType.toLowerCase();\n\n return value.includes(\"application/json\") || value.includes(\"+json\");\n}\n\n/**\n * Build the agent-facing `http_request` tool — a guarded HTTP/REST client\n * over the global `fetch`. The `options` bound what the model may do; the\n * model supplies the per-call URL / method / headers / body within those\n * rails.\n *\n * **Guardrails (all enforced before the network call).**\n * - **Method allowlist** — `allowMethods` (default `[\"GET\"]`). A method\n * outside the list is rejected with a typed\n * {@link HttpPolicyError} (`type: \"method-not-allowed\"`).\n * - **Host allowlist** — when `allowHosts` is set, any other host is\n * rejected (`type: \"host-not-allowed\"`), an SSRF guardrail.\n * - **`baseUrl` join** — when configured, the model passes a path that\n * is resolved against `baseUrl`; otherwise it must pass an absolute\n * `http(s)` URL. An unresolvable URL is rejected\n * (`type: \"invalid-url\"`).\n *\n * **Request shaping.** Static `options.headers` are merged under the\n * per-call `headers` (the per-call value wins). An object `body` is\n * JSON-serialized with a `content-type: application/json` default; a\n * string `body` is sent verbatim; `body` is dropped for bodyless methods\n * (`GET`). The call is bounded by `timeoutMs` (default `15_000`) via an\n * `AbortController`, also wired to `ctx.signal` for cooperative\n * cancellation.\n *\n * **Response shaping.** Headers are returned with lower-cased keys. The\n * body is read up to `maxBytes` (default `1_000_000`) and JSON-parsed\n * when the response `content-type` is JSON, otherwise returned as text;\n * `truncated` is `true` when the body was cut off at the cap (a truncated\n * JSON body is returned as the raw partial string, since it can no longer\n * be parsed).\n *\n * **Errors flow as data.** Every guardrail rejection and network failure\n * is thrown inside `execute`; the framework's `tool()` wrapper catches it\n * and surfaces it in the returned `{ error }` field, so the agent reads\n * the failure and self-corrects rather than crashing.\n *\n * @param options - Construction-time policy bounding the tool.\n * @returns A {@link ToolContract} the agent can call as `http_request`.\n *\n * @example\n * const stripe = httpRequestTool({\n * baseUrl: \"https://api.stripe.com\",\n * allowHosts: [\"api.stripe.com\"],\n * allowMethods: [\"GET\", \"POST\"],\n * headers: { authorization: `Bearer ${process.env.STRIPE_KEY}` },\n * });\n * const { data } = await stripe.invoke({ method: \"GET\", url: \"/v1/charges\" });\n */\nexport function httpRequestTool(\n options: HttpRequestOptions = {},\n): ToolContract<HttpRequestInput, HttpRequestResult> {\n const allowMethods = options.allowMethods ?? [\"GET\"];\n const allowedMethodSet = new Set<HttpMethod>(allowMethods);\n const allowHostSet = options.allowHosts ? new Set(options.allowHosts) : undefined;\n const timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;\n const maxBytes = options.maxBytes ?? DEFAULT_MAX_BYTES;\n const staticHeaders = options.headers;\n\n return tool<HttpRequestInput, HttpRequestResult>({\n name: options.name ?? DEFAULT_NAME,\n description:\n \"Issue an HTTP request and return the status, response headers, and \" +\n \"parsed body. Allowed methods and hosts are restricted by the tool's \" +\n \"configuration; a request outside those rails is rejected before any \" +\n \"network call. Pass an object body to send JSON, or a string to send \" +\n \"it verbatim. The response body is JSON-parsed when the content-type \" +\n \"is JSON, otherwise returned as text, and is capped — `truncated` is \" +\n \"true when the body was cut off.\",\n action: (input) => `Requesting ${input.method ?? \"GET\"} ${input.url}`,\n input: httpRequestInputSchema,\n async execute(input, ctx) {\n const method: HttpMethod = input.method ?? \"GET\";\n\n // 1. Method allowlist — rejected before anything else.\n if (!allowedMethodSet.has(method)) {\n throw new HttpPolicyError(\n `http_request method \"${method}\" is not allowed. ` +\n `Permitted methods: ${[...allowedMethodSet].join(\", \")}.`,\n { type: \"method-not-allowed\" },\n );\n }\n\n // 2. URL resolution (baseUrl join when configured).\n const url = resolveUrl(input.url, options.baseUrl);\n\n // 3. Host allowlist — SSRF guardrail, before the fetch.\n if (allowHostSet && !allowHostSet.has(url.hostname)) {\n throw new HttpPolicyError(\n `http_request host \"${url.hostname}\" is not in the allowlist. ` +\n `Permitted hosts: ${[...allowHostSet].join(\", \")}.`,\n { type: \"host-not-allowed\" },\n );\n }\n\n // 4. Merge headers — static option headers under the per-call ones,\n // so a per-call header overrides a static default of the same name.\n const headers: Record<string, string> = { ...staticHeaders, ...input.headers };\n\n // 5. Shape the body. Dropped for bodyless methods; objects become\n // JSON (with a default content-type); strings are sent verbatim.\n let body: string | undefined;\n\n if (!BODYLESS_METHODS.has(method) && input.body !== undefined) {\n if (typeof input.body === \"string\") {\n body = input.body;\n } else {\n body = JSON.stringify(input.body);\n\n const hasContentType = Object.keys(headers).some(\n (key) => key.toLowerCase() === \"content-type\",\n );\n\n if (!hasContentType) {\n headers[\"content-type\"] = \"application/json\";\n }\n }\n }\n\n // 6. Bound the call by timeout, chained to the caller's signal.\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), timeoutMs);\n\n const onAbort = () => controller.abort();\n\n if (ctx?.signal) {\n if (ctx.signal.aborted) {\n controller.abort();\n } else {\n ctx.signal.addEventListener(\"abort\", onAbort, { once: true });\n }\n }\n\n let response: Response;\n\n try {\n response = await fetch(url, { method, headers, body, signal: controller.signal });\n } finally {\n clearTimeout(timer);\n ctx?.signal?.removeEventListener(\"abort\", onAbort);\n }\n\n // 7. Collect response headers with lower-cased keys.\n const responseHeaders: Record<string, string> = {};\n response.headers.forEach((value, key) => {\n responseHeaders[key.toLowerCase()] = value;\n });\n\n // 8. Read the body up to the cap, then parse-or-pass.\n const { text, truncated } = await readCappedBody(response, maxBytes);\n\n let parsedBody: unknown = text;\n\n // A truncated body can no longer be valid JSON, so only attempt a\n // parse on a complete JSON response; otherwise hand back the raw text.\n if (!truncated && isJsonContentType(responseHeaders[\"content-type\"]) && text.length > 0) {\n try {\n parsedBody = JSON.parse(text);\n } catch {\n // Content-type claimed JSON but the body was not — fall back to\n // the raw text rather than failing the whole call.\n parsedBody = text;\n }\n }\n\n return {\n status: response.status,\n headers: responseHeaders,\n body: parsedBody,\n truncated,\n };\n },\n });\n}\n"],"mappings":"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"}
@@ -3,7 +3,7 @@ import { jsonSchemaToStandard } from "./json-schema-to-standard.mjs";
3
3
  import { createJsonRpcClient } from "./transport.mjs";
4
4
  import { tool } from "@warlock.js/ai";
5
5
 
6
- //#region ../@warlock.js/ai-tools/src/mcp/client.ts
6
+ //#region ../ai-tools/src/mcp/client.ts
7
7
  /** Default per-call timeout for `tools/call`. */
8
8
  const DEFAULT_CALL_TIMEOUT_MS = 3e4;
9
9
  /** The MCP protocol version this client advertises in `initialize`. */
@@ -1 +1 @@
1
- {"version":3,"file":"client.mjs","names":[],"sources":["../../../../../../../@warlock.js/ai-tools/src/mcp/client.ts"],"sourcesContent":["import { tool, type ToolContract } from \"@warlock.js/ai\";\nimport type {\n McpClient,\n McpClientOptions,\n McpContentBlock,\n McpToolCallResult,\n McpToolDescriptor,\n McpTransport,\n} from \"../contracts\";\nimport { McpTransportError } from \"../errors\";\nimport { jsonSchemaToStandard } from \"./json-schema-to-standard\";\nimport { createJsonRpcClient, type JsonRpcClientHandle } from \"./transport\";\nimport type { McpTransportClient } from \"./transport.type\";\n\n/** Default per-call timeout for `tools/call`. */\nconst DEFAULT_CALL_TIMEOUT_MS = 30_000;\n\n/** The MCP protocol version this client advertises in `initialize`. */\nconst PROTOCOL_VERSION = \"2025-06-18\";\n\n/** The `tools/list` response slice we read. */\ninterface ToolsListResult {\n tools?: McpToolDescriptor[];\n}\n\n/**\n * The internal {@link McpClient} implementation. Owns one JSON-RPC client\n * over a transport, runs the `initialize` handshake on first use, lists\n * the server's tools, and adapts each into a {@link ToolContract} whose\n * `execute` issues `tools/call`. The adapted contracts are cached after\n * the first `tools()` so repeat calls don't re-handshake.\n *\n * Constructed via {@link mcp}; the class itself is internal.\n */\nclass McpClientImpl implements McpClient {\n /** The JSON-RPC client over the transport. */\n private readonly rpc: JsonRpcClientHandle;\n\n /** Construction-time options (prefix / filter / timeout). */\n private readonly options: McpClientOptions;\n\n /** Resolved + cached adapted tools, set after the first `tools()`. */\n private cached: ToolContract[] | undefined;\n\n /** In-flight `tools()` so concurrent callers share one handshake. */\n private pending: Promise<ToolContract[]> | undefined;\n\n /** Flipped once the handshake completes so we only do it once. */\n private initialized = false;\n\n public constructor(\n source: McpTransport | McpTransportClient,\n options: McpClientOptions = {},\n ) {\n this.rpc = createJsonRpcClient(source);\n this.options = options;\n }\n\n /**\n * Run the MCP `initialize` handshake exactly once, then send the\n * `notifications/initialized` notification the protocol requires before\n * any other request. Wraps a handshake failure as a typed\n * {@link McpTransportError} of type `\"connect\"`.\n */\n private async handshake(): Promise<void> {\n if (this.initialized) {\n return;\n }\n\n try {\n await this.rpc.call(\"initialize\", {\n protocolVersion: PROTOCOL_VERSION,\n capabilities: {},\n clientInfo: { name: \"@warlock.js/ai-tools\", version: \"4.4.0\" },\n });\n\n await this.rpc.notify(\"notifications/initialized\");\n } catch (cause) {\n if (cause instanceof McpTransportError) {\n throw cause;\n }\n\n const message = cause instanceof Error ? cause.message : String(cause);\n\n throw new McpTransportError(`MCP initialize handshake failed: ${message}`, {\n type: \"connect\",\n method: \"initialize\",\n cause,\n });\n }\n\n this.initialized = true;\n }\n\n public tools(): Promise<ToolContract[]> {\n if (this.cached) {\n return Promise.resolve(this.cached);\n }\n\n if (this.pending) {\n return this.pending;\n }\n\n this.pending = this.listAndAdapt()\n .then((tools) => {\n this.cached = tools;\n\n return tools;\n })\n .finally(() => {\n this.pending = undefined;\n });\n\n return this.pending;\n }\n\n /**\n * Handshake, `tools/list`, and adapt each descriptor into a\n * {@link ToolContract}, applying the `filter` and `namePrefix` options.\n */\n private async listAndAdapt(): Promise<ToolContract[]> {\n await this.handshake();\n\n const result = await this.rpc.call<ToolsListResult>(\"tools/list\");\n const descriptors = result.tools ?? [];\n\n const filter = this.options.filter;\n const selected = filter ? descriptors.filter((d) => filter(d.name)) : descriptors;\n\n return selected.map((descriptor) => this.adapt(descriptor));\n }\n\n /**\n * Adapt one remote tool descriptor into a {@link ToolContract}: build\n * the input schema from its JSON Schema via {@link jsonSchemaToStandard},\n * prefix the name, and route `execute` through a `tools/call` that\n * honors `ctx.signal`, unwraps the content blocks, and throws on an\n * `isError` result so `tool()` wraps it.\n */\n private adapt(descriptor: McpToolDescriptor): ToolContract {\n const prefixedName = `${this.options.namePrefix ?? \"\"}${descriptor.name}`;\n const remoteName = descriptor.name;\n const timeoutMs = this.options.timeoutMs ?? DEFAULT_CALL_TIMEOUT_MS;\n const input = jsonSchemaToStandard(descriptor.inputSchema);\n\n return tool<unknown, unknown>({\n name: prefixedName,\n description:\n descriptor.description ?? `Invoke the remote MCP tool \"${remoteName}\".`,\n input,\n execute: async (args, ctx) => {\n const result = await this.rpc.call<McpToolCallResult>(\n \"tools/call\",\n { name: remoteName, arguments: args ?? {} },\n { signal: ctx?.signal, timeoutMs },\n );\n\n // An `isError` result is a tool-level failure — throw it so the\n // surrounding `tool()` wraps it as a `ToolExecutionError` and the\n // agent reads it as `{ error }` data and self-corrects.\n if (result.isError) {\n throw new McpTransportError(\n `MCP tool \"${remoteName}\" returned an error: ${unwrapContent(result.content)}`,\n { type: \"protocol\", method: \"tools/call\" },\n );\n }\n\n return unwrapResult(result.content);\n },\n });\n }\n\n public close(): Promise<void> {\n return this.rpc.close();\n }\n}\n\n/**\n * Flatten an MCP `tools/call` result's content blocks into the value a\n * tool returns. Text blocks are concatenated; a single block whose text is\n * valid JSON is parsed so structured tool output flows back as an object\n * rather than a string. Non-text blocks are preserved as `type`-tagged\n * objects (MCP's wire `type` is kept; any inbound `kind` is normalized to\n * `type`).\n */\nfunction unwrapResult(content: McpContentBlock[] | undefined): unknown {\n const blocks = content ?? [];\n\n // The overwhelmingly common case: a single text block. Parse JSON when\n // it is one, so structured results come back typed; otherwise the string.\n if (blocks.length === 1 && blocks[0].type === \"text\") {\n const text = blocks[0].text ?? \"\";\n\n return tryParseJson(text);\n }\n\n // Multiple / mixed blocks: return a normalized array, each tagged by\n // `type` (never `kind`).\n return blocks.map((block) => normalizeBlock(block));\n}\n\n/**\n * Render content blocks to a short human string for error messages — the\n * concatenated text of every text block.\n */\nfunction unwrapContent(content: McpContentBlock[] | undefined): string {\n return (content ?? [])\n .filter((block) => block.type === \"text\" && typeof block.text === \"string\")\n .map((block) => block.text)\n .join(\" \")\n .trim();\n}\n\n/**\n * Normalize one content block onto our `type`-only shape: translate an\n * inbound `kind` discriminator to `type` (and strip `kind`) so the value a\n * tool returns never carries MCP's `kind` vocabulary.\n */\nfunction normalizeBlock(block: McpContentBlock): Record<string, unknown> {\n const { kind, ...rest } = block as McpContentBlock & { kind?: string };\n const type = block.type ?? kind ?? \"unknown\";\n\n return { ...rest, type };\n}\n\n/**\n * Parse a string as JSON, returning the parsed value on success or the\n * original string when it is not JSON — so a plain-text tool result stays\n * a string while a JSON tool result becomes an object.\n */\nfunction tryParseJson(text: string): unknown {\n const trimmed = text.trim();\n\n if (!trimmed) {\n return text;\n }\n\n const first = trimmed[0];\n\n // Only attempt a parse for plausibly-structured payloads, so a bare\n // sentence isn't mangled by a lenient parse.\n if (first !== \"{\" && first !== \"[\") {\n return text;\n }\n\n try {\n return JSON.parse(trimmed);\n } catch {\n return text;\n }\n}\n\n/**\n * Connect to an external MCP server and adapt its tools as agent tools\n * (Direction A: server → local agent tools).\n *\n * Opens the transport lazily and exposes {@link McpClient.tools}, which on\n * first call runs the `initialize` handshake, lists the server's tools via\n * `tools/list`, and maps each into a {@link ToolContract}:\n * - **input schema** — the remote tool's JSON Schema is wrapped as a\n * Standard Schema via {@link jsonSchemaToStandard} (Ajv-backed, an\n * optional peer);\n * - **execute** — issues `tools/call` honoring `ctx.signal` and the\n * configured `timeoutMs`, unwraps the result content, and throws on an\n * `isError` result so the `tool()` wrapper surfaces it as `{ error }`;\n * - **name** — prefixed with `options.namePrefix` to avoid local\n * collisions; only tools passing `options.filter` are adapted.\n *\n * The adapted contracts are cached after the first `tools()` call, so the\n * handshake + list happen exactly once. The returned contracts drop\n * straight into `ai.agent({ tools: [...] })`.\n *\n * @param server - The transport config (`{ type: \"stdio\" }` /\n * `{ type: \"http\" }`). A pre-built transport client may be injected for\n * testing.\n * @param options - Prefix / filter / per-call timeout.\n * @returns An {@link McpClient} handle.\n *\n * @example\n * const github = mcp(\n * { type: \"stdio\", command: \"npx\", args: [\"-y\", \"@modelcontextprotocol/server-github\"] },\n * { namePrefix: \"github.\" },\n * );\n * const dev = ai.agent({ model, tools: [...(await github.tools())] });\n */\nexport function mcp(\n server: McpTransport | McpTransportClient,\n options?: McpClientOptions,\n): McpClient {\n return new McpClientImpl(server, options);\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"client.mjs","names":[],"sources":["../../../../../../../ai-tools/src/mcp/client.ts"],"sourcesContent":["import { tool, type ToolContract } from \"@warlock.js/ai\";\nimport type {\n McpClient,\n McpClientOptions,\n McpContentBlock,\n McpToolCallResult,\n McpToolDescriptor,\n McpTransport,\n} from \"../contracts\";\nimport { McpTransportError } from \"../errors\";\nimport { jsonSchemaToStandard } from \"./json-schema-to-standard\";\nimport { createJsonRpcClient, type JsonRpcClientHandle } from \"./transport\";\nimport type { McpTransportClient } from \"./transport.type\";\n\n/** Default per-call timeout for `tools/call`. */\nconst DEFAULT_CALL_TIMEOUT_MS = 30_000;\n\n/** The MCP protocol version this client advertises in `initialize`. */\nconst PROTOCOL_VERSION = \"2025-06-18\";\n\n/** The `tools/list` response slice we read. */\ninterface ToolsListResult {\n tools?: McpToolDescriptor[];\n}\n\n/**\n * The internal {@link McpClient} implementation. Owns one JSON-RPC client\n * over a transport, runs the `initialize` handshake on first use, lists\n * the server's tools, and adapts each into a {@link ToolContract} whose\n * `execute` issues `tools/call`. The adapted contracts are cached after\n * the first `tools()` so repeat calls don't re-handshake.\n *\n * Constructed via {@link mcp}; the class itself is internal.\n */\nclass McpClientImpl implements McpClient {\n /** The JSON-RPC client over the transport. */\n private readonly rpc: JsonRpcClientHandle;\n\n /** Construction-time options (prefix / filter / timeout). */\n private readonly options: McpClientOptions;\n\n /** Resolved + cached adapted tools, set after the first `tools()`. */\n private cached: ToolContract[] | undefined;\n\n /** In-flight `tools()` so concurrent callers share one handshake. */\n private pending: Promise<ToolContract[]> | undefined;\n\n /** Flipped once the handshake completes so we only do it once. */\n private initialized = false;\n\n public constructor(\n source: McpTransport | McpTransportClient,\n options: McpClientOptions = {},\n ) {\n this.rpc = createJsonRpcClient(source);\n this.options = options;\n }\n\n /**\n * Run the MCP `initialize` handshake exactly once, then send the\n * `notifications/initialized` notification the protocol requires before\n * any other request. Wraps a handshake failure as a typed\n * {@link McpTransportError} of type `\"connect\"`.\n */\n private async handshake(): Promise<void> {\n if (this.initialized) {\n return;\n }\n\n try {\n await this.rpc.call(\"initialize\", {\n protocolVersion: PROTOCOL_VERSION,\n capabilities: {},\n clientInfo: { name: \"@warlock.js/ai-tools\", version: \"4.4.0\" },\n });\n\n await this.rpc.notify(\"notifications/initialized\");\n } catch (cause) {\n if (cause instanceof McpTransportError) {\n throw cause;\n }\n\n const message = cause instanceof Error ? cause.message : String(cause);\n\n throw new McpTransportError(`MCP initialize handshake failed: ${message}`, {\n type: \"connect\",\n method: \"initialize\",\n cause,\n });\n }\n\n this.initialized = true;\n }\n\n public tools(): Promise<ToolContract[]> {\n if (this.cached) {\n return Promise.resolve(this.cached);\n }\n\n if (this.pending) {\n return this.pending;\n }\n\n this.pending = this.listAndAdapt()\n .then((tools) => {\n this.cached = tools;\n\n return tools;\n })\n .finally(() => {\n this.pending = undefined;\n });\n\n return this.pending;\n }\n\n /**\n * Handshake, `tools/list`, and adapt each descriptor into a\n * {@link ToolContract}, applying the `filter` and `namePrefix` options.\n */\n private async listAndAdapt(): Promise<ToolContract[]> {\n await this.handshake();\n\n const result = await this.rpc.call<ToolsListResult>(\"tools/list\");\n const descriptors = result.tools ?? [];\n\n const filter = this.options.filter;\n const selected = filter ? descriptors.filter((d) => filter(d.name)) : descriptors;\n\n return selected.map((descriptor) => this.adapt(descriptor));\n }\n\n /**\n * Adapt one remote tool descriptor into a {@link ToolContract}: build\n * the input schema from its JSON Schema via {@link jsonSchemaToStandard},\n * prefix the name, and route `execute` through a `tools/call` that\n * honors `ctx.signal`, unwraps the content blocks, and throws on an\n * `isError` result so `tool()` wraps it.\n */\n private adapt(descriptor: McpToolDescriptor): ToolContract {\n const prefixedName = `${this.options.namePrefix ?? \"\"}${descriptor.name}`;\n const remoteName = descriptor.name;\n const timeoutMs = this.options.timeoutMs ?? DEFAULT_CALL_TIMEOUT_MS;\n const input = jsonSchemaToStandard(descriptor.inputSchema);\n\n return tool<unknown, unknown>({\n name: prefixedName,\n description:\n descriptor.description ?? `Invoke the remote MCP tool \"${remoteName}\".`,\n input,\n execute: async (args, ctx) => {\n const result = await this.rpc.call<McpToolCallResult>(\n \"tools/call\",\n { name: remoteName, arguments: args ?? {} },\n { signal: ctx?.signal, timeoutMs },\n );\n\n // An `isError` result is a tool-level failure — throw it so the\n // surrounding `tool()` wraps it as a `ToolExecutionError` and the\n // agent reads it as `{ error }` data and self-corrects.\n if (result.isError) {\n throw new McpTransportError(\n `MCP tool \"${remoteName}\" returned an error: ${unwrapContent(result.content)}`,\n { type: \"protocol\", method: \"tools/call\" },\n );\n }\n\n return unwrapResult(result.content);\n },\n });\n }\n\n public close(): Promise<void> {\n return this.rpc.close();\n }\n}\n\n/**\n * Flatten an MCP `tools/call` result's content blocks into the value a\n * tool returns. Text blocks are concatenated; a single block whose text is\n * valid JSON is parsed so structured tool output flows back as an object\n * rather than a string. Non-text blocks are preserved as `type`-tagged\n * objects (MCP's wire `type` is kept; any inbound `kind` is normalized to\n * `type`).\n */\nfunction unwrapResult(content: McpContentBlock[] | undefined): unknown {\n const blocks = content ?? [];\n\n // The overwhelmingly common case: a single text block. Parse JSON when\n // it is one, so structured results come back typed; otherwise the string.\n if (blocks.length === 1 && blocks[0].type === \"text\") {\n const text = blocks[0].text ?? \"\";\n\n return tryParseJson(text);\n }\n\n // Multiple / mixed blocks: return a normalized array, each tagged by\n // `type` (never `kind`).\n return blocks.map((block) => normalizeBlock(block));\n}\n\n/**\n * Render content blocks to a short human string for error messages — the\n * concatenated text of every text block.\n */\nfunction unwrapContent(content: McpContentBlock[] | undefined): string {\n return (content ?? [])\n .filter((block) => block.type === \"text\" && typeof block.text === \"string\")\n .map((block) => block.text)\n .join(\" \")\n .trim();\n}\n\n/**\n * Normalize one content block onto our `type`-only shape: translate an\n * inbound `kind` discriminator to `type` (and strip `kind`) so the value a\n * tool returns never carries MCP's `kind` vocabulary.\n */\nfunction normalizeBlock(block: McpContentBlock): Record<string, unknown> {\n const { kind, ...rest } = block as McpContentBlock & { kind?: string };\n const type = block.type ?? kind ?? \"unknown\";\n\n return { ...rest, type };\n}\n\n/**\n * Parse a string as JSON, returning the parsed value on success or the\n * original string when it is not JSON — so a plain-text tool result stays\n * a string while a JSON tool result becomes an object.\n */\nfunction tryParseJson(text: string): unknown {\n const trimmed = text.trim();\n\n if (!trimmed) {\n return text;\n }\n\n const first = trimmed[0];\n\n // Only attempt a parse for plausibly-structured payloads, so a bare\n // sentence isn't mangled by a lenient parse.\n if (first !== \"{\" && first !== \"[\") {\n return text;\n }\n\n try {\n return JSON.parse(trimmed);\n } catch {\n return text;\n }\n}\n\n/**\n * Connect to an external MCP server and adapt its tools as agent tools\n * (Direction A: server → local agent tools).\n *\n * Opens the transport lazily and exposes {@link McpClient.tools}, which on\n * first call runs the `initialize` handshake, lists the server's tools via\n * `tools/list`, and maps each into a {@link ToolContract}:\n * - **input schema** — the remote tool's JSON Schema is wrapped as a\n * Standard Schema via {@link jsonSchemaToStandard} (Ajv-backed, an\n * optional peer);\n * - **execute** — issues `tools/call` honoring `ctx.signal` and the\n * configured `timeoutMs`, unwraps the result content, and throws on an\n * `isError` result so the `tool()` wrapper surfaces it as `{ error }`;\n * - **name** — prefixed with `options.namePrefix` to avoid local\n * collisions; only tools passing `options.filter` are adapted.\n *\n * The adapted contracts are cached after the first `tools()` call, so the\n * handshake + list happen exactly once. The returned contracts drop\n * straight into `ai.agent({ tools: [...] })`.\n *\n * @param server - The transport config (`{ type: \"stdio\" }` /\n * `{ type: \"http\" }`). A pre-built transport client may be injected for\n * testing.\n * @param options - Prefix / filter / per-call timeout.\n * @returns An {@link McpClient} handle.\n *\n * @example\n * const github = mcp(\n * { type: \"stdio\", command: \"npx\", args: [\"-y\", \"@modelcontextprotocol/server-github\"] },\n * { namePrefix: \"github.\" },\n * );\n * const dev = ai.agent({ model, tools: [...(await github.tools())] });\n */\nexport function mcp(\n server: McpTransport | McpTransportClient,\n options?: McpClientOptions,\n): McpClient {\n return new McpClientImpl(server, options);\n}\n"],"mappings":"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"}
@@ -4,7 +4,7 @@ import { jsonSchemaToStandard } from "./json-schema-to-standard.mjs";
4
4
  import { McpTransportClient } from "./transport.type.mjs";
5
5
  import { JsonRpcClientHandle, createJsonRpcClient, createTransport } from "./transport.mjs";
6
6
 
7
- //#region ../@warlock.js/ai-tools/src/mcp/index.d.ts
7
+ //#region ../ai-tools/src/mcp/index.d.ts
8
8
  /**
9
9
  * The callable `ai.mcp` surface — a factory that connects to an external
10
10
  * MCP server (Direction A) and also carries `.serve` to expose a local
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.mts","names":[],"sources":["../../../../../../../@warlock.js/ai-tools/src/mcp/index.ts"],"mappings":";;;;;;;;;AAwBA;;;;;UAAiB,UAAA;EAeD;;;;;;EAAA,CARb,MAAA,EAAQ,YAAA,EAAc,OAAA,GAAU,gBAAA,GAAmB,SAAA;EAAnB;;;;;;;EAQjC,KAAA,CAAM,MAAA,EAAQ,cAAA,EAAgB,OAAA,EAAS,eAAA,GAAkB,SAAA;AAAA;;AAAS;AAWpE;;;;AAGC;;cAHY,GAAA,EAAK,UAGjB"}
1
+ {"version":3,"file":"index.d.mts","names":[],"sources":["../../../../../../../ai-tools/src/mcp/index.ts"],"mappings":";;;;;;;;;AAwBA;;;;;UAAiB,UAAA;EAeD;;;;;;EAAA,CARb,MAAA,EAAQ,YAAA,EAAc,OAAA,GAAU,gBAAA,GAAmB,SAAA;EAAnB;;;;;;;EAQjC,KAAA,CAAM,MAAA,EAAQ,cAAA,EAAgB,OAAA,EAAS,eAAA,GAAkB,SAAA;AAAA;;AAAS;AAWpE;;;;AAGC;;cAHY,GAAA,EAAK,UAGjB"}
package/esm/mcp/index.mjs CHANGED
@@ -3,7 +3,7 @@ import { createJsonRpcClient, createTransport } from "./transport.mjs";
3
3
  import { mcp as mcp$1 } from "./client.mjs";
4
4
  import { createServeHandler, serve } from "./serve.mjs";
5
5
 
6
- //#region ../@warlock.js/ai-tools/src/mcp/index.ts
6
+ //#region ../ai-tools/src/mcp/index.ts
7
7
  /**
8
8
  * The `ai.mcp` factory value: the client factory with `.serve` attached.
9
9
  * `Object.assign` keeps `mcp` callable (Direction A) while widening it with
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":["mcpClient"],"sources":["../../../../../../../@warlock.js/ai-tools/src/mcp/index.ts"],"sourcesContent":["import type {\n McpClient,\n McpClientOptions,\n McpServeOptions,\n McpServeSource,\n McpServer,\n McpTransport,\n} from \"../contracts\";\nimport { mcp as mcpClient } from \"./client\";\nimport { serve } from \"./serve\";\n\nexport { serve, createServeHandler } from \"./serve\";\nexport { jsonSchemaToStandard } from \"./json-schema-to-standard\";\nexport { createJsonRpcClient, createTransport } from \"./transport\";\nexport type { JsonRpcClientHandle } from \"./transport\";\nexport type { McpTransportClient } from \"./transport.type\";\n\n/**\n * The callable `ai.mcp` surface — a factory that connects to an external\n * MCP server (Direction A) and also carries `.serve` to expose a local\n * primitive AS an MCP server (Direction B). Modeled as a function with an\n * attached `serve` property, mirroring how `ai.mcp(server).tools()` and\n * `ai.mcp.serve(source, options).start()` read in the design.\n */\nexport interface McpFactory {\n /**\n * Connect to an external MCP server and adapt its tools as agent tools.\n *\n * @param server - The transport config (`{ type: \"stdio\" }` / `{ type: \"http\" }`).\n * @param options - Prefix / filter / per-call timeout.\n */\n (server: McpTransport, options?: McpClientOptions): McpClient;\n /**\n * Expose a built agent / supervisor / orchestrator (or a literal\n * `ToolContract[]`) AS an MCP server.\n *\n * @param source - The tools to expose.\n * @param options - Server name, version, transport, and schema dialect.\n */\n serve(source: McpServeSource, options: McpServeOptions): McpServer;\n}\n\n/**\n * The `ai.mcp` factory value: the client factory with `.serve` attached.\n * `Object.assign` keeps `mcp` callable (Direction A) while widening it with\n * the `serve` member (Direction B) — one object, both directions. The\n * `declare module \"@warlock.js/ai\"` augmentation and the runtime\n * registration (`ai.mcp = mcp`) live in `../register`, so this barrel is a\n * pure value/type module the registrar consumes.\n */\nexport const mcp: McpFactory = Object.assign(\n (server: McpTransport, options?: McpClientOptions): McpClient => mcpClient(server, options),\n { serve },\n);\n"],"mappings":";;;;;;;;;;;;;;AAkDA,MAAa,MAAkB,OAAO,QACnC,QAAsB,YAA0CA,MAAU,QAAQ,OAAO,GAC1F,EAAE,MAAM,CACV"}
1
+ {"version":3,"file":"index.mjs","names":["mcpClient"],"sources":["../../../../../../../ai-tools/src/mcp/index.ts"],"sourcesContent":["import type {\n McpClient,\n McpClientOptions,\n McpServeOptions,\n McpServeSource,\n McpServer,\n McpTransport,\n} from \"../contracts\";\nimport { mcp as mcpClient } from \"./client\";\nimport { serve } from \"./serve\";\n\nexport { serve, createServeHandler } from \"./serve\";\nexport { jsonSchemaToStandard } from \"./json-schema-to-standard\";\nexport { createJsonRpcClient, createTransport } from \"./transport\";\nexport type { JsonRpcClientHandle } from \"./transport\";\nexport type { McpTransportClient } from \"./transport.type\";\n\n/**\n * The callable `ai.mcp` surface — a factory that connects to an external\n * MCP server (Direction A) and also carries `.serve` to expose a local\n * primitive AS an MCP server (Direction B). Modeled as a function with an\n * attached `serve` property, mirroring how `ai.mcp(server).tools()` and\n * `ai.mcp.serve(source, options).start()` read in the design.\n */\nexport interface McpFactory {\n /**\n * Connect to an external MCP server and adapt its tools as agent tools.\n *\n * @param server - The transport config (`{ type: \"stdio\" }` / `{ type: \"http\" }`).\n * @param options - Prefix / filter / per-call timeout.\n */\n (server: McpTransport, options?: McpClientOptions): McpClient;\n /**\n * Expose a built agent / supervisor / orchestrator (or a literal\n * `ToolContract[]`) AS an MCP server.\n *\n * @param source - The tools to expose.\n * @param options - Server name, version, transport, and schema dialect.\n */\n serve(source: McpServeSource, options: McpServeOptions): McpServer;\n}\n\n/**\n * The `ai.mcp` factory value: the client factory with `.serve` attached.\n * `Object.assign` keeps `mcp` callable (Direction A) while widening it with\n * the `serve` member (Direction B) — one object, both directions. The\n * `declare module \"@warlock.js/ai\"` augmentation and the runtime\n * registration (`ai.mcp = mcp`) live in `../register`, so this barrel is a\n * pure value/type module the registrar consumes.\n */\nexport const mcp: McpFactory = Object.assign(\n (server: McpTransport, options?: McpClientOptions): McpClient => mcpClient(server, options),\n { serve },\n);\n"],"mappings":";;;;;;;;;;;;;;AAkDA,MAAa,MAAkB,OAAO,QACnC,QAAsB,YAA0CA,MAAU,QAAQ,OAAO,GAC1F,EAAE,MAAM,CACV"}
@@ -1,6 +1,6 @@
1
1
  import { StandardSchemaV1 } from "../node_modules/@standard-schema/spec/dist/index.mjs";
2
2
 
3
- //#region ../@warlock.js/ai-tools/src/mcp/json-schema-to-standard.d.ts
3
+ //#region ../ai-tools/src/mcp/json-schema-to-standard.d.ts
4
4
  /**
5
5
  * Wrap a raw JSON Schema as a {@link StandardSchemaV1} whose
6
6
  * `~standard.validate` runs Ajv. The shape mirrors `passthroughSchema()`
@@ -1 +1 @@
1
- {"version":3,"file":"json-schema-to-standard.d.mts","names":[],"sources":["../../../../../../../@warlock.js/ai-tools/src/mcp/json-schema-to-standard.ts"],"mappings":";;;;;AA+JA;;;;;;;;;;;;;;AAE0B;;;;;;;;;;;iBAFV,oBAAA,mBACd,MAAA,EAAQ,MAAA,gCACP,gBAAA,CAAiB,MAAA"}
1
+ {"version":3,"file":"json-schema-to-standard.d.mts","names":[],"sources":["../../../../../../../ai-tools/src/mcp/json-schema-to-standard.ts"],"mappings":";;;;;AA+JA;;;;;;;;;;;;;;AAE0B;;;;;;;;;;;iBAFV,oBAAA,mBACd,MAAA,EAAQ,MAAA,gCACP,gBAAA,CAAiB,MAAA"}
@@ -1,4 +1,4 @@
1
- //#region ../@warlock.js/ai-tools/src/mcp/json-schema-to-standard.ts
1
+ //#region ../ai-tools/src/mcp/json-schema-to-standard.ts
2
2
  /**
3
3
  * The inverse of `@warlock.js/ai`'s `extractJsonSchema` (which goes
4
4
  * Standard Schema → JSON Schema). Here we wrap a raw JSON Schema as a
@@ -1 +1 @@
1
- {"version":3,"file":"json-schema-to-standard.mjs","names":[],"sources":["../../../../../../../@warlock.js/ai-tools/src/mcp/json-schema-to-standard.ts"],"sourcesContent":["import type { StandardSchemaV1 } from \"@standard-schema/spec\";\n\n/**\n * The inverse of `@warlock.js/ai`'s `extractJsonSchema` (which goes\n * Standard Schema → JSON Schema). Here we wrap a raw JSON Schema as a\n * {@link StandardSchemaV1} whose `~standard.validate` runs a lazily-imported\n * Ajv validator — so an MCP server's `inputSchema` (JSON Schema) becomes a\n * `ToolConfig.input` the `tool()` factory can validate against.\n *\n * Ajv is an OPTIONAL peer, lazy-imported on first validate following the\n * langfuse/readability pattern: a missing peer surfaces a curated install\n * string (via the returned issues), never a raw module-resolution stack.\n */\n\n/** The structural vendor template, mirroring `passthroughSchema()`. */\nconst VENDOR = \"warlock-ai\";\n\n// ============================================================\n// Lazily-loaded ajv (OPTIONAL peer)\n// ============================================================\n\n/**\n * Minimal structural view of an Ajv-compiled validator. Ajv is an optional\n * peer that may not be installed, so we model only the surface we touch\n * rather than depending on ajv's own published types. A validator is a\n * callable that returns a boolean and exposes the `errors` it collected.\n */\ninterface AjvValidateFunctionLike {\n (data: unknown): boolean;\n errors?: AjvErrorObject[] | null;\n}\n\n/** Minimal structural view of an `Ajv` instance — only `compile`. */\ninterface AjvInstanceLike {\n compile(schema: Record<string, unknown>): AjvValidateFunctionLike;\n}\n\n/** The `Ajv` constructor, as exposed by both the CJS and ESM builds. */\ntype AjvConstructorLike = new (options?: Record<string, unknown>) => AjvInstanceLike;\n\n/**\n * Minimal structural view of the dynamically imported `ajv` module. Ajv\n * ships its constructor as a `default` export under ESM interop; the older\n * CJS shape exposes the constructor as the module namespace itself, so\n * `default` is optional here and the loader falls back to the namespace.\n */\ninterface AjvModuleLike {\n default?: AjvConstructorLike;\n}\n\nlet AjvSdk: AjvModuleLike | undefined;\nlet ajvInstance: AjvInstanceLike | undefined;\nlet isAjvAvailable: boolean | undefined;\nlet loadingPromise: Promise<void> | undefined;\n\nconst AJV_INSTALL_INSTRUCTIONS = `\nThe MCP client's JSON-Schema validation requires the ajv package.\nInstall it with:\n\n npm install ajv\n\nOr with your preferred package manager:\n\n pnpm add ajv\n yarn add ajv\n`.trim();\n\n/**\n * Settle the lazy import of `ajv` once, concurrency-safe. A bare `catch`\n * flips the availability flag to `false`; the curated install string then\n * surfaces at validate time as a Standard Schema issue, never a raw\n * module-resolution error. The constructed `Ajv` instance is cached and\n * reused for every schema compile.\n */\nasync function loadAjv(): Promise<void> {\n if (isAjvAvailable !== undefined) {\n return;\n }\n\n if (loadingPromise) {\n return loadingPromise;\n }\n\n loadingPromise = (async () => {\n try {\n AjvSdk = (await import(\"ajv\")) as AjvModuleLike;\n // Ajv ships as a default export under both CJS and ESM interop; the\n // older CJS shape exposes the constructor as the namespace itself.\n const AjvCtor: AjvConstructorLike =\n AjvSdk.default ?? (AjvSdk as unknown as AjvConstructorLike);\n ajvInstance = new AjvCtor({ allErrors: true, strict: false });\n isAjvAvailable = true;\n } catch {\n isAjvAvailable = false;\n }\n })();\n\n return loadingPromise;\n}\n\n/**\n * Compile a JSON Schema with the shared Ajv instance, caching the compiled\n * validator on a closure so repeated validations don't recompile. A schema\n * Ajv itself rejects at compile time (an invalid meta-schema) degrades to\n * an accept-all validator so a malformed remote schema can't wedge the\n * tool — the server, not us, owns its schema's correctness.\n */\nfunction makeCompiler(schema: Record<string, unknown>): () => AjvValidateFunctionLike | undefined {\n let compiled: AjvValidateFunctionLike | undefined;\n let attempted = false;\n\n return () => {\n if (attempted) {\n return compiled;\n }\n\n attempted = true;\n\n if (!ajvInstance) {\n return undefined;\n }\n\n try {\n compiled = ajvInstance.compile(schema);\n } catch {\n compiled = undefined;\n }\n\n return compiled;\n };\n}\n\n/**\n * Wrap a raw JSON Schema as a {@link StandardSchemaV1} whose\n * `~standard.validate` runs Ajv. The shape mirrors `passthroughSchema()`\n * (`{ \"~standard\": { version: 1, vendor, validate } }`) so it drops into\n * `tool({ input })` exactly like a native seal schema.\n *\n * Validation behavior:\n * - **Valid input** → `{ value }` (the input is passed through unchanged;\n * Ajv validates, it does not transform).\n * - **Invalid input** → `{ issues }` carrying Ajv's `instancePath` +\n * message per failure, so `tool()` produces a `SchemaValidationError`.\n * - **Missing `ajv` peer** → a single issue carrying the curated install\n * string, surfaced the same way (a developer-facing message in logs).\n * - **No / empty schema** → an accept-all passthrough (an MCP tool may\n * advertise no `inputSchema`).\n *\n * @param schema - The JSON Schema (an MCP tool's `inputSchema`), or\n * `undefined` for a no-argument tool.\n * @returns A `StandardSchemaV1<TInput>` ready for `tool({ input })`.\n *\n * @example\n * const input = jsonSchemaToStandard<{ q: string }>({\n * type: \"object\",\n * properties: { q: { type: \"string\" } },\n * required: [\"q\"],\n * });\n */\nexport function jsonSchemaToStandard<TInput = unknown>(\n schema: Record<string, unknown> | undefined,\n): StandardSchemaV1<TInput> {\n // A tool with no schema (or an empty object schema) validates everything\n // — return an accept-all passthrough and never touch Ajv.\n if (!schema || Object.keys(schema).length === 0) {\n return {\n \"~standard\": {\n version: 1,\n vendor: VENDOR,\n validate: (value: unknown) => ({ value: value as TInput }),\n },\n };\n }\n\n const compile = makeCompiler(schema);\n\n return {\n \"~standard\": {\n version: 1,\n vendor: VENDOR,\n async validate(value: unknown): Promise<StandardSchemaV1.Result<TInput>> {\n await loadAjv();\n\n if (!isAjvAvailable) {\n return {\n issues: [{ message: AJV_INSTALL_INSTRUCTIONS }],\n };\n }\n\n const validator = compile();\n\n // A schema Ajv could not compile degrades to accept-all rather\n // than failing every call — the remote server owns its schema.\n if (!validator) {\n return { value: value as TInput };\n }\n\n const ok = validator(value);\n\n if (ok) {\n return { value: value as TInput };\n }\n\n const issues: StandardSchemaV1.Issue[] = (validator.errors ?? []).map((error) => ({\n message: formatAjvError(error),\n path: pathFromInstancePath(error.instancePath),\n }));\n\n return {\n issues: issues.length > 0 ? issues : [{ message: \"input failed JSON Schema validation\" }],\n };\n },\n },\n };\n}\n\n/** A single Ajv error object — narrowed to the fields we read. */\ninterface AjvErrorObject {\n instancePath?: string;\n message?: string;\n keyword?: string;\n}\n\n/**\n * Render one Ajv error into a human-readable issue message. Prefixes the\n * failing instance path (when present) so the model can see WHICH field\n * was wrong, e.g. `/query: must be string`.\n */\nfunction formatAjvError(error: AjvErrorObject): string {\n const where = error.instancePath ? `${error.instancePath}: ` : \"\";\n const message = error.message ?? `failed \"${error.keyword ?? \"validation\"}\"`;\n\n return `${where}${message}`;\n}\n\n/**\n * Convert an Ajv `instancePath` (a JSON-Pointer like `/items/0/name`) into\n * the Standard Schema `path` segment array (`[\"items\", \"0\", \"name\"]`).\n * Empty paths (a root-level failure) become an empty array.\n */\nfunction pathFromInstancePath(instancePath: string | undefined): string[] {\n if (!instancePath) {\n return [];\n }\n\n return instancePath\n .split(\"/\")\n .filter((segment) => segment.length > 0)\n .map((segment) => segment.replace(/~1/g, \"/\").replace(/~0/g, \"~\"));\n}\n\n/**\n * Reset the cached lazy-import state. Test-only seam so a spec can mock\n * `ajv` as present/absent across cases without module-cache bleed.\n *\n * @internal\n */\nexport function resetAjvCacheForTests(): void {\n AjvSdk = undefined;\n ajvInstance = undefined;\n isAjvAvailable = undefined;\n loadingPromise = undefined;\n}\n"],"mappings":";;;;;;;;;;;;;AAeA,MAAM,SAAS;AAmCf,IAAI;AACJ,IAAI;AACJ,IAAI;AACJ,IAAI;AAEJ,MAAM,2BAA2B;;;;;;;;;;EAU/B,KAAK;;;;;;;;AASP,eAAe,UAAyB;CACtC,IAAI,mBAAmB,QACrB;CAGF,IAAI,gBACF,OAAO;CAGT,kBAAkB,YAAY;EAC5B,IAAI;GACF,SAAU,MAAM,OAAO;GAKvB,cAAc,KADZ,OAAO,WAAY,QACK;IAAE,WAAW;IAAM,QAAQ;GAAM,CAAC;GAC5D,iBAAiB;EACnB,QAAQ;GACN,iBAAiB;EACnB;CACF,EAAC,CAAE;CAEH,OAAO;AACT;;;;;;;;AASA,SAAS,aAAa,QAA4E;CAChG,IAAI;CACJ,IAAI,YAAY;CAEhB,aAAa;EACX,IAAI,WACF,OAAO;EAGT,YAAY;EAEZ,IAAI,CAAC,aACH;EAGF,IAAI;GACF,WAAW,YAAY,QAAQ,MAAM;EACvC,QAAQ;GACN,WAAW;EACb;EAEA,OAAO;CACT;AACF;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA6BA,SAAgB,qBACd,QAC0B;CAG1B,IAAI,CAAC,UAAU,OAAO,KAAK,MAAM,CAAC,CAAC,WAAW,GAC5C,OAAO,EACL,aAAa;EACX,SAAS;EACT,QAAQ;EACR,WAAW,WAAoB,EAAS,MAAgB;CAC1D,EACF;CAGF,MAAM,UAAU,aAAa,MAAM;CAEnC,OAAO,EACL,aAAa;EACX,SAAS;EACT,QAAQ;EACR,MAAM,SAAS,OAA0D;GACvE,MAAM,QAAQ;GAEd,IAAI,CAAC,gBACH,OAAO,EACL,QAAQ,CAAC,EAAE,SAAS,yBAAyB,CAAC,EAChD;GAGF,MAAM,YAAY,QAAQ;GAI1B,IAAI,CAAC,WACH,OAAO,EAAS,MAAgB;GAKlC,IAFW,UAAU,KAEhB,GACH,OAAO,EAAS,MAAgB;GAGlC,MAAM,UAAoC,UAAU,UAAU,CAAC,EAAC,CAAE,KAAK,WAAW;IAChF,SAAS,eAAe,KAAK;IAC7B,MAAM,qBAAqB,MAAM,YAAY;GAC/C,EAAE;GAEF,OAAO,EACL,QAAQ,OAAO,SAAS,IAAI,SAAS,CAAC,EAAE,SAAS,sCAAsC,CAAC,EAC1F;EACF;CACF,EACF;AACF;;;;;;AAcA,SAAS,eAAe,OAA+B;CAIrD,OAAO,GAHO,MAAM,eAAe,GAAG,MAAM,aAAa,MAAM,KAC/C,MAAM,WAAW,WAAW,MAAM,WAAW,aAAa;AAG5E;;;;;;AAOA,SAAS,qBAAqB,cAA4C;CACxE,IAAI,CAAC,cACH,OAAO,CAAC;CAGV,OAAO,aACJ,MAAM,GAAG,CAAC,CACV,QAAQ,YAAY,QAAQ,SAAS,CAAC,CAAC,CACvC,KAAK,YAAY,QAAQ,QAAQ,OAAO,GAAG,CAAC,CAAC,QAAQ,OAAO,GAAG,CAAC;AACrE"}
1
+ {"version":3,"file":"json-schema-to-standard.mjs","names":[],"sources":["../../../../../../../ai-tools/src/mcp/json-schema-to-standard.ts"],"sourcesContent":["import type { StandardSchemaV1 } from \"@standard-schema/spec\";\n\n/**\n * The inverse of `@warlock.js/ai`'s `extractJsonSchema` (which goes\n * Standard Schema → JSON Schema). Here we wrap a raw JSON Schema as a\n * {@link StandardSchemaV1} whose `~standard.validate` runs a lazily-imported\n * Ajv validator — so an MCP server's `inputSchema` (JSON Schema) becomes a\n * `ToolConfig.input` the `tool()` factory can validate against.\n *\n * Ajv is an OPTIONAL peer, lazy-imported on first validate following the\n * langfuse/readability pattern: a missing peer surfaces a curated install\n * string (via the returned issues), never a raw module-resolution stack.\n */\n\n/** The structural vendor template, mirroring `passthroughSchema()`. */\nconst VENDOR = \"warlock-ai\";\n\n// ============================================================\n// Lazily-loaded ajv (OPTIONAL peer)\n// ============================================================\n\n/**\n * Minimal structural view of an Ajv-compiled validator. Ajv is an optional\n * peer that may not be installed, so we model only the surface we touch\n * rather than depending on ajv's own published types. A validator is a\n * callable that returns a boolean and exposes the `errors` it collected.\n */\ninterface AjvValidateFunctionLike {\n (data: unknown): boolean;\n errors?: AjvErrorObject[] | null;\n}\n\n/** Minimal structural view of an `Ajv` instance — only `compile`. */\ninterface AjvInstanceLike {\n compile(schema: Record<string, unknown>): AjvValidateFunctionLike;\n}\n\n/** The `Ajv` constructor, as exposed by both the CJS and ESM builds. */\ntype AjvConstructorLike = new (options?: Record<string, unknown>) => AjvInstanceLike;\n\n/**\n * Minimal structural view of the dynamically imported `ajv` module. Ajv\n * ships its constructor as a `default` export under ESM interop; the older\n * CJS shape exposes the constructor as the module namespace itself, so\n * `default` is optional here and the loader falls back to the namespace.\n */\ninterface AjvModuleLike {\n default?: AjvConstructorLike;\n}\n\nlet AjvSdk: AjvModuleLike | undefined;\nlet ajvInstance: AjvInstanceLike | undefined;\nlet isAjvAvailable: boolean | undefined;\nlet loadingPromise: Promise<void> | undefined;\n\nconst AJV_INSTALL_INSTRUCTIONS = `\nThe MCP client's JSON-Schema validation requires the ajv package.\nInstall it with:\n\n npm install ajv\n\nOr with your preferred package manager:\n\n pnpm add ajv\n yarn add ajv\n`.trim();\n\n/**\n * Settle the lazy import of `ajv` once, concurrency-safe. A bare `catch`\n * flips the availability flag to `false`; the curated install string then\n * surfaces at validate time as a Standard Schema issue, never a raw\n * module-resolution error. The constructed `Ajv` instance is cached and\n * reused for every schema compile.\n */\nasync function loadAjv(): Promise<void> {\n if (isAjvAvailable !== undefined) {\n return;\n }\n\n if (loadingPromise) {\n return loadingPromise;\n }\n\n loadingPromise = (async () => {\n try {\n AjvSdk = (await import(\"ajv\")) as AjvModuleLike;\n // Ajv ships as a default export under both CJS and ESM interop; the\n // older CJS shape exposes the constructor as the namespace itself.\n const AjvCtor: AjvConstructorLike =\n AjvSdk.default ?? (AjvSdk as unknown as AjvConstructorLike);\n ajvInstance = new AjvCtor({ allErrors: true, strict: false });\n isAjvAvailable = true;\n } catch {\n isAjvAvailable = false;\n }\n })();\n\n return loadingPromise;\n}\n\n/**\n * Compile a JSON Schema with the shared Ajv instance, caching the compiled\n * validator on a closure so repeated validations don't recompile. A schema\n * Ajv itself rejects at compile time (an invalid meta-schema) degrades to\n * an accept-all validator so a malformed remote schema can't wedge the\n * tool — the server, not us, owns its schema's correctness.\n */\nfunction makeCompiler(schema: Record<string, unknown>): () => AjvValidateFunctionLike | undefined {\n let compiled: AjvValidateFunctionLike | undefined;\n let attempted = false;\n\n return () => {\n if (attempted) {\n return compiled;\n }\n\n attempted = true;\n\n if (!ajvInstance) {\n return undefined;\n }\n\n try {\n compiled = ajvInstance.compile(schema);\n } catch {\n compiled = undefined;\n }\n\n return compiled;\n };\n}\n\n/**\n * Wrap a raw JSON Schema as a {@link StandardSchemaV1} whose\n * `~standard.validate` runs Ajv. The shape mirrors `passthroughSchema()`\n * (`{ \"~standard\": { version: 1, vendor, validate } }`) so it drops into\n * `tool({ input })` exactly like a native seal schema.\n *\n * Validation behavior:\n * - **Valid input** → `{ value }` (the input is passed through unchanged;\n * Ajv validates, it does not transform).\n * - **Invalid input** → `{ issues }` carrying Ajv's `instancePath` +\n * message per failure, so `tool()` produces a `SchemaValidationError`.\n * - **Missing `ajv` peer** → a single issue carrying the curated install\n * string, surfaced the same way (a developer-facing message in logs).\n * - **No / empty schema** → an accept-all passthrough (an MCP tool may\n * advertise no `inputSchema`).\n *\n * @param schema - The JSON Schema (an MCP tool's `inputSchema`), or\n * `undefined` for a no-argument tool.\n * @returns A `StandardSchemaV1<TInput>` ready for `tool({ input })`.\n *\n * @example\n * const input = jsonSchemaToStandard<{ q: string }>({\n * type: \"object\",\n * properties: { q: { type: \"string\" } },\n * required: [\"q\"],\n * });\n */\nexport function jsonSchemaToStandard<TInput = unknown>(\n schema: Record<string, unknown> | undefined,\n): StandardSchemaV1<TInput> {\n // A tool with no schema (or an empty object schema) validates everything\n // — return an accept-all passthrough and never touch Ajv.\n if (!schema || Object.keys(schema).length === 0) {\n return {\n \"~standard\": {\n version: 1,\n vendor: VENDOR,\n validate: (value: unknown) => ({ value: value as TInput }),\n },\n };\n }\n\n const compile = makeCompiler(schema);\n\n return {\n \"~standard\": {\n version: 1,\n vendor: VENDOR,\n async validate(value: unknown): Promise<StandardSchemaV1.Result<TInput>> {\n await loadAjv();\n\n if (!isAjvAvailable) {\n return {\n issues: [{ message: AJV_INSTALL_INSTRUCTIONS }],\n };\n }\n\n const validator = compile();\n\n // A schema Ajv could not compile degrades to accept-all rather\n // than failing every call — the remote server owns its schema.\n if (!validator) {\n return { value: value as TInput };\n }\n\n const ok = validator(value);\n\n if (ok) {\n return { value: value as TInput };\n }\n\n const issues: StandardSchemaV1.Issue[] = (validator.errors ?? []).map((error) => ({\n message: formatAjvError(error),\n path: pathFromInstancePath(error.instancePath),\n }));\n\n return {\n issues: issues.length > 0 ? issues : [{ message: \"input failed JSON Schema validation\" }],\n };\n },\n },\n };\n}\n\n/** A single Ajv error object — narrowed to the fields we read. */\ninterface AjvErrorObject {\n instancePath?: string;\n message?: string;\n keyword?: string;\n}\n\n/**\n * Render one Ajv error into a human-readable issue message. Prefixes the\n * failing instance path (when present) so the model can see WHICH field\n * was wrong, e.g. `/query: must be string`.\n */\nfunction formatAjvError(error: AjvErrorObject): string {\n const where = error.instancePath ? `${error.instancePath}: ` : \"\";\n const message = error.message ?? `failed \"${error.keyword ?? \"validation\"}\"`;\n\n return `${where}${message}`;\n}\n\n/**\n * Convert an Ajv `instancePath` (a JSON-Pointer like `/items/0/name`) into\n * the Standard Schema `path` segment array (`[\"items\", \"0\", \"name\"]`).\n * Empty paths (a root-level failure) become an empty array.\n */\nfunction pathFromInstancePath(instancePath: string | undefined): string[] {\n if (!instancePath) {\n return [];\n }\n\n return instancePath\n .split(\"/\")\n .filter((segment) => segment.length > 0)\n .map((segment) => segment.replace(/~1/g, \"/\").replace(/~0/g, \"~\"));\n}\n\n/**\n * Reset the cached lazy-import state. Test-only seam so a spec can mock\n * `ajv` as present/absent across cases without module-cache bleed.\n *\n * @internal\n */\nexport function resetAjvCacheForTests(): void {\n AjvSdk = undefined;\n ajvInstance = undefined;\n isAjvAvailable = undefined;\n loadingPromise = undefined;\n}\n"],"mappings":";;;;;;;;;;;;;AAeA,MAAM,SAAS;AAmCf,IAAI;AACJ,IAAI;AACJ,IAAI;AACJ,IAAI;AAEJ,MAAM,2BAA2B;;;;;;;;;;EAU/B,KAAK;;;;;;;;AASP,eAAe,UAAyB;CACtC,IAAI,mBAAmB,QACrB;CAGF,IAAI,gBACF,OAAO;CAGT,kBAAkB,YAAY;EAC5B,IAAI;GACF,SAAU,MAAM,OAAO;GAKvB,cAAc,KADZ,OAAO,WAAY,QACK;IAAE,WAAW;IAAM,QAAQ;GAAM,CAAC;GAC5D,iBAAiB;EACnB,QAAQ;GACN,iBAAiB;EACnB;CACF,EAAC,CAAE;CAEH,OAAO;AACT;;;;;;;;AASA,SAAS,aAAa,QAA4E;CAChG,IAAI;CACJ,IAAI,YAAY;CAEhB,aAAa;EACX,IAAI,WACF,OAAO;EAGT,YAAY;EAEZ,IAAI,CAAC,aACH;EAGF,IAAI;GACF,WAAW,YAAY,QAAQ,MAAM;EACvC,QAAQ;GACN,WAAW;EACb;EAEA,OAAO;CACT;AACF;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA6BA,SAAgB,qBACd,QAC0B;CAG1B,IAAI,CAAC,UAAU,OAAO,KAAK,MAAM,CAAC,CAAC,WAAW,GAC5C,OAAO,EACL,aAAa;EACX,SAAS;EACT,QAAQ;EACR,WAAW,WAAoB,EAAS,MAAgB;CAC1D,EACF;CAGF,MAAM,UAAU,aAAa,MAAM;CAEnC,OAAO,EACL,aAAa;EACX,SAAS;EACT,QAAQ;EACR,MAAM,SAAS,OAA0D;GACvE,MAAM,QAAQ;GAEd,IAAI,CAAC,gBACH,OAAO,EACL,QAAQ,CAAC,EAAE,SAAS,yBAAyB,CAAC,EAChD;GAGF,MAAM,YAAY,QAAQ;GAI1B,IAAI,CAAC,WACH,OAAO,EAAS,MAAgB;GAKlC,IAFW,UAAU,KAEhB,GACH,OAAO,EAAS,MAAgB;GAGlC,MAAM,UAAoC,UAAU,UAAU,CAAC,EAAC,CAAE,KAAK,WAAW;IAChF,SAAS,eAAe,KAAK;IAC7B,MAAM,qBAAqB,MAAM,YAAY;GAC/C,EAAE;GAEF,OAAO,EACL,QAAQ,OAAO,SAAS,IAAI,SAAS,CAAC,EAAE,SAAS,sCAAsC,CAAC,EAC1F;EACF;CACF,EACF;AACF;;;;;;AAcA,SAAS,eAAe,OAA+B;CAIrD,OAAO,GAHO,MAAM,eAAe,GAAG,MAAM,aAAa,MAAM,KAC/C,MAAM,WAAW,WAAW,MAAM,WAAW,aAAa;AAG5E;;;;;;AAOA,SAAS,qBAAqB,cAA4C;CACxE,IAAI,CAAC,cACH,OAAO,CAAC;CAGV,OAAO,aACJ,MAAM,GAAG,CAAC,CACV,QAAQ,YAAY,QAAQ,SAAS,CAAC,CAAC,CACvC,KAAK,YAAY,QAAQ,QAAQ,OAAO,GAAG,CAAC,CAAC,QAAQ,OAAO,GAAG,CAAC;AACrE"}
@@ -1,5 +1,5 @@
1
1
  import { JsonRpcRequest, JsonRpcResponse, McpServeOptions, McpServeSource, McpServer } from "../contracts/mcp.type.mjs";
2
- //#region ../@warlock.js/ai-tools/src/mcp/serve.d.ts
2
+ //#region ../ai-tools/src/mcp/serve.d.ts
3
3
  /**
4
4
  * Build the pure protocol handler for a serve source. Exposed (alongside
5
5
  * {@link serve}) so callers and tests can drive the MCP protocol without an
@@ -1 +1 @@
1
- {"version":3,"file":"serve.d.mts","names":[],"sources":["../../../../../../../@warlock.js/ai-tools/src/mcp/serve.ts"],"mappings":";;;;;AAiOA;;;;;;iBAAgB,kBAAA,CACd,MAAA,EAAQ,cAAA,EACR,OAAA,EAAS,eAAA;EACN,MAAA,CAAO,OAAA,EAAS,cAAA,GAAiB,OAAA,CAAQ,eAAA;AAAA;;;;;;;;;;;;AAAe;AAqH7D;;;;;;;;;;;;;;AAAkF;;iBAAlE,KAAA,CAAM,MAAA,EAAQ,cAAA,EAAgB,OAAA,EAAS,eAAA,GAAkB,SAAA"}
1
+ {"version":3,"file":"serve.d.mts","names":[],"sources":["../../../../../../../ai-tools/src/mcp/serve.ts"],"mappings":";;;;;AAiOA;;;;;;iBAAgB,kBAAA,CACd,MAAA,EAAQ,cAAA,EACR,OAAA,EAAS,eAAA;EACN,MAAA,CAAO,OAAA,EAAS,cAAA,GAAiB,OAAA,CAAQ,eAAA;AAAA;;;;;;;;;;;;AAAe;AAqH7D;;;;;;;;;;;;;;AAAkF;;iBAAlE,KAAA,CAAM,MAAA,EAAQ,cAAA,EAAgB,OAAA,EAAS,eAAA,GAAkB,SAAA"}
package/esm/mcp/serve.mjs CHANGED
@@ -2,7 +2,7 @@ import { McpTransportError } from "../errors.mjs";
2
2
  import { extractJsonSchema } from "@warlock.js/ai";
3
3
  import { createInterface } from "node:readline";
4
4
 
5
- //#region ../@warlock.js/ai-tools/src/mcp/serve.ts
5
+ //#region ../ai-tools/src/mcp/serve.ts
6
6
  /** JSON-RPC version literal every outbound message carries. */
7
7
  const JSONRPC_VERSION = "2.0";
8
8
  /** Default JSON-Schema dialect emitted for each tool's `inputSchema`. */
@@ -1 +1 @@
1
- {"version":3,"file":"serve.mjs","names":[],"sources":["../../../../../../../@warlock.js/ai-tools/src/mcp/serve.ts"],"sourcesContent":["import { createInterface, type Interface } from \"node:readline\";\nimport { extractJsonSchema, type ToolContract } from \"@warlock.js/ai\";\nimport type {\n JsonRpcRequest,\n JsonRpcResponse,\n McpServeOptions,\n McpServeSource,\n McpServer,\n McpToolCallResult,\n McpToolDescriptor,\n} from \"../contracts\";\nimport { McpTransportError } from \"../errors\";\n\n/** JSON-RPC version literal every outbound message carries. */\nconst JSONRPC_VERSION = \"2.0\";\n\n/** Default JSON-Schema dialect emitted for each tool's `inputSchema`. */\nconst DEFAULT_SCHEMA_TARGET = \"draft-2020-12\";\n\n/** Default advertised server version when the caller omits one. */\nconst DEFAULT_VERSION = \"4.4.0\";\n\n/** The MCP protocol version this server advertises in `initialize`. */\nconst PROTOCOL_VERSION = \"2025-06-18\";\n\n/** JSON-RPC standard error codes we emit. */\nconst JSON_RPC_METHOD_NOT_FOUND = -32601;\nconst JSON_RPC_INVALID_PARAMS = -32602;\n\n/**\n * Resolve the {@link McpServeSource} (either an object exposing `tools()`\n * or a literal `ToolContract[]`) into a flat contract array.\n */\nfunction resolveTools(source: McpServeSource): ToolContract[] {\n if (Array.isArray(source)) {\n return source;\n }\n\n return source.tools();\n}\n\n/**\n * The pure protocol core of `serve` — maps one JSON-RPC request to its\n * response, with no I/O. Both the stdio and http serve-transports pump\n * their inbound requests through this, and specs can drive it directly.\n *\n * Handles exactly the MCP slice this package serves: `initialize`,\n * `tools/list`, and `tools/call`. Any other method answers with a\n * JSON-RPC `method not found` error.\n *\n * Constructed via {@link createServeHandler}.\n */\nclass McpServeHandler {\n /** The tools this server exposes (snapshotted at construction). */\n private readonly tools: ToolContract[];\n\n /** Fast lookup by tool name for `tools/call` dispatch. */\n private readonly byName: Map<string, ToolContract>;\n\n /** Construction-time serve options (name / version / schema target). */\n private readonly options: McpServeOptions;\n\n public constructor(source: McpServeSource, options: McpServeOptions) {\n this.tools = resolveTools(source);\n this.byName = new Map(this.tools.map((contract) => [contract.name, contract]));\n this.options = options;\n }\n\n /**\n * Dispatch one inbound JSON-RPC request to its handler and produce the\n * response. A handler that throws is mapped to a JSON-RPC error response\n * — the serve loop never crashes on a bad request.\n */\n public async handle(request: JsonRpcRequest): Promise<JsonRpcResponse> {\n try {\n switch (request.method) {\n case \"initialize\":\n return this.ok(request.id, this.initializeResult());\n case \"tools/list\":\n return this.ok(request.id, { tools: this.listTools() });\n case \"tools/call\":\n return this.ok(request.id, await this.callTool(request.params));\n default:\n return this.error(\n request.id,\n JSON_RPC_METHOD_NOT_FOUND,\n `Method \"${request.method}\" is not supported by this MCP server.`,\n );\n }\n } catch (cause) {\n const message = cause instanceof Error ? cause.message : String(cause);\n\n return this.error(request.id, JSON_RPC_INVALID_PARAMS, message);\n }\n }\n\n /** Build the `initialize` result advertising name / version / capabilities. */\n private initializeResult(): Record<string, unknown> {\n return {\n protocolVersion: PROTOCOL_VERSION,\n capabilities: { tools: {} },\n serverInfo: {\n name: this.options.name,\n version: this.options.version ?? DEFAULT_VERSION,\n },\n };\n }\n\n /**\n * Build the `tools/list` payload: one {@link McpToolDescriptor} per\n * contract, its `inputSchema` extracted via `extractJsonSchema` at the\n * configured dialect (default `draft-2020-12` — overriding\n * `extractJsonSchema`'s own `openai-strict` default to a neutral MCP draft).\n */\n private listTools(): McpToolDescriptor[] {\n const target = this.options.schemaTarget ?? DEFAULT_SCHEMA_TARGET;\n\n return this.tools.map((contract) => {\n const inputSchema = extractJsonSchema(contract.input, { target }) ?? {\n type: \"object\",\n properties: {},\n };\n\n return {\n name: contract.name,\n description: contract.description,\n inputSchema,\n };\n });\n }\n\n /**\n * Route a `tools/call` to the named contract's `invoke()` and map the\n * never-throwing {@link import(\"@warlock.js/ai\").ToolInvokeResult}: `data`\n * → a text content block, `error` → an `isError: true` result. An unknown\n * tool name throws (mapped to a JSON-RPC error by {@link handle}).\n */\n private async callTool(params: unknown): Promise<McpToolCallResult> {\n const { name, args } = readCallParams(params);\n const contract = this.byName.get(name);\n\n if (!contract) {\n throw new McpTransportError(`Unknown tool \"${name}\".`, {\n type: \"protocol\",\n method: \"tools/call\",\n });\n }\n\n const result = await contract.invoke(args);\n\n if (result.error) {\n return {\n content: [{ type: \"text\", text: result.error.message }],\n isError: true,\n };\n }\n\n return {\n content: [{ type: \"text\", text: serializeData(result.data) }],\n isError: false,\n };\n }\n\n /** Build a JSON-RPC success response. */\n private ok(id: JsonRpcRequest[\"id\"], result: unknown): JsonRpcResponse {\n return { jsonrpc: JSONRPC_VERSION, id, result };\n }\n\n /** Build a JSON-RPC error response. */\n private error(id: JsonRpcRequest[\"id\"], code: number, message: string): JsonRpcResponse {\n return { jsonrpc: JSONRPC_VERSION, id, error: { code, message } };\n }\n}\n\n/**\n * Read and validate the `tools/call` params into `{ name, args }`. Throws\n * a typed {@link McpTransportError} when `name` is missing — mapped to a\n * JSON-RPC `invalid params` error by the handler.\n */\nfunction readCallParams(params: unknown): { name: string; args: unknown } {\n if (typeof params !== \"object\" || params === null) {\n throw new McpTransportError(\"tools/call params must be an object.\", {\n type: \"protocol\",\n method: \"tools/call\",\n });\n }\n\n const record = params as { name?: unknown; arguments?: unknown };\n\n if (typeof record.name !== \"string\") {\n throw new McpTransportError(\"tools/call requires a string `name`.\", {\n type: \"protocol\",\n method: \"tools/call\",\n });\n }\n\n return { name: record.name, args: record.arguments ?? {} };\n}\n\n/**\n * Serialize a tool's `data` for an MCP text content block — a string is\n * passed verbatim, everything else is JSON-stringified so structured\n * output crosses the wire as text the consuming client can re-parse.\n */\nfunction serializeData(data: unknown): string {\n if (typeof data === \"string\") {\n return data;\n }\n\n if (data === undefined) {\n return \"\";\n }\n\n return JSON.stringify(data);\n}\n\n/**\n * Build the pure protocol handler for a serve source. Exposed (alongside\n * {@link serve}) so callers and tests can drive the MCP protocol without an\n * actual transport — feed it a JSON-RPC request, get the response.\n *\n * @param source - The tools to expose (an object with `tools()` or a literal array).\n * @param options - Serve options (name / version / schema target).\n * @returns An object whose `handle(request)` maps a request to a response.\n */\nexport function createServeHandler(\n source: McpServeSource,\n options: McpServeOptions,\n): { handle(request: JsonRpcRequest): Promise<JsonRpcResponse> } {\n return new McpServeHandler(source, options);\n}\n\n/**\n * The internal {@link McpServer} — owns a {@link McpServeHandler} and a\n * transport pump. For `stdio` it reads newline-delimited JSON-RPC requests\n * from `process.stdin` and writes responses to `process.stdout`; the\n * `http` transport is accepted but listening is deferred to the host\n * (a serve-over-HTTP needs a server the caller owns).\n *\n * Constructed via {@link serve}; the class itself is internal.\n */\nclass McpServerImpl implements McpServer {\n /** The pure protocol handler. */\n private readonly handler: McpServeHandler;\n\n /** Serve options (transport selection lives here). */\n private readonly options: McpServeOptions;\n\n /** The stdin line reader while serving over stdio. */\n private reader: Interface | undefined;\n\n /** Flipped while the server is actively reading the transport. */\n private running = false;\n\n public constructor(source: McpServeSource, options: McpServeOptions) {\n this.handler = new McpServeHandler(source, options);\n this.options = options;\n }\n\n public async start(): Promise<void> {\n if (this.running) {\n return;\n }\n\n const transport = this.options.transport ?? { type: \"stdio\" };\n\n if (transport.type !== \"stdio\") {\n throw new McpTransportError(\n \"serve() over http requires a host-provided server; only stdio is auto-pumped.\",\n { type: \"connect\" },\n );\n }\n\n this.running = true;\n this.reader = createInterface({ input: process.stdin });\n\n this.reader.on(\"line\", (line) => {\n void this.onLine(line);\n });\n }\n\n /**\n * Parse one stdin line as a JSON-RPC request, dispatch it through the\n * handler, and write the response as a single line to stdout. Non-JSON\n * lines and notifications (no `id`) are ignored.\n */\n private async onLine(line: string): Promise<void> {\n const trimmed = line.trim();\n\n if (!trimmed) {\n return;\n }\n\n let request: JsonRpcRequest;\n\n try {\n request = JSON.parse(trimmed) as JsonRpcRequest;\n } catch {\n return;\n }\n\n if (request.id === undefined || request.id === null) {\n // A notification (e.g. notifications/initialized) — nothing to answer.\n return;\n }\n\n const response = await this.handler.handle(request);\n process.stdout.write(`${JSON.stringify(response)}\\n`);\n }\n\n public async stop(): Promise<void> {\n this.running = false;\n this.reader?.close();\n this.reader = undefined;\n }\n}\n\n/**\n * Expose a built agent / supervisor / orchestrator (or a raw\n * `ToolContract[]`) AS an MCP server (Direction B: local primitive → MCP\n * server other clients consume).\n *\n * Enumerates `source.tools()` (or the literal array) once at construction.\n * `tools/list` answers with each tool's `inputSchema` extracted via\n * `extractJsonSchema` at the configured `schemaTarget` (default\n * `draft-2020-12`). `tools/call` routes to the named contract's\n * `invoke()` and maps the never-throwing result — `data` becomes a text\n * content block, `error` becomes an `isError: true` result — so a failing\n * tool surfaces as a normal MCP tool error rather than crashing the server.\n *\n * The default transport is `stdio`, pumped over `process.stdin` /\n * `process.stdout`. Serving over HTTP is left to a host-owned server;\n * `start()` rejects an `http` transport (the protocol core is available\n * via {@link createServeHandler} for a caller's own HTTP wiring).\n *\n * @param source - The tools to expose.\n * @param options - Server name, version, transport, and schema dialect.\n * @returns An {@link McpServer} with `start()` / `stop()`.\n *\n * @example\n * serve(\n * { tools: () => ws.allTools() },\n * { name: \"warlock-workspace\", transport: { type: \"stdio\" } },\n * ).start();\n */\nexport function serve(source: McpServeSource, options: McpServeOptions): McpServer {\n return new McpServerImpl(source, options);\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"serve.mjs","names":[],"sources":["../../../../../../../ai-tools/src/mcp/serve.ts"],"sourcesContent":["import { createInterface, type Interface } from \"node:readline\";\nimport { extractJsonSchema, type ToolContract } from \"@warlock.js/ai\";\nimport type {\n JsonRpcRequest,\n JsonRpcResponse,\n McpServeOptions,\n McpServeSource,\n McpServer,\n McpToolCallResult,\n McpToolDescriptor,\n} from \"../contracts\";\nimport { McpTransportError } from \"../errors\";\n\n/** JSON-RPC version literal every outbound message carries. */\nconst JSONRPC_VERSION = \"2.0\";\n\n/** Default JSON-Schema dialect emitted for each tool's `inputSchema`. */\nconst DEFAULT_SCHEMA_TARGET = \"draft-2020-12\";\n\n/** Default advertised server version when the caller omits one. */\nconst DEFAULT_VERSION = \"4.4.0\";\n\n/** The MCP protocol version this server advertises in `initialize`. */\nconst PROTOCOL_VERSION = \"2025-06-18\";\n\n/** JSON-RPC standard error codes we emit. */\nconst JSON_RPC_METHOD_NOT_FOUND = -32601;\nconst JSON_RPC_INVALID_PARAMS = -32602;\n\n/**\n * Resolve the {@link McpServeSource} (either an object exposing `tools()`\n * or a literal `ToolContract[]`) into a flat contract array.\n */\nfunction resolveTools(source: McpServeSource): ToolContract[] {\n if (Array.isArray(source)) {\n return source;\n }\n\n return source.tools();\n}\n\n/**\n * The pure protocol core of `serve` — maps one JSON-RPC request to its\n * response, with no I/O. Both the stdio and http serve-transports pump\n * their inbound requests through this, and specs can drive it directly.\n *\n * Handles exactly the MCP slice this package serves: `initialize`,\n * `tools/list`, and `tools/call`. Any other method answers with a\n * JSON-RPC `method not found` error.\n *\n * Constructed via {@link createServeHandler}.\n */\nclass McpServeHandler {\n /** The tools this server exposes (snapshotted at construction). */\n private readonly tools: ToolContract[];\n\n /** Fast lookup by tool name for `tools/call` dispatch. */\n private readonly byName: Map<string, ToolContract>;\n\n /** Construction-time serve options (name / version / schema target). */\n private readonly options: McpServeOptions;\n\n public constructor(source: McpServeSource, options: McpServeOptions) {\n this.tools = resolveTools(source);\n this.byName = new Map(this.tools.map((contract) => [contract.name, contract]));\n this.options = options;\n }\n\n /**\n * Dispatch one inbound JSON-RPC request to its handler and produce the\n * response. A handler that throws is mapped to a JSON-RPC error response\n * — the serve loop never crashes on a bad request.\n */\n public async handle(request: JsonRpcRequest): Promise<JsonRpcResponse> {\n try {\n switch (request.method) {\n case \"initialize\":\n return this.ok(request.id, this.initializeResult());\n case \"tools/list\":\n return this.ok(request.id, { tools: this.listTools() });\n case \"tools/call\":\n return this.ok(request.id, await this.callTool(request.params));\n default:\n return this.error(\n request.id,\n JSON_RPC_METHOD_NOT_FOUND,\n `Method \"${request.method}\" is not supported by this MCP server.`,\n );\n }\n } catch (cause) {\n const message = cause instanceof Error ? cause.message : String(cause);\n\n return this.error(request.id, JSON_RPC_INVALID_PARAMS, message);\n }\n }\n\n /** Build the `initialize` result advertising name / version / capabilities. */\n private initializeResult(): Record<string, unknown> {\n return {\n protocolVersion: PROTOCOL_VERSION,\n capabilities: { tools: {} },\n serverInfo: {\n name: this.options.name,\n version: this.options.version ?? DEFAULT_VERSION,\n },\n };\n }\n\n /**\n * Build the `tools/list` payload: one {@link McpToolDescriptor} per\n * contract, its `inputSchema` extracted via `extractJsonSchema` at the\n * configured dialect (default `draft-2020-12` — overriding\n * `extractJsonSchema`'s own `openai-strict` default to a neutral MCP draft).\n */\n private listTools(): McpToolDescriptor[] {\n const target = this.options.schemaTarget ?? DEFAULT_SCHEMA_TARGET;\n\n return this.tools.map((contract) => {\n const inputSchema = extractJsonSchema(contract.input, { target }) ?? {\n type: \"object\",\n properties: {},\n };\n\n return {\n name: contract.name,\n description: contract.description,\n inputSchema,\n };\n });\n }\n\n /**\n * Route a `tools/call` to the named contract's `invoke()` and map the\n * never-throwing {@link import(\"@warlock.js/ai\").ToolInvokeResult}: `data`\n * → a text content block, `error` → an `isError: true` result. An unknown\n * tool name throws (mapped to a JSON-RPC error by {@link handle}).\n */\n private async callTool(params: unknown): Promise<McpToolCallResult> {\n const { name, args } = readCallParams(params);\n const contract = this.byName.get(name);\n\n if (!contract) {\n throw new McpTransportError(`Unknown tool \"${name}\".`, {\n type: \"protocol\",\n method: \"tools/call\",\n });\n }\n\n const result = await contract.invoke(args);\n\n if (result.error) {\n return {\n content: [{ type: \"text\", text: result.error.message }],\n isError: true,\n };\n }\n\n return {\n content: [{ type: \"text\", text: serializeData(result.data) }],\n isError: false,\n };\n }\n\n /** Build a JSON-RPC success response. */\n private ok(id: JsonRpcRequest[\"id\"], result: unknown): JsonRpcResponse {\n return { jsonrpc: JSONRPC_VERSION, id, result };\n }\n\n /** Build a JSON-RPC error response. */\n private error(id: JsonRpcRequest[\"id\"], code: number, message: string): JsonRpcResponse {\n return { jsonrpc: JSONRPC_VERSION, id, error: { code, message } };\n }\n}\n\n/**\n * Read and validate the `tools/call` params into `{ name, args }`. Throws\n * a typed {@link McpTransportError} when `name` is missing — mapped to a\n * JSON-RPC `invalid params` error by the handler.\n */\nfunction readCallParams(params: unknown): { name: string; args: unknown } {\n if (typeof params !== \"object\" || params === null) {\n throw new McpTransportError(\"tools/call params must be an object.\", {\n type: \"protocol\",\n method: \"tools/call\",\n });\n }\n\n const record = params as { name?: unknown; arguments?: unknown };\n\n if (typeof record.name !== \"string\") {\n throw new McpTransportError(\"tools/call requires a string `name`.\", {\n type: \"protocol\",\n method: \"tools/call\",\n });\n }\n\n return { name: record.name, args: record.arguments ?? {} };\n}\n\n/**\n * Serialize a tool's `data` for an MCP text content block — a string is\n * passed verbatim, everything else is JSON-stringified so structured\n * output crosses the wire as text the consuming client can re-parse.\n */\nfunction serializeData(data: unknown): string {\n if (typeof data === \"string\") {\n return data;\n }\n\n if (data === undefined) {\n return \"\";\n }\n\n return JSON.stringify(data);\n}\n\n/**\n * Build the pure protocol handler for a serve source. Exposed (alongside\n * {@link serve}) so callers and tests can drive the MCP protocol without an\n * actual transport — feed it a JSON-RPC request, get the response.\n *\n * @param source - The tools to expose (an object with `tools()` or a literal array).\n * @param options - Serve options (name / version / schema target).\n * @returns An object whose `handle(request)` maps a request to a response.\n */\nexport function createServeHandler(\n source: McpServeSource,\n options: McpServeOptions,\n): { handle(request: JsonRpcRequest): Promise<JsonRpcResponse> } {\n return new McpServeHandler(source, options);\n}\n\n/**\n * The internal {@link McpServer} — owns a {@link McpServeHandler} and a\n * transport pump. For `stdio` it reads newline-delimited JSON-RPC requests\n * from `process.stdin` and writes responses to `process.stdout`; the\n * `http` transport is accepted but listening is deferred to the host\n * (a serve-over-HTTP needs a server the caller owns).\n *\n * Constructed via {@link serve}; the class itself is internal.\n */\nclass McpServerImpl implements McpServer {\n /** The pure protocol handler. */\n private readonly handler: McpServeHandler;\n\n /** Serve options (transport selection lives here). */\n private readonly options: McpServeOptions;\n\n /** The stdin line reader while serving over stdio. */\n private reader: Interface | undefined;\n\n /** Flipped while the server is actively reading the transport. */\n private running = false;\n\n public constructor(source: McpServeSource, options: McpServeOptions) {\n this.handler = new McpServeHandler(source, options);\n this.options = options;\n }\n\n public async start(): Promise<void> {\n if (this.running) {\n return;\n }\n\n const transport = this.options.transport ?? { type: \"stdio\" };\n\n if (transport.type !== \"stdio\") {\n throw new McpTransportError(\n \"serve() over http requires a host-provided server; only stdio is auto-pumped.\",\n { type: \"connect\" },\n );\n }\n\n this.running = true;\n this.reader = createInterface({ input: process.stdin });\n\n this.reader.on(\"line\", (line) => {\n void this.onLine(line);\n });\n }\n\n /**\n * Parse one stdin line as a JSON-RPC request, dispatch it through the\n * handler, and write the response as a single line to stdout. Non-JSON\n * lines and notifications (no `id`) are ignored.\n */\n private async onLine(line: string): Promise<void> {\n const trimmed = line.trim();\n\n if (!trimmed) {\n return;\n }\n\n let request: JsonRpcRequest;\n\n try {\n request = JSON.parse(trimmed) as JsonRpcRequest;\n } catch {\n return;\n }\n\n if (request.id === undefined || request.id === null) {\n // A notification (e.g. notifications/initialized) — nothing to answer.\n return;\n }\n\n const response = await this.handler.handle(request);\n process.stdout.write(`${JSON.stringify(response)}\\n`);\n }\n\n public async stop(): Promise<void> {\n this.running = false;\n this.reader?.close();\n this.reader = undefined;\n }\n}\n\n/**\n * Expose a built agent / supervisor / orchestrator (or a raw\n * `ToolContract[]`) AS an MCP server (Direction B: local primitive → MCP\n * server other clients consume).\n *\n * Enumerates `source.tools()` (or the literal array) once at construction.\n * `tools/list` answers with each tool's `inputSchema` extracted via\n * `extractJsonSchema` at the configured `schemaTarget` (default\n * `draft-2020-12`). `tools/call` routes to the named contract's\n * `invoke()` and maps the never-throwing result — `data` becomes a text\n * content block, `error` becomes an `isError: true` result — so a failing\n * tool surfaces as a normal MCP tool error rather than crashing the server.\n *\n * The default transport is `stdio`, pumped over `process.stdin` /\n * `process.stdout`. Serving over HTTP is left to a host-owned server;\n * `start()` rejects an `http` transport (the protocol core is available\n * via {@link createServeHandler} for a caller's own HTTP wiring).\n *\n * @param source - The tools to expose.\n * @param options - Server name, version, transport, and schema dialect.\n * @returns An {@link McpServer} with `start()` / `stop()`.\n *\n * @example\n * serve(\n * { tools: () => ws.allTools() },\n * { name: \"warlock-workspace\", transport: { type: \"stdio\" } },\n * ).start();\n */\nexport function serve(source: McpServeSource, options: McpServeOptions): McpServer {\n return new McpServerImpl(source, options);\n}\n"],"mappings":"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"}
@@ -1,7 +1,7 @@
1
1
  import { McpTransport } from "../contracts/mcp.type.mjs";
2
2
  import { McpTransportClient } from "./transport.type.mjs";
3
3
 
4
- //#region ../@warlock.js/ai-tools/src/mcp/transport.d.ts
4
+ //#region ../ai-tools/src/mcp/transport.d.ts
5
5
  /**
6
6
  * Build the concrete {@link McpTransportClient} for an {@link McpTransport}
7
7
  * config — a {@link StdioTransport} for `type: "stdio"`, an
@@ -1 +1 @@
1
- {"version":3,"file":"transport.d.mts","names":[],"sources":["../../../../../../../@warlock.js/ai-tools/src/mcp/transport.ts"],"mappings":";;;;;;;AAicA;;;;;;iBAAgB,eAAA,CACd,SAAA,EAAW,YAAA,GACV,kBAAkB;EAAK,UAAA;AAAA;AAAU;AA8EpC;;;;AA9EoC,UA8EnB,mBAAA;EAMZ;EAJH,IAAA,oBACE,MAAA,UACA,MAAA,YACA,OAAA;IAAY,MAAA,GAAS,WAAA;IAAa,SAAA;EAAA,IACjC,OAAA,CAAQ,OAAA;EAJX;EAMA,MAAA,CAAO,MAAA,UAAgB,MAAA,aAAmB,OAAA;EALxC;EAOF,KAAA,IAAS,OAAA;AAAA;;;;;;;;;;;;;iBAeK,mBAAA,CACd,MAAA,EAAQ,YAAA,GAAe,kBAAA,GACtB,mBAAA"}
1
+ {"version":3,"file":"transport.d.mts","names":[],"sources":["../../../../../../../ai-tools/src/mcp/transport.ts"],"mappings":";;;;;;;AAicA;;;;;;iBAAgB,eAAA,CACd,SAAA,EAAW,YAAA,GACV,kBAAkB;EAAK,UAAA;AAAA;AAAU;AA8EpC;;;;AA9EoC,UA8EnB,mBAAA;EAMZ;EAJH,IAAA,oBACE,MAAA,UACA,MAAA,YACA,OAAA;IAAY,MAAA,GAAS,WAAA;IAAa,SAAA;EAAA,IACjC,OAAA,CAAQ,OAAA;EAJX;EAMA,MAAA,CAAO,MAAA,UAAgB,MAAA,aAAmB,OAAA;EALxC;EAOF,KAAA,IAAS,OAAA;AAAA;;;;;;;;;;;;;iBAeK,mBAAA,CACd,MAAA,EAAQ,YAAA,GAAe,kBAAA,GACtB,mBAAA"}
@@ -2,7 +2,7 @@ import { McpTransportError } from "../errors.mjs";
2
2
  import { spawn } from "node:child_process";
3
3
  import { createInterface } from "node:readline";
4
4
 
5
- //#region ../@warlock.js/ai-tools/src/mcp/transport.ts
5
+ //#region ../ai-tools/src/mcp/transport.ts
6
6
  /** Default per-request wait before a transport call is abandoned. */
7
7
  const DEFAULT_REQUEST_TIMEOUT_MS = 3e4;
8
8
  /** The JSON-RPC version literal every outbound message carries. */