@promptev/context-engine 0.0.2 → 0.0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/mcp.cjs CHANGED
@@ -1,16 +1,52 @@
1
1
  'use strict';
2
2
 
3
+ require('franc');
4
+ require('openai');
5
+ require('crypto');
6
+ require('jsonrepair');
7
+ require('cheerio');
8
+ require('exceljs');
9
+ require('jszip');
10
+ require('mammoth');
11
+ require('postal-mime');
3
12
  var zod = require('zod');
13
+ require('fs');
14
+ require('path');
4
15
 
5
- // src/mcp.ts
16
+ var __defProp = Object.defineProperty;
17
+ var __getOwnPropNames = Object.getOwnPropertyNames;
18
+ var __esm = (fn, res) => function __init() {
19
+ return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
20
+ };
21
+ var __export = (target, all) => {
22
+ for (var name in all)
23
+ __defProp(target, name, { get: all[name], enumerable: true });
24
+ };
6
25
 
7
26
  // src/errors.ts
8
- var ExtraMissingError = class extends Error {
9
- constructor(extra, pkg, what) {
10
- super(`${what} requires the '${pkg}' package (optional extra: ${extra}): npm install ${pkg}`);
11
- this.name = "ExtraMissingError";
27
+ var EngineActionError, DocumentNotFoundError, ExtraMissingError;
28
+ var init_errors = __esm({
29
+ "src/errors.ts"() {
30
+ EngineActionError = class extends Error {
31
+ constructor(message) {
32
+ super(message);
33
+ this.name = "EngineActionError";
34
+ }
35
+ };
36
+ DocumentNotFoundError = class extends Error {
37
+ constructor(documentId) {
38
+ super(`document not found: ${documentId}`);
39
+ this.name = "DocumentNotFoundError";
40
+ }
41
+ };
42
+ ExtraMissingError = class extends Error {
43
+ constructor(extra, pkg, what) {
44
+ super(`${what} requires the '${pkg}' package (optional extra: ${extra}): npm install ${pkg}`);
45
+ this.name = "ExtraMissingError";
46
+ }
47
+ };
12
48
  }
13
- };
49
+ });
14
50
 
15
51
  // src/extras.ts
16
52
  async function requireExtra(specifier, extra, what) {
@@ -20,11 +56,863 @@ async function requireExtra(specifier, extra, what) {
20
56
  throw new ExtraMissingError(extra, specifier, what);
21
57
  }
22
58
  }
59
+ var init_extras = __esm({
60
+ "src/extras.ts"() {
61
+ init_errors();
62
+ }
63
+ });
64
+ var UNICODE_NAME_RANGES;
65
+ var init_text = __esm({
66
+ "src/text.ts"() {
67
+ UNICODE_NAME_RANGES = `41,5a,124;61,7a,124;aa,aa,74;b5,b5,156;ba,ba,148;c0,d6,124;d8,f6,124;f8,2af,124;2b0,2c1,159;2c6,2c6,159;2c7,2c7,34;2c8,2d1,159;2e0,2e4,159;2ec,2ec,159;2ee,2ee,159;300,300,84;301,301,0;302,302,41;303,303,239;304,304,137;305,305,184;306,306,26;307,307,59;308,308,55;309,309,100;30a,30a,202;30b,30b,61;30c,30c,34;30d,30d,252;30e,30f,61;310,310,32;311,311,107;312,312,243;313,313,44;314,314,199;315,315,44;316,316,84;317,317,0;318,318,125;319,319,200;31a,31a,125;31b,31b,101;31c,31c,125;31d,31d,245;31e,31e,64;31f,31f,194;320,320,157;321,321,186;322,322,197;323,323,59;324,324,55;325,325,202;326,326,44;327,327,36;328,328,176;329,329,252;32a,32a,28;32b,32b,107;32c,32c,34;32d,32d,41;32e,32e,26;32f,32f,107;330,330,239;331,331,137;332,332,134;333,333,61;334,334,239;335,335,211;336,336,133;337,337,211;338,338,133;339,339,200;33a,33a,107;33b,33b,219;33c,33c,208;33d,33d,259;33e,33e,252;33f,33f,61;340,340,84;341,341,0;342,345,87;346,346,28;347,347,71;348,348,61;349,349,125;34a,34a,171;34b,34b,99;34c,34c,6;34d,34d,125;34e,34e,246;34f,34f,83;350,350,200;351,351,125;352,352,75;353,353,259;354,354,125;355,357,200;358,358,59;359,359,13;35a,35a,61;35b,35b,264;35c,362,61;363,36f,124;370,374,87;376,377,87;37a,37d,87;37f,37f,87;386,386,87;388,38a,87;38c,38c,87;38e,3a1,87;3a3,3e1,87;3e2,3ef,46;3f0,3f5,87;3f7,3ff,87;400,481,50;483,52f,50;531,556,12;559,559,12;560,588,12;591,5bd,96;5bf,5bf,96;5c1,5c2,96;5c4,5c5,96;5c7,5c7,96;5d0,5ea,96;5ef,5f2,96;610,61a,11;620,65f,11;66e,6d3,11;6d5,6dc,11;6df,6e8,11;6ea,6ef,11;6fa,6fc,11;6ff,6ff,11;710,74a,225;74d,74f,225;750,77f,11;780,7b1,234;7ca,7f5,170;7fa,7fa,170;7fd,7fd,170;800,82d,205;840,85b,144;860,86a,225;870,887,11;889,88e,11;898,8e1,11;8e3,8ff,11;900,963,54;971,97f,54;980,983,19;985,98c,19;98f,990,19;993,9a8,19;9aa,9b0,19;9b2,9b2,19;9b6,9b9,19;9bc,9c4,19;9c7,9c8,19;9cb,9ce,19;9d7,9d7,19;9dc,9dd,19;9df,9e3,19;9f0,9f1,19;9fc,9fc,19;9fe,9fe,19;a01,a03,90;a05,a0a,90;a0f,a10,90;a13,a28,90;a2a,a30,90;a32,a33,90;a35,a36,90;a38,a39,90;a3c,a3c,90;a3e,a42,90;a47,a48,90;a4b,a4d,90;a51,a51,90;a59,a5c,90;a5e,a5e,90;a70,a75,90;a81,a83,88;a85,a8d,88;a8f,a91,88;a93,aa8,88;aaa,ab0,88;ab2,ab3,88;ab5,ab9,88;abc,ac5,88;ac7,ac9,88;acb,acd,88;ad0,ad0,88;ae0,ae3,88;af9,aff,88;b01,b03,181;b05,b0c,181;b0f,b10,181;b13,b28,181;b2a,b30,181;b32,b33,181;b35,b39,181;b3c,b44,181;b47,b48,181;b4b,b4d,181;b55,b57,181;b5c,b5d,181;b5f,b63,181;b71,b71,181;b82,b83,230;b85,b8a,230;b8e,b90,230;b92,b95,230;b99,b9a,230;b9c,b9c,230;b9e,b9f,230;ba3,ba4,230;ba8,baa,230;bae,bb9,230;bbe,bc2,230;bc6,bc8,230;bca,bcd,230;bd0,bd0,230;bd7,bd7,230;c00,c0c,233;c0e,c10,233;c12,c28,233;c2a,c39,233;c3c,c44,233;c46,c48,233;c4a,c4d,233;c55,c56,233;c58,c5a,233;c5d,c5d,233;c60,c63,233;c80,c83,111;c85,c8c,111;c8e,c90,111;c92,ca8,111;caa,cb3,111;cb5,cb9,111;cbc,cc4,111;cc6,cc8,111;cca,ccd,111;cd5,cd6,111;cdd,cde,111;ce0,ce3,111;cf1,cf3,111;d00,d0c,143;d0e,d10,143;d12,d44,143;d46,d48,143;d4a,d4e,143;d54,d57,143;d5f,d63,143;d7a,d7f,143;d81,d83,214;d85,d96,214;d9a,db1,214;db3,dbb,214;dbd,dbd,214;dc0,dc6,214;dca,dca,214;dcf,dd4,214;dd6,dd6,214;dd8,ddf,214;df2,df3,214;e01,e3a,235;e40,e4e,235;e81,e82,123;e84,e84,123;e86,e8a,123;e8c,ea3,123;ea5,ea5,123;ea7,ebd,123;ec0,ec4,123;ec6,ec6,123;ec8,ece,123;edc,edf,123;f00,f00,237;f18,f19,237;f35,f35,237;f37,f37,237;f39,f39,237;f3e,f47,237;f49,f6c,237;f71,f84,237;f86,f97,237;f99,fbc,237;fc6,fc6,237;1000,103f,164;1050,108f,164;109a,109d,164;10a0,10c5,78;10c7,10c7,78;10cd,10cd,78;10d0,10fa,78;10fc,10fc,159;10fd,10ff,78;1100,11ff,92;1200,1248,72;124a,124d,72;1250,1256,72;1258,1258,72;125a,125d,72;1260,1288,72;128a,128d,72;1290,12b0,72;12b2,12b5,72;12b8,12be,72;12c0,12c0,72;12c2,12c5,72;12c8,12d6,72;12d8,1310,72;1312,1315,72;1318,135a,72;135d,135f,72;1380,138f,72;13a0,13f5,39;13f8,13fd,39;1401,166c,31;166f,167f,31;1681,169a,175;16a0,16ea,204;16f1,16f8,204;1700,1715,226;171f,171f,226;1720,1734,94;1740,1753,30;1760,176c,227;176e,1770,227;1772,1773,227;1780,17d3,120;17d7,17d7,120;17dc,17dd,120;180b,180d,160;180f,180f,160;1820,1878,160;1880,18aa,160;18b0,18f5,31;1900,191e,130;1920,192b,130;1930,193b,130;1950,196d,228;1970,1974,228;1980,19ab,168;19b0,19c9,168;1a00,1a1b,29;1a20,1a5e,228;1a60,1a7c,228;1a7f,1a7f,228;1aa7,1aa7,228;1ab0,1ab0,63;1ab1,1ab1,56;1ab2,1ab2,104;1ab3,1ab3,65;1ab4,1ab4,242;1ab5,1ab5,260;1ab6,1ab6,258;1ab7,1ab7,180;1ab8,1ab8,61;1ab9,1ab9,129;1aba,1aba,220;1abb,1abb,188;1abc,1abc,61;1abd,1abe,188;1abf,1ac0,124;1ac1,1ac1,125;1ac2,1ac2,200;1ac3,1ac3,125;1ac4,1ac4,200;1ac5,1ac5,219;1ac6,1ac6,172;1ac7,1ac7,107;1ac8,1ac8,194;1ac9,1aca,61;1acb,1acb,242;1acc,1ace,124;1b00,1b4c,15;1b6b,1b73,15;1b80,1baf,221;1bba,1bbf,221;1bc0,1bf3,18;1c00,1c37,127;1c4d,1c4f,127;1c5a,1c7d,178;1c80,1c88,50;1c90,1cba,78;1cbd,1cbf,78;1cd0,1cd2,251;1cd4,1cfa,251;1d00,1d25,124;1d26,1d2a,87;1d2b,1d2b,50;1d2c,1d61,159;1d62,1d65,124;1d66,1d6a,87;1d6b,1d77,124;1d78,1d78,159;1d79,1d9a,124;1d9b,1dbf,159;1dc0,1dc1,60;1dc2,1dc2,215;1dc3,1dc3,223;1dc4,1dc4,138;1dc5,1dc5,86;1dc6,1dc6,140;1dc7,1dc7,2;1dc8,1dc8,85;1dc9,1dc9,1;1dca,1dca,124;1dcb,1dcb,27;1dcc,1dcc,139;1dcd,1dcd,61;1dce,1dce,176;1dcf,1dcf,264;1dd0,1dd0,108;1dd1,1dd1,247;1dd2,1dd2,248;1dd3,1df4,124;1df5,1df5,245;1df6,1df7,114;1df8,1df8,59;1df9,1df9,257;1dfa,1dfa,59;1dfb,1dfb,52;1dfc,1dfc,61;1dfd,1dfd,6;1dfe,1dfe,125;1dff,1dff,200;1e00,1eff,124;1f00,1f15,87;1f18,1f1d,87;1f20,1f45,87;1f48,1f4d,87;1f50,1f57,87;1f59,1f59,87;1f5b,1f5b,87;1f5d,1f5d,87;1f5f,1f7d,87;1f80,1fb4,87;1fb6,1fbc,87;1fbe,1fbe,87;1fc2,1fc4,87;1fc6,1fcc,87;1fd0,1fd3,87;1fd6,1fdb,87;1fe0,1fec,87;1ff2,1ff4,87;1ff6,1ffc,87;2071,2071,222;207f,207f,222;2090,209c,124;20d0,20d0,125;20d1,20d1,200;20d2,20d2,133;20d3,20d3,211;20d4,20d4,10;20d5,20d5,43;20d6,20d6,125;20d7,20d7,200;20d8,20d8,202;20d9,20d9,43;20da,20da,10;20db,20db,236;20dc,20dc,76;20dd,20e0,70;20e1,20e1,125;20e2,20e4,70;20e5,20e5,198;20e6,20e6,61;20e7,20e7,9;20e8,20e8,242;20e9,20e9,257;20ea,20ea,126;20eb,20eb,133;20ec,20ec,201;20ed,20ed,126;20ee,20ee,125;20ef,20ef,200;20f0,20f0,13;2102,2102,62;2107,2107,73;210a,210b,207;210c,210c,23;210d,210d,62;210e,210f,193;2110,2110,207;2111,2111,23;2112,2113,207;2115,2115,62;2119,211a,62;211b,211b,207;211c,211c,23;211d,211d,62;2124,2124,62;2126,2126,177;2128,2128,23;212a,212a,117;212b,212b,8;212c,212c,207;212d,212d,23;212f,2131,207;2132,2132,243;2133,2134,207;2135,2135,5;2136,2136,20;2137,2137,79;2138,2138,51;2139,2139,105;213c,213f,62;2145,2149,62;214e,214e,243;2183,2183,203;2184,2184,124;2c00,2c5f,80;2c60,2c7c,124;2c7d,2c7d,159;2c7e,2c7f,124;2c80,2ce4,46;2ceb,2cf3,46;2d00,2d25,78;2d27,2d27,78;2d2d,2d2d,78;2d30,2d67,238;2d6f,2d6f,238;2d7f,2d7f,238;2d80,2d96,72;2da0,2da6,72;2da8,2dae,72;2db0,2db6,72;2db8,2dbe,72;2dc0,2dc6,72;2dc8,2dce,72;2dd0,2dd6,72;2dd8,2dde,72;2de0,2dff,50;2e2f,2e2f,252;3005,3006,102;302a,302d,102;302e,302f,92;3031,3035,252;303b,303b,252;303c,303c,149;3041,3096,98;3099,309a,113;309d,309f,98;30a1,30fa,112;30fc,30fc,113;30fd,30ff,112;3105,312f,24;3131,318e,92;31a0,31bf,24;31f0,31ff,112;3400,4dbf,42;4e00,9fff,42;a000,a48c,262;a4d0,a4fd,132;a500,a60c,249;a610,a61f,249;a62a,a62b,249;a640,a672,50;a674,a67d,50;a67f,a69b,50;a69c,a69d,159;a69e,a69f,50;a6a0,a6e5,16;a6f0,a6f1,16;a717,a71f,159;a722,a76f,124;a770,a770,159;a771,a787,124;a788,a788,159;a78b,a7ca,124;a7d0,a7d1,124;a7d3,a7d3,124;a7d5,a7d9,124;a7f2,a7f4,159;a7f5,a7f7,124;a7f8,a7f9,159;a7fa,a7ff,124;a800,a827,224;a82c,a82c,224;a840,a873,190;a880,a8c5,206;a8e0,a8f7,54;a8fb,a8fb,54;a8fd,a8ff,54;a90a,a92d,116;a930,a953,196;a960,a97c,92;a980,a9c0,109;a9cf,a9cf,109;a9e0,a9ef,164;a9fa,a9fe,164;aa00,aa36,38;aa40,aa4d,38;aa60,aa76,164;aa7a,aa7f,164;aa80,aac2,228;aadb,aadd,228;aae0,aaef,152;aaf2,aaf6,152;ab01,ab06,72;ab09,ab0e,72;ab11,ab16,72;ab20,ab26,72;ab28,ab2e,72;ab30,ab5a,124;ab5c,ab5f,159;ab60,ab64,124;ab65,ab65,87;ab66,ab68,124;ab69,ab69,159;ab70,abbf,39;abc0,abea,152;abec,abed,152;ac00,d7a3,92;d7b0,d7c6,92;d7cb,d7fb,92;f900,fa6d,42;fa70,fad9,42;fb00,fb06,124;fb13,fb17,12;fb1d,fb28,96;fb2a,fb36,96;fb38,fb3c,96;fb3e,fb3e,96;fb40,fb41,96;fb43,fb44,96;fb46,fb4f,96;fb50,fbb1,11;fbd3,fd3d,11;fd50,fd8f,11;fd92,fdc7,11;fdf0,fdfb,11;fe00,fe0f,250;fe20,fe21,128;fe22,fe23,61;fe24,fe25,137;fe26,fe26,45;fe27,fe28,128;fe29,fe2a,239;fe2b,fe2c,137;fe2d,fe2d,45;fe2e,fe2f,50;fe70,fe74,11;fe76,fefc,11;ff21,ff3a,77;ff41,ff5a,77;ff66,ffbe,91;ffc2,ffc7,91;ffca,ffcf,91;ffd2,ffd7,91;ffda,ffdc,91;10000,1000b,131;1000d,10026,131;10028,1003a,131;1003c,1003d,131;1003f,1004d,131;10050,1005d,131;10080,100fa,131;101fd,101fd,191;10280,1029c,135;102a0,102d0,33;102e0,102e0,46;10300,1031f,179;1032d,1032f,179;10330,10340,81;10342,10349,81;10350,1037a,179;10380,1039d,244;103a0,103c3,179;103c8,103cf,179;10400,1044f,53;10450,1047f,210;10480,1049d,183;104b0,104d3,182;104d8,104fb,182;10500,10527,68;10530,10563,35;10570,1057a,254;1057c,1058a,254;1058c,10592,254;10594,10595,254;10597,105a1,254;105a3,105b1,254;105b3,105b9,254;105bb,105bc,254;10600,10736,131;10740,10755,131;10760,10767,131;10780,10785,159;10787,107b0,159;107b2,107ba,159;10800,10805,48;10808,10808,48;1080a,10835,48;10837,10838,48;1083c,1083c,48;1083f,1083f,48;10840,10855,103;10860,10876,187;10880,1089e,165;108e0,108f2,95;108f4,108f5,95;10900,10915,192;10920,10939,136;10980,109b7,154;109be,109bf,154;10a00,10a03,118;10a05,10a06,118;10a0c,10a13,118;10a15,10a17,118;10a19,10a35,118;10a38,10a3a,118;10a3f,10a3f,118;10a60,10a7c,179;10a80,10a9c,179;10ac0,10ac7,145;10ac9,10ae6,145;10b00,10b35,14;10b40,10b55,106;10b60,10b72,106;10b80,10b91,195;10c00,10c48,179;10c80,10cb2,179;10cc0,10cf2,179;10d00,10d27,93;10e80,10ea9,261;10eab,10eac,261;10eb0,10eb1,261;10efd,10eff,11;10f00,10f1c,179;10f27,10f27,179;10f30,10f50,216;10f70,10f85,179;10fb0,10fc4,40;10fe0,10ff6,69;11000,11046,25;11070,11075,25;1107f,1107f,25;11080,110ba,110;110c2,110c2,110;110d0,110e8,217;11100,11134,37;11144,11147,37;11150,11173,141;11176,11176,141;11180,111c4,209;111c9,111cc,209;111ce,111cf,209;111da,111da,209;111dc,111dc,209;11200,11211,121;11213,11237,121;1123e,11241,121;11280,11286,162;11288,11288,162;1128a,1128d,162;1128f,1129d,162;1129f,112a8,162;112b0,112ea,122;11300,11303,82;11305,1130c,82;1130f,11310,82;11313,11328,82;1132a,11330,82;11332,11333,82;11335,11339,82;1133b,1133b,22;1133c,11344,82;11347,11348,82;1134b,1134d,82;11350,11350,82;11357,11357,82;1135d,11363,82;11366,1136c,82;11370,11374,82;11400,1144a,169;1145e,11461,169;11480,114c5,240;114c7,114c7,240;11580,115b5,212;115b8,115c0,212;115d8,115dd,212;11600,11640,158;11644,11644,158;11680,116b8,229;11700,1171a,4;1171d,1172b,4;11740,11746,4;11800,1183a,58;118a0,118df,256;118ff,118ff,256;11900,11906,57;11909,11909,57;1190c,11913,57;11915,11916,57;11918,11935,57;11937,11938,57;1193b,11943,57;119a0,119a7,167;119aa,119d7,167;119da,119e1,167;119e3,119e4,167;11a00,11a3e,263;11a47,11a47,263;11a50,11a99,218;11a9d,11a9d,218;11ab0,11abf,31;11ac0,11af8,189;11c00,11c08,21;11c0a,11c36,21;11c38,11c40,21;11c72,11c8f,146;11c92,11ca7,146;11ca9,11cb6,146;11d00,11d06,147;11d08,11d09,147;11d0b,11d36,147;11d3a,11d3a,147;11d3c,11d3d,147;11d3f,11d47,147;11d60,11d65,89;11d67,11d68,89;11d6a,11d8e,89;11d90,11d91,89;11d93,11d98,89;11ee0,11ef6,142;11f00,11f10,115;11f12,11f3a,115;11f3e,11f42,115;11fb0,11fb0,132;12000,12399,47;12480,12543,47;12f90,12ff0,49;13000,1342f,67;13440,13455,67;14400,14646,7;16800,16a38,16;16a40,16a5e,161;16a70,16abe,231;16ad0,16aed,17;16af0,16af4,17;16b00,16b36,185;16b40,16b43,185;16b63,16b77,185;16b7d,16b8f,185;16e40,16e7f,151;16f00,16f4a,155;16f4f,16f87,155;16f8f,16f9f,155;16fe0,16fe0,232;16fe1,16fe1,173;16fe3,16fe3,179;16fe4,16fe4,119;16ff0,16ff1,253;18800,18aff,232;18b00,18cd5,119;1aff0,1aff3,112;1aff5,1affb,112;1affd,1affe,112;1b000,1b000,112;1b001,1b001,98;1b002,1b11e,97;1b11f,1b11f,98;1b120,1b122,112;1b132,1b132,98;1b150,1b152,98;1b155,1b155,112;1b164,1b167,112;1b170,1b2fb,173;1bc00,1bc6a,66;1bc70,1bc7c,66;1bc80,1bc88,66;1bc90,1bc99,66;1bc9d,1bc9e,66;1cf00,1cf2d,265;1cf30,1cf46,265;1d165,1d169,163;1d16d,1d172,163;1d17b,1d182,163;1d185,1d18b,163;1d1aa,1d1ad,163;1d242,1d244,87;1d400,1d454,150;1d456,1d49c,150;1d49e,1d49f,150;1d4a2,1d4a2,150;1d4a5,1d4a6,150;1d4a9,1d4ac,150;1d4ae,1d4b9,150;1d4bb,1d4bb,150;1d4bd,1d4c3,150;1d4c5,1d505,150;1d507,1d50a,150;1d50d,1d514,150;1d516,1d51c,150;1d51e,1d539,150;1d53b,1d53e,150;1d540,1d544,150;1d546,1d546,150;1d54a,1d550,150;1d552,1d6a5,150;1d6a8,1d6c0,150;1d6c2,1d6da,150;1d6dc,1d6fa,150;1d6fc,1d714,150;1d716,1d734,150;1d736,1d74e,150;1d750,1d76e,150;1d770,1d788,150;1d78a,1d7a8,150;1d7aa,1d7c2,150;1d7c4,1d7cb,150;1da00,1da36,213;1da3b,1da6c,213;1da75,1da75,213;1da84,1da84,213;1da9b,1da9f,213;1daa1,1daaf,213;1df00,1df1e,124;1df25,1df2a,124;1e000,1e006,80;1e008,1e018,80;1e01b,1e021,80;1e023,1e024,80;1e026,1e02a,80;1e030,1e050,159;1e051,1e06a,50;1e06b,1e06d,159;1e08f,1e08f,50;1e100,1e12c,174;1e130,1e13d,174;1e14e,1e14e,174;1e290,1e2ae,241;1e2c0,1e2ef,255;1e4d0,1e4ef,166;1e7e0,1e7e6,72;1e7e8,1e7eb,72;1e7ed,1e7ee,72;1e7f0,1e7fe,72;1e800,1e8c4,153;1e8d0,1e8d6,153;1e900,1e94b,3;1ee00,1ee03,11;1ee05,1ee1f,11;1ee21,1ee22,11;1ee24,1ee24,11;1ee27,1ee27,11;1ee29,1ee32,11;1ee34,1ee37,11;1ee39,1ee39,11;1ee3b,1ee3b,11;1ee42,1ee42,11;1ee47,1ee47,11;1ee49,1ee49,11;1ee4b,1ee4b,11;1ee4d,1ee4f,11;1ee51,1ee52,11;1ee54,1ee54,11;1ee57,1ee57,11;1ee59,1ee59,11;1ee5b,1ee5b,11;1ee5d,1ee5d,11;1ee5f,1ee5f,11;1ee61,1ee62,11;1ee64,1ee64,11;1ee67,1ee6a,11;1ee6c,1ee72,11;1ee74,1ee77,11;1ee79,1ee7c,11;1ee7e,1ee7e,11;1ee80,1ee89,11;1ee8b,1ee9b,11;1eea1,1eea3,11;1eea5,1eea9,11;1eeab,1eebb,11;20000,2a6df,42;2a700,2b739,42;2b740,2b81d,42;2b820,2cea1,42;2ceb0,2ebe0,42;2ebf0,2ee5d,42;2f800,2fa1d,42;30000,3134a,42;31350,323af,42;e0100,e01ef,250`;
68
+ UNICODE_NAME_RANGES.split(";").map((entry) => {
69
+ const [start, end, word] = entry.split(",");
70
+ return { start: Number.parseInt(start, 16), end: Number.parseInt(end, 16), word: Number(word) };
71
+ });
72
+ }
73
+ });
74
+
75
+ // src/hooks.ts
76
+ var init_hooks = __esm({
77
+ "src/hooks.ts"() {
78
+ }
79
+ });
80
+
81
+ // src/providers/google.ts
82
+ var init_google = __esm({
83
+ "src/providers/google.ts"() {
84
+ init_errors();
85
+ }
86
+ });
87
+ var init_llm = __esm({
88
+ "src/providers/llm.ts"() {
89
+ init_errors();
90
+ init_google();
91
+ }
92
+ });
93
+ var init_crypto = __esm({
94
+ "src/crypto.ts"() {
95
+ }
96
+ });
97
+ var init_redaction = __esm({
98
+ "src/redaction.ts"() {
99
+ init_crypto();
100
+ init_hooks();
101
+ }
102
+ });
103
+ var init_json = __esm({
104
+ "src/extraction/json.ts"() {
105
+ }
106
+ });
107
+ var init_files = __esm({
108
+ "src/extraction/files.ts"() {
109
+ Buffer.from([80, 75, 3, 4]);
110
+ }
111
+ });
112
+ var init_extraction = __esm({
113
+ "src/extraction/index.ts"() {
114
+ init_extras();
115
+ init_hooks();
116
+ init_files();
117
+ }
118
+ });
119
+
120
+ // src/graph/retrieval.ts
121
+ async function corpusIsAclUniform(pool, sourceIds) {
122
+ const result = await pool.query(
123
+ `SELECT EXISTS(SELECT 1 FROM context_engine_chunks c
124
+ WHERE c.acl IS NOT NULL AND ($1::text[] IS NULL OR c.source_id = ANY($1::text[]))) AS has_acl`,
125
+ [sourceIds]
126
+ );
127
+ return !result.rows[0]?.has_acl;
128
+ }
129
+ async function shouldUseCommunitySummaries(pool, opts) {
130
+ if (opts.principals == null) return true;
131
+ return corpusIsAclUniform(pool, opts.sourceIds);
132
+ }
133
+ var init_retrieval = __esm({
134
+ "src/graph/retrieval.ts"() {
135
+ init_hooks();
136
+ }
137
+ });
138
+
139
+ // src/graph/navigation.ts
140
+ var navigation_exports = {};
141
+ __export(navigation_exports, {
142
+ SUMMARIES_WITHHELD: () => SUMMARIES_WITHHELD,
143
+ communitySummary: () => communitySummary,
144
+ findRelated: () => findRelated,
145
+ getNeighbors: () => getNeighbors,
146
+ traverse: () => traverse
147
+ });
148
+ function clamp(value, fallback, ceiling) {
149
+ const n = typeof value === "number" && Number.isFinite(value) ? Math.trunc(value) : fallback;
150
+ return Math.max(1, Math.min(n, ceiling));
151
+ }
152
+ function scopeArgs(opts) {
153
+ return [opts.sourceIds ?? null, opts.documentIds ?? null, opts.principals ?? null];
154
+ }
155
+ async function resolveEntity(pool, name, opts) {
156
+ const result = await pool.query(
157
+ `SELECT e.id::text, e.name, e.type FROM context_engine_entities e
158
+ WHERE e.normalized_name = $4 AND ${ENTITY_VISIBLE} LIMIT 1`,
159
+ [...scopeArgs(opts), (name ?? "").trim().toLowerCase()]
160
+ );
161
+ const row = result.rows[0];
162
+ return row ? { id: String(row.id), name: row.name, type: row.type } : null;
163
+ }
164
+ async function getNeighbors(pool, opts) {
165
+ const start = await resolveEntity(pool, opts.entity, opts);
166
+ if (!start) return { found: false, error: NOT_FOUND, neighbors: [], count: 0 };
167
+ const result = await pool.query(
168
+ `SELECT other.name, other.type, r.category, r.label, r.evidence,
169
+ CASE WHEN r.source_entity_id = $4::uuid THEN 'outgoing' ELSE 'incoming' END AS direction
170
+ FROM context_engine_entity_relationships r
171
+ JOIN context_engine_entities other ON other.id =
172
+ CASE WHEN r.source_entity_id = $4::uuid THEN r.target_entity_id ELSE r.source_entity_id END
173
+ WHERE (r.source_entity_id = $4::uuid OR r.target_entity_id = $4::uuid)
174
+ ${EDGE_VISIBLE}
175
+ ORDER BY other.name LIMIT $5`,
176
+ [...scopeArgs(opts), start.id, clamp(opts.limit, 20, MAX_LIMIT)]
177
+ );
178
+ const neighbors = result.rows.map((r) => ({
179
+ name: r.name,
180
+ type: r.type,
181
+ category: r.category,
182
+ label: r.label,
183
+ evidence: r.evidence,
184
+ direction: r.direction
185
+ }));
186
+ return {
187
+ found: true,
188
+ entity: { name: start.name, type: start.type },
189
+ neighbors,
190
+ count: neighbors.length
191
+ };
192
+ }
193
+ async function traverse(pool, opts) {
194
+ const start = await resolveEntity(pool, opts.entity, opts);
195
+ if (!start) return { found: false, error: NOT_FOUND, paths: [], count: 0 };
196
+ const result = await pool.query(
197
+ `WITH RECURSIVE walk AS (
198
+ SELECT r.id AS rel_id,
199
+ CASE WHEN r.source_entity_id = $4::uuid THEN r.target_entity_id ELSE r.source_entity_id END AS node_id,
200
+ r.category, r.label, r.evidence, 1 AS hop,
201
+ ARRAY[r.source_entity_id, r.target_entity_id] AS visited
202
+ FROM context_engine_entity_relationships r
203
+ WHERE (r.source_entity_id = $4::uuid OR r.target_entity_id = $4::uuid)
204
+ ${EDGE_VISIBLE}
205
+ UNION ALL
206
+ SELECT r.id,
207
+ CASE WHEN r.source_entity_id = w.node_id THEN r.target_entity_id ELSE r.source_entity_id END,
208
+ r.category, r.label, r.evidence, w.hop + 1,
209
+ w.visited || CASE WHEN r.source_entity_id = w.node_id THEN r.target_entity_id ELSE r.source_entity_id END
210
+ FROM walk w
211
+ JOIN context_engine_entity_relationships r
212
+ ON (r.source_entity_id = w.node_id OR r.target_entity_id = w.node_id)
213
+ WHERE w.hop < $5
214
+ AND NOT (CASE WHEN r.source_entity_id = w.node_id THEN r.target_entity_id ELSE r.source_entity_id END = ANY(w.visited))
215
+ ${EDGE_VISIBLE}
216
+ )
217
+ SELECT DISTINCT ON (w.node_id, w.category, w.label)
218
+ e.name, e.type, w.category, w.label, w.evidence, w.hop
219
+ FROM walk w JOIN context_engine_entities e ON e.id = w.node_id
220
+ WHERE ($6::text IS NULL OR w.category = $6::text)
221
+ ORDER BY w.node_id, w.category, w.label, w.hop ASC
222
+ LIMIT $7`,
223
+ [
224
+ ...scopeArgs(opts),
225
+ start.id,
226
+ clamp(opts.depth, 2, MAX_DEPTH),
227
+ opts.category ?? null,
228
+ clamp(opts.limit, 50, MAX_LIMIT)
229
+ ]
230
+ );
231
+ const paths = result.rows.map((r) => ({
232
+ target: r.name,
233
+ target_type: r.type,
234
+ category: r.category,
235
+ label: r.label,
236
+ evidence: r.evidence,
237
+ hops: r.hop
238
+ }));
239
+ paths.sort((a, b) => a.hops - b.hops || String(a.target).localeCompare(String(b.target)));
240
+ return { found: true, entity: { name: start.name, type: start.type }, paths, count: paths.length };
241
+ }
242
+ async function findRelated(pool, opts) {
243
+ const result = await pool.query(
244
+ `SELECT src.name AS src_name, src.type AS src_type, tgt.name AS tgt_name, tgt.type AS tgt_type,
245
+ r.category, r.label, r.evidence
246
+ FROM context_engine_entity_relationships r
247
+ JOIN context_engine_entities src ON src.id = r.source_entity_id
248
+ JOIN context_engine_entities tgt ON tgt.id = r.target_entity_id
249
+ WHERE ($4::text IS NULL OR r.category = $4::text)
250
+ AND ($5::text IS NULL OR r.label = $5::text)
251
+ AND ($6::text IS NULL OR src.type = $6::text OR tgt.type = $6::text)
252
+ ${EDGE_VISIBLE}
253
+ ORDER BY src.name, tgt.name LIMIT $7`,
254
+ [
255
+ ...scopeArgs(opts),
256
+ opts.category ?? null,
257
+ opts.label ?? null,
258
+ opts.entityType ?? null,
259
+ clamp(opts.limit, 50, MAX_LIMIT)
260
+ ]
261
+ );
262
+ const relationships = result.rows.map((r) => ({
263
+ source: r.src_name,
264
+ source_type: r.src_type,
265
+ target: r.tgt_name,
266
+ target_type: r.tgt_type,
267
+ category: r.category,
268
+ label: r.label,
269
+ evidence: r.evidence
270
+ }));
271
+ return { found: true, relationships, count: relationships.length };
272
+ }
273
+ async function communitySummary(pool, opts) {
274
+ const allowed = await shouldUseCommunitySummaries(pool, {
275
+ sourceIds: opts.sourceIds ?? null,
276
+ principals: opts.principals ?? null
277
+ });
278
+ if (!allowed) {
279
+ return { available: false, error: SUMMARIES_WITHHELD, communities: [], count: 0 };
280
+ }
281
+ const vectors = await opts.embedder.embed([opts.query ?? ""], "query");
282
+ const vector = vectors?.[0];
283
+ if (!vector) return { available: true, communities: [], count: 0 };
284
+ const result = await pool.query(
285
+ `SELECT summary, entity_count, relationship_count, level,
286
+ 1 - (embedding <=> CAST($1 AS vector))::float AS sim
287
+ FROM context_engine_communities
288
+ WHERE embedding IS NOT NULL AND summary IS NOT NULL
289
+ ORDER BY embedding <=> CAST($1 AS vector) LIMIT $2`,
290
+ [`[${vector.map((x) => Number(x)).join(",")}]`, clamp(opts.limit, 3, 10)]
291
+ );
292
+ const communities = result.rows.filter((r) => Number(r.sim) > MIN_COMMUNITY_RELEVANCE).map((r) => ({
293
+ summary: r.summary,
294
+ entity_count: r.entity_count,
295
+ relationship_count: r.relationship_count,
296
+ hierarchy_level: r.level,
297
+ relevance: Math.round(Number(r.sim) * 1e4) / 1e4
298
+ }));
299
+ return { available: true, communities, count: communities.length };
300
+ }
301
+ var SCOPE, EDGE_VISIBLE, ENTITY_VISIBLE, NOT_FOUND, MAX_DEPTH, MAX_LIMIT, MIN_COMMUNITY_RELEVANCE, SUMMARIES_WITHHELD;
302
+ var init_navigation = __esm({
303
+ "src/graph/navigation.ts"() {
304
+ init_retrieval();
305
+ SCOPE = `
306
+ AND ($1::text[] IS NULL OR c.source_id = ANY($1::text[]))
307
+ AND ($2::uuid[] IS NULL OR c.document_id = ANY($2::uuid[]))
308
+ AND ($3::text[] IS NULL OR c.acl IS NULL OR c.acl && $3::text[])
309
+ `;
310
+ EDGE_VISIBLE = `
311
+ AND (
312
+ ($3::text[] IS NULL AND r.chunk_id IS NULL)
313
+ OR EXISTS (
314
+ SELECT 1 FROM context_engine_chunks c
315
+ WHERE c.id = r.chunk_id ${SCOPE}
316
+ )
317
+ )
318
+ `;
319
+ ENTITY_VISIBLE = `
320
+ EXISTS (
321
+ SELECT 1 FROM context_engine_chunk_entities ce
322
+ JOIN context_engine_chunks c ON c.id = ce.chunk_id
323
+ WHERE ce.entity_id = e.id ${SCOPE}
324
+ )
325
+ `;
326
+ NOT_FOUND = "entity not found";
327
+ MAX_DEPTH = 5;
328
+ MAX_LIMIT = 200;
329
+ MIN_COMMUNITY_RELEVANCE = 0.25;
330
+ SUMMARIES_WITHHELD = "community summaries are withheld: this corpus mixes access-controlled and open documents, and a community summary is computed over the whole corpus, so it cannot be shown to a caller who can only see part of it. Use search, traverse or get_neighbors instead \u2014 those are filtered per document.";
331
+ }
332
+ });
333
+
334
+ // src/mcp.ts
335
+ init_errors();
336
+ init_extras();
337
+ var ENTITY_TYPES = [
338
+ "PERSON",
339
+ "ORG",
340
+ "PRODUCT",
341
+ "LOCATION",
342
+ "REFERENCE",
343
+ "TEMPORAL",
344
+ "CONCEPT"
345
+ ];
346
+ var RELATIONSHIP_CATEGORY_VALUES = [
347
+ "HIERARCHICAL",
348
+ "MEMBERSHIP",
349
+ "CREATION",
350
+ "TEMPORAL",
351
+ "SPATIAL",
352
+ "REFERENCE",
353
+ "FUNCTIONAL",
354
+ "QUANTITATIVE"
355
+ ];
356
+ new Set(RELATIONSHIP_CATEGORY_VALUES);
357
+
358
+ // src/chunkers.ts
359
+ init_text();
360
+ new Set(
361
+ "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~ \n\r\v\f"
362
+ );
363
+ function isTabularMime(mime) {
364
+ if (!mime) return false;
365
+ const m = mime.toLowerCase();
366
+ return ["csv", "sheet", "excel", "spreadsheetml", "tab-separated"].some((k) => m.includes(k));
367
+ }
368
+
369
+ // src/actions.ts
370
+ init_errors();
371
+ init_hooks();
372
+ init_llm();
373
+ init_redaction();
374
+
375
+ // src/sandbox.ts
376
+ init_errors();
377
+
378
+ // src/structured.ts
379
+ init_json();
380
+ init_llm();
381
+ init_text();
23
382
 
24
383
  // src/sentinels.ts
25
384
  var TRUSTED = /* @__PURE__ */ Symbol.for("context_engine.TRUSTED");
385
+ var UNSCOPED = /* @__PURE__ */ Symbol.for("context_engine.UNSCOPED");
386
+ init_extraction();
387
+ init_hooks();
388
+
389
+ // src/ingest.ts
390
+ init_redaction();
26
391
 
27
- // src/routing-core.ts
392
+ // src/actions.ts
393
+ var MAX_LIST_LIMIT = 200;
394
+
395
+ // src/knowledge-tool.ts
396
+ init_errors();
397
+ var KNOWLEDGE_ACTIONS = [
398
+ "discover",
399
+ "search",
400
+ "get_doc",
401
+ "list",
402
+ "query_meta",
403
+ "compute",
404
+ "get_chunks",
405
+ "get_docs",
406
+ "map_reduce",
407
+ "traverse",
408
+ "find_related",
409
+ "get_neighbors",
410
+ "community_summary"
411
+ ];
412
+ var KNOWLEDGE_TOOL_DESCRIPTION = "The knowledge base \u2014 the ingested documents and data files (PDF, Word, Excel, CSV and the rest) \u2014 as ONE tool with actions. Nothing is searched for you: use it before answering anything that should come from those documents, and do not use it for general knowledge. Call action 'discover' FIRST when you do not already know what is there: it lists the documents and says what is INSIDE each one \u2014 a spreadsheet's sheet names, column headers and row counts; a document's section titles and last page; a JSON file's top-level keys \u2014 plus the extracted field names grouped by document type with each field's data type and how many documents carry it, a census of the whole corpus, and which actions this deployment can run. Use those names verbatim: they are what compute, query_meta and get_chunks match on, so one discover is enough and you never have to go looking for a column, a section or a field name. A long list is shortened there, with the rest reported as a count and, when it is worth the trip, the exact call that returns it in full \u2014 discover again with document_ids set to that one document, which answers whole. When it is not worth the trip the payload says so and says what to do instead: a sheet with hundreds of columns is one to compute over, never one to read back. Then match the action to the task. 'search' finds passages by meaning or keywords \u2014 for questions answered by reading text. Looking up an identifier (an ID, code, SKU or invoice number) is the exception: search the BARE identifier alone, e.g. '2525', never the whole question. Those are CONTENT identifiers \u2014 written inside a document \u2014 and they belong in a query; a document_id is a system id (a uuid) that only discover, list or a search hit can give you, so never search a uuid as text and never hand an invoice number to get_doc. 'get_doc' reads one whole document by id, 'get_docs' reads several at once, and 'get_chunks' walks one long document in order a piece at a time when you need more of it than an excerpt; 'list' browses the documents without searching. 'map_reduce' asks the SAME question of every document in scope and answers once per document \u2014 for 'which contracts mention X', where search would return a handful of passages and miss the rest. 'query_meta' filters and aggregates documents by their structured fields (dates, amounts, categories) \u2014 usually the right action for a question about spreadsheet data. 'compute' runs code over the spreadsheets for any figure DERIVED from them \u2014 a total, average, count, ranking, margin or comparison across rows \u2014 AND for finding the exact row matching one id or value. In a large table, search cannot reliably locate an individual row; compute can. When the question is about how things are CONNECTED rather than what a document says \u2014 who works with whom, what belongs to what, what a change touches \u2014 use the graph actions: 'get_neighbors' for what is one step from one thing, 'traverse' for everything within a few steps of it, 'find_related' for connections of a kind across the corpus, and 'community_summary' for the themes the corpus groups into. They are available only where a graph was built; discover says so. Searching with mode='graph' ranks passages by those same connections instead of by wording alone, which finds a passage that never repeats your words. Rules that decide answers: search returns EXCERPTS, and rows of a spreadsheet are not arithmetic \u2014 never add up, average or rank rows yourself from what search returned, and never say a figure is not available before computing over the sheet that holds it. A listing that says has_more has MORE: send its next_cursor back as 'cursor' for the next page, and never conclude a document is absent from a first page that was truncated. Search again with different words before saying a document is missing. A result may carry a next_action (or next_page): it is the call to make next, already filled in \u2014 follow it rather than guessing the next step. Cite document names.";
413
+ var ACTION_PARAM_DESCRIPTION = "Match it to the task: reading questions -> search (a bare identifier for an ID or code); spreadsheet analysis, or the exact row for one id or value -> query_meta or compute; how things connect -> get_neighbors, traverse, find_related or community_summary; browse everything -> list; unsure what exists -> discover first.";
414
+ var ACTION_HELP = {
415
+ search: "passages by meaning or keywords; an ID, code or number as the BARE identifier",
416
+ get_doc: "one whole document by id \u2014 a clause, a policy, the full text",
417
+ list: "browse the documents without searching",
418
+ query_meta: "filter documents by structured fields (dates, amounts, categories)",
419
+ compute: "a figure DERIVED from the spreadsheets \u2014 total, average, count, ranking, margin, comparison, or the one row matching a value. Say what you want in plain words, not code: it runs over EVERY ROW of the sheets in scope, not a sample and not the excerpts search returned, so name the columns the way the sheet spells them and say which sheet if more than one could match. Never retype a figure from memory or from an excerpt \u2014 ask for it here",
420
+ get_chunks: "read one document in order, a range at a time \u2014 the long ones",
421
+ get_docs: "several whole documents at once, by id",
422
+ map_reduce: "ask the SAME question of every document in scope, one answer per document \u2014 for 'which contracts mention X', not for a figure (that is compute)",
423
+ get_neighbors: "what is ONE step from a named thing, and which way each link points",
424
+ traverse: "everything within a few steps of a named thing",
425
+ find_related: "connections of a given kind across the corpus, with no starting thing",
426
+ community_summary: "the themes the corpus groups into, ranked against a question"
427
+ };
428
+ var OFF = {
429
+ query_meta: "query_meta is off: this deployment has no LLM configured.",
430
+ compute: "compute is off: code execution is not enabled on this deployment. Quote the rows you found instead.",
431
+ map_reduce: "map_reduce is off: this deployment has no LLM configured. Use search, or read the documents with get_docs.",
432
+ graph: "the graph actions are off: this deployment has no graph configured, so nothing has been linked up. Use search, get_doc or list instead."
433
+ };
434
+ var GRAPH_ACTIONS = ["traverse", "find_related", "get_neighbors", "community_summary"];
435
+ var DOCUMENT_ID_ACTIONS = ["search", "compute", "get_docs", "map_reduce", "discover", "list"];
436
+ var DEFAULT_GET_DOCS_CHARS = 2e5;
437
+ var INPUT_PROPERTIES = {
438
+ action: { type: "string", enum: [...KNOWLEDGE_ACTIONS], description: ACTION_PARAM_DESCRIPTION },
439
+ query: {
440
+ type: "string",
441
+ description: "What you are looking for, in words \u2014 or a BARE content identifier when you are looking one up. (for search, query_meta, compute, community_summary)"
442
+ },
443
+ document_id: {
444
+ type: "string",
445
+ description: "One document's id, as returned by discover, list, or a search hit. A system id, never something written inside a document. (for get_doc)"
446
+ },
447
+ source_ids: {
448
+ type: "array",
449
+ items: { type: "string" },
450
+ description: "Narrows to these sources, using ids from discover or a search hit. Intersected with what this deployment allows: ids outside it are ignored, not refused. (for discover, list, search, query_meta, compute, and the graph actions)"
451
+ },
452
+ document_ids: {
453
+ type: "array",
454
+ items: { type: "string" },
455
+ description: "Narrows to these documents, using ids from discover, list or a search hit. It INTERSECTS with source_ids, so a document outside the sources you named returns nothing. On discover it also asks for those documents' structure IN FULL, past the shortening a whole-page discover applies. (for search, compute, get_docs, map_reduce, discover, list)"
456
+ },
457
+ entity: {
458
+ type: "string",
459
+ description: "The NAME of a thing to start from \u2014 a person, an organisation, a product \u2014 spelled as it appears in the documents. A name, NOT AN ID: take one from discover's graph section, from a search hit, or from an earlier get_neighbors answer. (for traverse, get_neighbors)"
460
+ },
461
+ depth: {
462
+ type: "integer",
463
+ description: "How many hops out to walk, 1 to 5. Default 2. (for traverse)"
464
+ },
465
+ category: {
466
+ type: "string",
467
+ enum: [...RELATIONSHIP_CATEGORY_VALUES],
468
+ description: "The kind of connection to follow or look for. These are the only values the graph holds; see discover for which occur in this corpus. (for traverse, find_related)"
469
+ },
470
+ label: {
471
+ type: "string",
472
+ description: "The exact relationship wording, as extracted \u2014 for example reports_to, works_for, owns. Narrower than category; use it when you know the phrasing, otherwise use category. (for find_related)"
473
+ },
474
+ entity_type: {
475
+ type: "string",
476
+ enum: [...ENTITY_TYPES],
477
+ description: "Keep only relationships with an entity of this type on one end. (for find_related)"
478
+ },
479
+ top_k: {
480
+ type: "integer",
481
+ description: "How many passages to return. Default 10. (for search)"
482
+ },
483
+ mode: {
484
+ type: "string",
485
+ enum: ["hybrid", "graph"],
486
+ description: "How to rank. OMIT IT and the right one is worked out from the documents in scope: wording and meaning, plus how things are connected whenever those documents were linked up \u2014 which finds a passage that never repeats your words. Name 'hybrid' to force wording only, or 'graph' to force the connection signal on. (for search)"
487
+ },
488
+ limit: {
489
+ type: "integer",
490
+ description: "How many rows to return: documents for discover and list (default 50), candidates for query_meta (20), relationships or paths for the graph actions (50), communities for community_summary (3). (for discover, list, query_meta, and the graph actions)"
491
+ },
492
+ cursor: {
493
+ type: "string",
494
+ description: "The next_cursor a previous page returned, sent back verbatim to read the rest. (for discover, list)"
495
+ },
496
+ start: {
497
+ type: "integer",
498
+ description: "First chunk position to read, 0-based. Use the next_start a previous answer returned to continue. (for get_chunks)"
499
+ },
500
+ end: {
501
+ type: "integer",
502
+ description: "Last chunk position to read, inclusive. At most 25 chunks come back at a time whatever you ask for. (for get_chunks)"
503
+ },
504
+ max_chars: {
505
+ type: "integer",
506
+ description: "How much document text to return in total before stopping and handing back the ids it did not reach. (for get_docs)"
507
+ }
508
+ };
509
+ function resolveScope(value) {
510
+ if (value === UNSCOPED) return { sourceIds: null, documentIds: null };
511
+ if (value == null) {
512
+ throw new Error(
513
+ "scope is required: pass the source ids this tool may reach, or UNSCOPED (from @promptev/context-engine) to say the whole corpus on purpose."
514
+ );
515
+ }
516
+ if (Array.isArray(value)) {
517
+ return { sourceIds: [...new Set(value.map(String))], documentIds: null };
518
+ }
519
+ if (typeof value === "object") {
520
+ return {
521
+ sourceIds: value.sourceIds != null ? [...new Set(value.sourceIds.map(String))] : null,
522
+ documentIds: value.documentIds != null ? [...new Set(value.documentIds.map(String))] : null
523
+ };
524
+ }
525
+ throw new Error(`scope must be UNSCOPED, an array of source ids, or a Scope \u2014 got ${typeof value}`);
526
+ }
527
+ function narrowToCeiling(requested, ceiling) {
528
+ if (ceiling == null) return requested != null ? [...new Set(requested)] : null;
529
+ if (requested == null) return [...ceiling];
530
+ const allowed = new Set(ceiling);
531
+ return [...new Set(requested)].filter((r) => allowed.has(r));
532
+ }
533
+ function withinCeiling(documentId, ceiling) {
534
+ return ceiling.documentIds == null || ceiling.documentIds.includes(String(documentId));
535
+ }
536
+ function knowledgeActionsAvailable(engine, opts = {}) {
537
+ const hasLlm = engine.config.llm != null;
538
+ const graphOn = Boolean(engine.config.graph?.enabled);
539
+ return {
540
+ search: true,
541
+ get_doc: true,
542
+ list: true,
543
+ query_meta: hasLlm,
544
+ compute: opts.compute != null || hasLlm && Boolean(engine.config.enableCodeExecution),
545
+ get_chunks: true,
546
+ get_docs: true,
547
+ map_reduce: opts.mapReduce != null || hasLlm,
548
+ traverse: graphOn,
549
+ find_related: graphOn,
550
+ get_neighbors: graphOn,
551
+ community_summary: graphOn
552
+ };
553
+ }
554
+ function documentKind(doc) {
555
+ return isTabularMime(String(doc.mimeType ?? doc.mime_type ?? "")) ? "spreadsheet" : "text";
556
+ }
557
+ function parseCursor(cursor) {
558
+ if (cursor == null || cursor === "") return null;
559
+ if (typeof cursor === "object" && !Array.isArray(cursor)) return cursor;
560
+ let parsed = null;
561
+ try {
562
+ parsed = JSON.parse(String(cursor));
563
+ } catch {
564
+ parsed = null;
565
+ }
566
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
567
+ throw new Error(`invalid cursor: ${JSON.stringify(cursor)}`);
568
+ }
569
+ return parsed;
570
+ }
571
+ async function listPage(engine, sourceIds, principals, action, limit, cursor, documentIds, ceiling, redaction) {
572
+ const page = await engine.listDocuments({
573
+ // `!= null`, NOT truthiness: an EMPTY array means "nothing is in scope"
574
+ // and collapsing it to null would list the whole corpus.
575
+ sourceIds: sourceIds != null ? [...new Set(sourceIds)] : null,
576
+ // Already intersected with the host's ceiling by `narrowToCeiling`, and
577
+ // filtered in SQL rather than after the page is built — three named
578
+ // documents sitting on page four must come back as themselves, not as an
579
+ // empty page.
580
+ documentIds: documentIds != null ? [...new Set(documentIds)] : null,
581
+ principals,
582
+ cursor,
583
+ limit: Math.max(1, Math.min(limit, MAX_LIST_LIMIT)),
584
+ redaction
585
+ });
586
+ let documents = (page.documents ?? []).map((raw) => ({
587
+ id: String(raw.id),
588
+ name: raw.name,
589
+ source_id: raw.sourceId ?? raw.source_id,
590
+ kind: documentKind(raw),
591
+ document_type: raw.documentType ?? raw.document_type ?? null,
592
+ mode: raw.mode ?? null
593
+ }));
594
+ if (ceiling.documentIds != null) {
595
+ const allowed = new Set(ceiling.documentIds);
596
+ documents = documents.filter((d) => allowed.has(d.id));
597
+ }
598
+ const out = {
599
+ documents,
600
+ count: documents.length,
601
+ has_more: Boolean(page.hasMore ?? page.has_more)
602
+ };
603
+ const next = page.nextCursor ?? page.next_cursor;
604
+ if (next != null) {
605
+ out.next_cursor = JSON.stringify(next);
606
+ out.next_page = { action, cursor: out.next_cursor };
607
+ }
608
+ return out;
609
+ }
610
+ async function callKnowledgeTool(engine, args) {
611
+ const action = args.action;
612
+ const principals = args.principals;
613
+ const ceiling = resolveScope(args.scope);
614
+ const requestedDocumentIds = args.document_ids;
615
+ const sourceIds = narrowToCeiling(args.source_ids, ceiling.sourceIds);
616
+ const documentIds = narrowToCeiling(args.document_ids, ceiling.documentIds);
617
+ const text = String(args.query ?? "").trim();
618
+ const available = knowledgeActionsAvailable(engine, {
619
+ compute: args.compute,
620
+ mapReduce: args.map_reduce
621
+ });
622
+ if (!KNOWLEDGE_ACTIONS.includes(action)) {
623
+ return {
624
+ success: false,
625
+ error: `unknown action '${action}' \u2014 use one of: ${KNOWLEDGE_ACTIONS.join(", ")}`
626
+ };
627
+ }
628
+ if (requestedDocumentIds?.length && !DOCUMENT_ID_ACTIONS.includes(action)) {
629
+ return {
630
+ success: false,
631
+ error: `document_ids is not honored by ${action} \u2014 only by ${DOCUMENT_ID_ACTIONS.join(", ")}. Narrow a listing with source_ids, or read one document with get_doc.`
632
+ };
633
+ }
634
+ if (GRAPH_ACTIONS.includes(action) && !available[action]) {
635
+ return { success: false, error: OFF.graph };
636
+ }
637
+ if (action === "discover" || action === "list") {
638
+ const page = await listPage(
639
+ engine,
640
+ sourceIds,
641
+ principals,
642
+ action,
643
+ args.limit ?? 50,
644
+ parseCursor(args.cursor),
645
+ documentIds,
646
+ ceiling,
647
+ args.redaction
648
+ );
649
+ if (action === "list") return { success: true, ...page };
650
+ const structures = await engine.documentStructure({
651
+ documentIds: page.documents.map((d) => d.id),
652
+ sourceIds,
653
+ principals,
654
+ // The cap is for the call that did NOT name its documents. A caller
655
+ // that asked about specific documents asked for all of them, and the
656
+ // truncated payload tells it to make exactly this call — so answering
657
+ // it truncated again would be a loop.
658
+ bounded: !requestedDocumentIds?.length,
659
+ redaction: args.redaction
660
+ });
661
+ for (const doc of page.documents) doc.structure = structures[doc.id] ?? {};
662
+ const hasSpreadsheet = page.documents.some((d) => d.kind === "spreadsheet");
663
+ const fieldsByType = await engine.fieldSummary({
664
+ sourceIds,
665
+ documentIds,
666
+ principals,
667
+ redaction: args.redaction
668
+ });
669
+ const census = await engine.documentTypes({
670
+ sourceIds,
671
+ documentIds,
672
+ principals,
673
+ redaction: args.redaction
674
+ });
675
+ const forAFact = { action: "search", query: "<bare identifier or key words>" };
676
+ const nextAction = {};
677
+ if (hasSpreadsheet && available.compute) {
678
+ nextAction["for a figure from a spreadsheet"] = {
679
+ action: "compute",
680
+ query: "<what to compute, columns as named above>"
681
+ };
682
+ }
683
+ nextAction["for a clause or a fact"] = forAFact;
684
+ if (fieldsByType.length && available.query_meta) {
685
+ nextAction["for documents by a field value"] = {
686
+ action: "query_meta",
687
+ query: "<a question naming a field from fields_by_type>"
688
+ };
689
+ }
690
+ if (available.get_neighbors) {
691
+ nextAction["for how things connect"] = {
692
+ action: "get_neighbors",
693
+ entity: "<a name that appears in the documents>"
694
+ };
695
+ }
696
+ const discovered = {
697
+ success: true,
698
+ ...page,
699
+ document_types: census,
700
+ fields_by_type: fieldsByType,
701
+ available_actions: Object.fromEntries(
702
+ Object.keys(ACTION_HELP).map((name) => [
703
+ name,
704
+ available[name] ? ACTION_HELP[name] : `${ACTION_HELP[name]} (OFF)`
705
+ ])
706
+ ),
707
+ next_action: nextAction
708
+ };
709
+ return discovered;
710
+ }
711
+ if (action === "search") {
712
+ if (!text) return { success: false, error: "search needs a query" };
713
+ const result = await engine.search(text, {
714
+ sourceIds,
715
+ documentIds,
716
+ principals,
717
+ topK: args.top_k ?? 10,
718
+ // Omitted means "work it out from the documents in scope".
719
+ mode: args.mode ?? null,
720
+ redaction: args.redaction
721
+ });
722
+ return {
723
+ success: true,
724
+ hits: (result.hits ?? []).map((hit) => {
725
+ const h = hit;
726
+ return {
727
+ document_id: h.document_id ?? h.documentId,
728
+ document_name: h.document_name ?? h.documentName,
729
+ chunk_text: h.chunk_text ?? h.chunkText,
730
+ score: h.score,
731
+ source_id: h.source_id ?? h.sourceId
732
+ };
733
+ }),
734
+ usage: result.usage
735
+ };
736
+ }
737
+ if (action === "get_doc") {
738
+ const documentId = args.document_id;
739
+ if (!documentId) return { success: false, error: "get_doc needs a document_id" };
740
+ if (!withinCeiling(documentId, ceiling)) {
741
+ return { success: false, error: `document not found: ${documentId}` };
742
+ }
743
+ try {
744
+ const document = await engine.getDocument(documentId, {
745
+ principals,
746
+ redaction: args.redaction
747
+ });
748
+ const src = document?.sourceId ?? document?.source_id;
749
+ if (ceiling.sourceIds != null && !ceiling.sourceIds.includes(String(src))) {
750
+ return { success: false, error: `document not found: ${documentId}` };
751
+ }
752
+ return { success: true, document };
753
+ } catch (err) {
754
+ if (err instanceof DocumentNotFoundError) return { success: false, error: err.message };
755
+ throw err;
756
+ }
757
+ }
758
+ if (action === "query_meta") {
759
+ if (!available.query_meta || !engine.queryStructured) return { success: false, error: OFF.query_meta };
760
+ if (!text) return { success: false, error: "query_meta needs a query" };
761
+ const out = await engine.queryStructured(text, {
762
+ sourceIds,
763
+ principals,
764
+ limit: Math.max(1, Math.min(args.limit ?? 20, 100)),
765
+ redaction: args.redaction
766
+ });
767
+ return { success: true, ...out };
768
+ }
769
+ if (action === "compute") {
770
+ if (!available.compute) return { success: false, error: OFF.compute };
771
+ if (!text) return { success: false, error: "compute needs a query: what to compute, in plain words" };
772
+ const runner = args.compute ?? engine.compute;
773
+ if (!runner) return { success: false, error: OFF.compute };
774
+ try {
775
+ const out = await runner(text, { sourceIds, documentIds, principals });
776
+ return { success: true, ...out };
777
+ } catch (err) {
778
+ if (err instanceof EngineActionError) return { success: false, error: err.message };
779
+ throw err;
780
+ }
781
+ }
782
+ if (action === "get_chunks") {
783
+ const documentId = args.document_id;
784
+ if (!documentId) return { success: false, error: "get_chunks needs a document_id" };
785
+ if (!withinCeiling(documentId, ceiling)) {
786
+ return { success: false, error: `document not found: ${documentId}` };
787
+ }
788
+ try {
789
+ const page = await engine.getChunks(documentId, {
790
+ principals,
791
+ start: args.start,
792
+ end: args.end,
793
+ redaction: args.redaction
794
+ });
795
+ if (ceiling.sourceIds != null) {
796
+ const doc = await engine.getDocument(documentId, { principals });
797
+ const src = doc?.sourceId ?? doc?.source_id;
798
+ if (!ceiling.sourceIds.includes(String(src))) {
799
+ return { success: false, error: `document not found: ${documentId}` };
800
+ }
801
+ }
802
+ const out = { success: true, ...page };
803
+ if (page.has_more) {
804
+ out.next_page = {
805
+ action: "get_chunks",
806
+ document_id: String(documentId),
807
+ start: page.next_start
808
+ };
809
+ }
810
+ return out;
811
+ } catch (err) {
812
+ if (err instanceof DocumentNotFoundError) return { success: false, error: err.message };
813
+ throw err;
814
+ }
815
+ }
816
+ if (action === "get_docs") {
817
+ if (!documentIds?.length) return { success: false, error: "get_docs needs document_ids" };
818
+ const docs = await engine.getDocuments(documentIds, { principals, redaction: args.redaction });
819
+ const budget = Math.max(1e3, Math.trunc(args.max_chars || DEFAULT_GET_DOCS_CHARS));
820
+ const kept = [];
821
+ let used = 0;
822
+ for (const doc of docs) {
823
+ const size = String(doc.text ?? "").length;
824
+ if (kept.length && used + size > budget) break;
825
+ kept.push(doc);
826
+ used += size;
827
+ }
828
+ const returned = new Set(kept.map((d) => String(d.id)));
829
+ const remaining = (args.document_ids ?? []).filter((d) => !returned.has(String(d)));
830
+ const out = { success: true, documents: kept, count: kept.length };
831
+ if (remaining.length) {
832
+ out.remaining_document_ids = remaining;
833
+ out.next_page = { action: "get_docs", document_ids: remaining };
834
+ }
835
+ return out;
836
+ }
837
+ if (action === "map_reduce") {
838
+ if (!available.map_reduce) return { success: false, error: OFF.map_reduce };
839
+ if (!text) {
840
+ return {
841
+ success: false,
842
+ error: "map_reduce needs a query: the question to ask of each document"
843
+ };
844
+ }
845
+ const runner = args.map_reduce ?? engine.mapReduce;
846
+ if (!runner) return { success: false, error: OFF.map_reduce };
847
+ try {
848
+ const out = await runner(text, {
849
+ sourceIds,
850
+ documentIds,
851
+ principals,
852
+ limit: args.limit
853
+ });
854
+ return { success: true, ...out };
855
+ } catch (err) {
856
+ if (err instanceof EngineActionError) return { success: false, error: err.message };
857
+ throw err;
858
+ }
859
+ }
860
+ return callGraphAction(engine, args, { sourceIds, principals, text });
861
+ }
862
+ async function callGraphAction(engine, args, ctx) {
863
+ const nav = await Promise.resolve().then(() => (init_navigation(), navigation_exports));
864
+ const pool = await engine.ensurePool?.();
865
+ const principals = ctx.principals ?? null;
866
+ const scoped = { sourceIds: ctx.sourceIds, principals };
867
+ if (args.action === "community_summary") {
868
+ if (!ctx.text) return { success: false, error: "community_summary needs a query" };
869
+ const out2 = await nav.communitySummary(pool, {
870
+ embedder: engine.embedder,
871
+ query: ctx.text,
872
+ limit: args.limit,
873
+ sourceIds: ctx.sourceIds,
874
+ principals
875
+ });
876
+ if (!out2.available) return { success: false, error: out2.error };
877
+ return { success: true, communities: out2.communities, count: out2.count };
878
+ }
879
+ if (args.action === "find_related") {
880
+ if (!(args.category || args.label || args.entity_type)) {
881
+ return {
882
+ success: false,
883
+ error: "find_related needs a category, a label or an entity_type to look for"
884
+ };
885
+ }
886
+ const out2 = await nav.findRelated(pool, {
887
+ ...scoped,
888
+ category: args.category,
889
+ label: args.label,
890
+ entityType: args.entity_type,
891
+ limit: args.limit
892
+ });
893
+ return { success: true, relationships: out2.relationships, count: out2.count };
894
+ }
895
+ if (!args.entity) {
896
+ return {
897
+ success: false,
898
+ error: `${args.action} needs an entity: the name of a thing to start from`
899
+ };
900
+ }
901
+ if (args.action === "get_neighbors") {
902
+ const out2 = await nav.getNeighbors(pool, { ...scoped, entity: args.entity, limit: args.limit });
903
+ if (!out2.found) return { success: false, error: out2.error };
904
+ return { success: true, entity: out2.entity, neighbors: out2.neighbors, count: out2.count };
905
+ }
906
+ const out = await nav.traverse(pool, {
907
+ ...scoped,
908
+ entity: args.entity,
909
+ depth: args.depth,
910
+ category: args.category,
911
+ limit: args.limit
912
+ });
913
+ if (!out.found) return { success: false, error: out.error };
914
+ return { success: true, entity: out.entity, paths: out.paths, count: out.count };
915
+ }
28
916
  var HandlerError = class extends Error {
29
917
  status;
30
918
  detail;
@@ -133,6 +1021,9 @@ function registerToolGateway(mcp, engine, principals, approvalScope = null) {
133
1021
  var __version__ = "0.0.0";
134
1022
 
135
1023
  // src/mcp.ts
1024
+ function described(name) {
1025
+ return String(INPUT_PROPERTIES[name]?.description ?? "");
1026
+ }
136
1027
  function asMcpResult(data) {
137
1028
  return { content: [{ type: "text", text: JSON.stringify(data) }] };
138
1029
  }
@@ -140,6 +1031,11 @@ async function createMcpApp(engine, opts) {
140
1031
  if (!opts?.principals) {
141
1032
  throw new TypeError("createMcpApp requires principals");
142
1033
  }
1034
+ if (opts.scope === void 0 || opts.scope === null) {
1035
+ throw new TypeError(
1036
+ "createMcpApp requires scope: the source ids this tool may reach, or UNSCOPED (from @promptev/context-engine) to say the whole corpus on purpose."
1037
+ );
1038
+ }
143
1039
  let McpServer;
144
1040
  let StreamableHTTPServerTransport;
145
1041
  try {
@@ -166,83 +1062,53 @@ async function createMcpApp(engine, opts) {
166
1062
  };
167
1063
  tool(
168
1064
  "search_knowledge_base",
169
- `Hybrid search (full-text + trigram + vector, RRF-fused) over the ingested corpus. Returns the top-matching chunks with their source document.${SCOPE_NOTE}`,
1065
+ `${KNOWLEDGE_TOOL_DESCRIPTION}${SCOPE_NOTE}`,
170
1066
  {
171
1067
  // Zod RAW SHAPES, not JSON schema: the SDK's isZodRawShape test
172
1068
  // rejects a plain schema object — on current SDK versions that made
173
1069
  // registration THROW at startup, and on 1.12.0 the object was consumed
174
1070
  // as annotations and every handler ran with NO arguments.
175
- query: zod.z.string(),
176
- source_ids: zod.z.array(zod.z.string()).optional(),
177
- document_ids: zod.z.array(zod.z.string()).optional(),
178
- top_k: zod.z.number().int().optional(),
179
- mode: zod.z.enum(["hybrid", "graph"]).optional()
1071
+ // Every `.describe()` comes from the ONE property table, so the schema
1072
+ // a model reads over the protocol and the exported JSON Schema cannot
1073
+ // say different things.
1074
+ action: zod.z.enum(KNOWLEDGE_ACTIONS).describe(described("action")),
1075
+ query: zod.z.string().optional().describe(described("query")),
1076
+ document_id: zod.z.string().optional().describe(described("document_id")),
1077
+ source_ids: zod.z.array(zod.z.string()).optional().describe(described("source_ids")),
1078
+ document_ids: zod.z.array(zod.z.string()).optional().describe(described("document_ids")),
1079
+ entity: zod.z.string().optional().describe(described("entity")),
1080
+ depth: zod.z.number().int().optional().describe(described("depth")),
1081
+ category: zod.z.enum(RELATIONSHIP_CATEGORY_VALUES).optional().describe(described("category")),
1082
+ label: zod.z.string().optional().describe(described("label")),
1083
+ entity_type: zod.z.enum(ENTITY_TYPES).optional().describe(described("entity_type")),
1084
+ top_k: zod.z.number().int().optional().describe(described("top_k")),
1085
+ mode: zod.z.enum(["hybrid", "graph"]).optional().describe(described("mode")),
1086
+ limit: zod.z.number().int().optional().describe(described("limit")),
1087
+ cursor: zod.z.union([zod.z.string(), zod.z.record(zod.z.unknown())]).optional().describe(described("cursor")),
1088
+ start: zod.z.number().int().optional().describe(described("start")),
1089
+ end: zod.z.number().int().optional().describe(described("end")),
1090
+ max_chars: zod.z.number().int().optional().describe(described("max_chars"))
180
1091
  },
181
1092
  async (args) => {
182
- for (const did of args.document_ids ?? []) {
1093
+ const documentIds = args.document_ids;
1094
+ const documentId = args.document_id;
1095
+ for (const did of [...documentIds ?? [], ...documentId ? [documentId] : []]) {
183
1096
  requireValidUuid(did);
184
1097
  }
185
1098
  const callerPrincipals = await resolvePrincipalsFn(opts.principals);
186
- const result = await engine.search(String(args.query ?? ""), {
187
- sourceIds: args.source_ids,
188
- documentIds: args.document_ids,
1099
+ const ceiling = typeof opts.scope === "function" ? await opts.scope() : opts.scope;
1100
+ const policy = typeof opts.redaction === "function" ? await opts.redaction() : opts.redaction;
1101
+ return callKnowledgeTool(engine, {
1102
+ ...args,
1103
+ action: String(args.action),
189
1104
  principals: callerPrincipals,
190
- topK: args.top_k ?? 10,
191
- mode: args.mode ?? "hybrid"
1105
+ scope: ceiling,
1106
+ redaction: policy,
1107
+ compute: opts.compute ?? null,
1108
+ map_reduce: opts.mapReduce ?? null
192
1109
  });
193
- return {
194
- hits: (result.hits ?? []).map((hit) => {
195
- const h = hit;
196
- return {
197
- document_name: h.document_name ?? h.documentName,
198
- chunk_text: h.chunk_text ?? h.chunkText,
199
- score: h.score,
200
- source_id: h.source_id ?? h.sourceId
201
- };
202
- }),
203
- usage: result.usage
204
- };
205
1110
  }
206
1111
  );
207
- tool(
208
- "get_document",
209
- `Fetch one document's full text and metadata by id.${SCOPE_NOTE}`,
210
- { document_id: zod.z.string() },
211
- async (args) => {
212
- const callerPrincipals = await resolvePrincipalsFn(opts.principals);
213
- return engine.getDocument(String(args.document_id), { principals: callerPrincipals });
214
- }
215
- );
216
- if (engine.config.llm != null) {
217
- tool(
218
- "query_structured",
219
- `Answer a question against documents' extracted structured data (only documents with structured data are candidates).${SCOPE_NOTE}`,
220
- { question: zod.z.string(), source_ids: zod.z.array(zod.z.string()).optional() },
221
- async (args) => {
222
- const callerPrincipals = await resolvePrincipalsFn(opts.principals);
223
- if (!engine.queryStructured) throw new Error("queryStructured is not available on this engine");
224
- return engine.queryStructured(String(args.question), {
225
- sourceIds: args.source_ids,
226
- principals: callerPrincipals
227
- });
228
- }
229
- );
230
- if (engine.config.enableCodeExecution) {
231
- tool(
232
- "compute",
233
- `Run LLM-authored JavaScript over in-scope spreadsheet documents and return the computed result.${SCOPE_NOTE}`,
234
- { instruction: zod.z.string(), source_ids: zod.z.array(zod.z.string()).optional() },
235
- async (args) => {
236
- const callerPrincipals = await resolvePrincipalsFn(opts.principals);
237
- if (!engine.compute) throw new Error("compute is not available on this engine");
238
- return engine.compute(String(args.instruction), {
239
- sourceIds: args.source_ids,
240
- principals: callerPrincipals
241
- });
242
- }
243
- );
244
- }
245
- }
246
1112
  registerToolGateway(
247
1113
  {
248
1114
  tool: (name, description, schema, handler2) => {