@agentforge/tools 0.16.76 → 0.16.78

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/index.cjs CHANGED
@@ -1,7 +1,9 @@
1
1
  'use strict';
2
2
 
3
3
  var core = require('@agentforge/core');
4
- var axios16 = require('axios');
4
+ var promises = require('dns/promises');
5
+ var net = require('net');
6
+ var axios13 = require('axios');
5
7
  var zod = require('zod');
6
8
  var cheerio = require('cheerio');
7
9
  var webApi = require('@slack/web-api');
@@ -14,7 +16,7 @@ var events = require('events');
14
16
  var stream = require('stream');
15
17
  var crypto = require('crypto');
16
18
  var fs = require('fs');
17
- var path7 = require('path');
19
+ var path = require('path');
18
20
  var dateFns = require('date-fns');
19
21
 
20
22
  function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
@@ -37,10 +39,11 @@ function _interopNamespace(e) {
37
39
  return Object.freeze(n);
38
40
  }
39
41
 
40
- var axios16__default = /*#__PURE__*/_interopDefault(axios16);
42
+ var net__default = /*#__PURE__*/_interopDefault(net);
43
+ var axios13__default = /*#__PURE__*/_interopDefault(axios13);
41
44
  var cheerio__namespace = /*#__PURE__*/_interopNamespace(cheerio);
42
45
  var neo4j__default = /*#__PURE__*/_interopDefault(neo4j);
43
- var path7__namespace = /*#__PURE__*/_interopNamespace(path7);
46
+ var path__namespace = /*#__PURE__*/_interopNamespace(path);
44
47
 
45
48
  var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
46
49
  get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
@@ -48,6 +51,204 @@ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require
48
51
  if (typeof require !== "undefined") return require.apply(this, arguments);
49
52
  throw Error('Dynamic require of "' + x + '" is not supported');
50
53
  });
54
+ var DEFAULT_MAX_REDIRECTS = 5;
55
+ var REDIRECT_STATUSES = /* @__PURE__ */ new Set([301, 302, 303, 307, 308]);
56
+ var IPV4_MAPPED_NETWORK = "::ffff:0:0";
57
+ var SENSITIVE_REDIRECT_HEADERS = /* @__PURE__ */ new Set(["authorization", "cookie", "proxy-authorization"]);
58
+ var DEFAULT_DESTINATION_POLICY = Object.freeze({
59
+ allowLocalhost: false,
60
+ allowPrivateNetwork: false,
61
+ allowLinkLocal: false,
62
+ allowMetadata: false,
63
+ allowRedirects: true,
64
+ maxRedirects: DEFAULT_MAX_REDIRECTS
65
+ });
66
+ var DestinationPolicyError = class extends Error {
67
+ constructor(url, reason, message) {
68
+ super(message);
69
+ this.url = url;
70
+ this.reason = reason;
71
+ this.name = "DestinationPolicyError";
72
+ }
73
+ code = "WEB_DESTINATION_BLOCKED";
74
+ };
75
+ function ipv4ToNumber(address) {
76
+ const octets = address.split(".").map(Number);
77
+ if (octets.length !== 4 || octets.some((octet) => !Number.isInteger(octet) || octet < 0 || octet > 255)) return void 0;
78
+ return ((octets[0] * 256 + octets[1]) * 256 + octets[2]) * 256 + octets[3] >>> 0;
79
+ }
80
+ function ipv4InCidr(address, network, prefix) {
81
+ const value = ipv4ToNumber(address);
82
+ const networkValue = ipv4ToNumber(network);
83
+ if (value === void 0 || networkValue === void 0) return false;
84
+ const mask = prefix === 0 ? 0 : 4294967295 << 32 - prefix >>> 0;
85
+ return (value & mask) === (networkValue & mask);
86
+ }
87
+ function expandIpv6(address) {
88
+ const normalized = address.toLowerCase().split("%")[0];
89
+ let input = normalized;
90
+ const ipv4Start = normalized.lastIndexOf(":") + 1;
91
+ const ipv4 = normalized.slice(ipv4Start);
92
+ if (net__default.default.isIP(ipv4) === 4) {
93
+ const value = ipv4ToNumber(ipv4);
94
+ if (value === void 0) return void 0;
95
+ input = `${normalized.slice(0, ipv4Start)}${(value >>> 16).toString(16)}:${(value & 65535).toString(16)}`;
96
+ }
97
+ const halves = input.split("::");
98
+ if (halves.length > 2) return void 0;
99
+ const left = halves[0] ? halves[0].split(":").filter(Boolean) : [];
100
+ const right = halves.length === 2 && halves[1] ? halves[1].split(":").filter(Boolean) : [];
101
+ const missing = 8 - left.length - right.length;
102
+ if (missing < 0 || halves.length === 1 && missing !== 0) return void 0;
103
+ const groups = [...left, ...Array.from({ length: missing }, () => "0"), ...right];
104
+ if (groups.length !== 8 || groups.some((group) => !/^[0-9a-f]{1,4}$/.test(group))) return void 0;
105
+ return groups.reduce((value, group) => value << 16n | BigInt(parseInt(group, 16)), 0n);
106
+ }
107
+ function ipv6InCidr(address, network, prefix) {
108
+ return prefix === 0 || address >> BigInt(128 - prefix) === network >> BigInt(128 - prefix);
109
+ }
110
+ function ipv6ToIpv4(address) {
111
+ const mappedNetwork = expandIpv6(IPV4_MAPPED_NETWORK);
112
+ if (mappedNetwork === void 0 || !ipv6InCidr(address, mappedNetwork, 96)) return void 0;
113
+ const value = Number(address & 0xffffffffn) >>> 0;
114
+ return [value >>> 24, value >>> 16 & 255, value >>> 8 & 255, value & 255].join(".");
115
+ }
116
+ function classifyIp(address) {
117
+ const normalized = address.replace(/^\[|\]$/g, "").toLowerCase();
118
+ const version = net__default.default.isIP(normalized);
119
+ if (version === 4) {
120
+ if (normalized === "169.254.169.254") return "metadata";
121
+ if (ipv4InCidr(normalized, "127.0.0.0", 8)) return "localhost";
122
+ if (ipv4InCidr(normalized, "169.254.0.0", 16)) return "link-local";
123
+ if (ipv4InCidr(normalized, "10.0.0.0", 8) || ipv4InCidr(normalized, "172.16.0.0", 12) || ipv4InCidr(normalized, "192.168.0.0", 16) || ipv4InCidr(normalized, "100.64.0.0", 10)) return "private-network";
124
+ return void 0;
125
+ }
126
+ if (version !== 6) return void 0;
127
+ const value = expandIpv6(normalized);
128
+ if (value === void 0) return void 0;
129
+ const mappedIpv4 = ipv6ToIpv4(value);
130
+ if (mappedIpv4) return classifyIp(mappedIpv4);
131
+ if (value === expandIpv6("fd00:ec2::254")) return "metadata";
132
+ if (value === expandIpv6("::1")) return "localhost";
133
+ if (ipv6InCidr(value, expandIpv6("fe80::"), 10)) return "link-local";
134
+ if (ipv6InCidr(value, expandIpv6("fc00::"), 7)) return "private-network";
135
+ return void 0;
136
+ }
137
+ function classifyHostname(hostname) {
138
+ const normalized = hostname.toLowerCase().replace(/\.$/, "");
139
+ if (normalized === "localhost" || normalized.endsWith(".localhost") || normalized === "localhost.localdomain") return "localhost";
140
+ if (normalized === "metadata" || normalized === "metadata.google.internal" || normalized === "instance-data") return "metadata";
141
+ return classifyIp(normalized);
142
+ }
143
+ function policyAllows(reason, policy) {
144
+ if (reason === "localhost") return policy.allowLocalhost === true;
145
+ if (reason === "metadata") return policy.allowMetadata === true;
146
+ if (reason === "private-network") return policy.allowPrivateNetwork === true;
147
+ if (reason === "link-local") return policy.allowLinkLocal === true;
148
+ return false;
149
+ }
150
+ async function resolveDestinationAddresses(hostname) {
151
+ try {
152
+ const addresses = await promises.lookup(hostname, { all: true, verbatim: true });
153
+ return addresses.map(({ address, family }) => ({ address, family }));
154
+ } catch (error) {
155
+ throw new DestinationPolicyError(hostname, "dns-resolution-failed", `Destination policy could not resolve hostname "${hostname}" before making a request: ${error instanceof Error ? error.message : String(error)}`);
156
+ }
157
+ }
158
+ function blockDestination(url, reason) {
159
+ throw new DestinationPolicyError(url, reason, `Destination blocked by web egress policy (${reason}): ${url}`);
160
+ }
161
+ async function validateDestinationAllowed(url, policy) {
162
+ const effectivePolicy = { ...DEFAULT_DESTINATION_POLICY, ...policy };
163
+ let parsed;
164
+ try {
165
+ parsed = new URL(url);
166
+ } catch {
167
+ blockDestination(url, "unsupported-protocol");
168
+ }
169
+ if (parsed.protocol !== "http:" && parsed.protocol !== "https:") blockDestination(url, "unsupported-protocol");
170
+ const hostname = parsed.hostname.replace(/^\[|\]$/g, "").toLowerCase();
171
+ const directReason = classifyHostname(hostname);
172
+ if (directReason && !policyAllows(directReason, effectivePolicy)) blockDestination(url, directReason);
173
+ const version = net__default.default.isIP(hostname);
174
+ if (version !== 0) return [{ address: hostname, family: version }];
175
+ const addresses = await resolveDestinationAddresses(hostname);
176
+ for (const { address } of addresses) {
177
+ const reason = classifyIp(address);
178
+ if (reason && !policyAllows(reason, effectivePolicy)) blockDestination(url, reason);
179
+ }
180
+ return addresses;
181
+ }
182
+ async function assertDestinationAllowed(url, policy = {}) {
183
+ await validateDestinationAllowed(url, policy);
184
+ }
185
+ function isRedirectStatus(status) {
186
+ return REDIRECT_STATUSES.has(status);
187
+ }
188
+ function responseLocation(response) {
189
+ const headers = response.headers;
190
+ return headers.location ?? headers.Location;
191
+ }
192
+ function stripSensitiveRedirectHeaders(headers) {
193
+ if (!headers) return headers;
194
+ const sanitizedHeaders = { ...headers };
195
+ for (const headerName of Object.keys(sanitizedHeaders)) {
196
+ if (SENSITIVE_REDIRECT_HEADERS.has(headerName.toLowerCase())) delete sanitizedHeaders[headerName];
197
+ }
198
+ return sanitizedHeaders;
199
+ }
200
+ function createPinnedLookup(addresses) {
201
+ return (_hostname, options, callback) => {
202
+ const requestedFamily = "family" in options && (options.family === 4 || options.family === 6) ? options.family : void 0;
203
+ const candidates = requestedFamily ? addresses.filter(({ family: family2 }) => family2 === requestedFamily) : addresses;
204
+ if (candidates.length === 0) {
205
+ callback(new Error(`No validated destination address matches the requested address family: ${requestedFamily}`), "", void 0);
206
+ return;
207
+ }
208
+ if ("all" in options && options.all === true) {
209
+ callback(null, candidates, void 0);
210
+ return;
211
+ }
212
+ const [{ address, family }] = candidates;
213
+ callback(null, address, family);
214
+ };
215
+ }
216
+ async function requestWithDestinationPolicy(config, policy = {}) {
217
+ if (!config.url) throw new Error("A request URL is required for destination policy enforcement");
218
+ const effectivePolicy = { ...DEFAULT_DESTINATION_POLICY, ...policy };
219
+ const maxRedirects = effectivePolicy.maxRedirects;
220
+ if (!Number.isInteger(maxRedirects) || maxRedirects < 0) throw new Error("maxRedirects must be a non-negative integer");
221
+ let currentUrl = config.url;
222
+ let currentConfig = {
223
+ ...config,
224
+ url: currentUrl,
225
+ maxRedirects: 0,
226
+ validateStatus: (status) => isRedirectStatus(status) || (config.validateStatus ? config.validateStatus(status) : status >= 200 && status < 300)
227
+ };
228
+ for (let redirectCount = 0; ; redirectCount += 1) {
229
+ const validatedAddresses = await validateDestinationAllowed(currentUrl, effectivePolicy);
230
+ currentConfig.lookup = createPinnedLookup(validatedAddresses);
231
+ const response = await axios13__default.default(currentConfig);
232
+ if (!isRedirectStatus(response.status)) return response;
233
+ if (effectivePolicy.allowRedirects === false) blockDestination(currentUrl, "redirect");
234
+ if (redirectCount >= maxRedirects) throw new DestinationPolicyError(currentUrl, "redirect", `Maximum redirect count (${maxRedirects}) exceeded for ${currentUrl}`);
235
+ const location = responseLocation(response);
236
+ if (!location) throw new DestinationPolicyError(currentUrl, "redirect", `Redirect response did not include a Location header: ${currentUrl}`);
237
+ const previousUrl = currentUrl;
238
+ try {
239
+ currentUrl = new URL(location, currentUrl).toString();
240
+ } catch {
241
+ throw new DestinationPolicyError(currentUrl, "redirect", `Redirect response included an invalid Location header: ${location}`);
242
+ }
243
+ const method = String(currentConfig.method ?? "GET").toUpperCase();
244
+ const nextMethod = response.status === 303 || (response.status === 301 || response.status === 302) && method === "POST" ? "GET" : method;
245
+ currentConfig = { ...currentConfig, url: currentUrl, method: nextMethod };
246
+ if (new URL(previousUrl).origin !== new URL(currentUrl).origin) {
247
+ currentConfig.headers = stripSensitiveRedirectHeaders(currentConfig.headers);
248
+ }
249
+ if (nextMethod === "GET" || nextMethod === "HEAD") delete currentConfig.data;
250
+ }
251
+ }
51
252
  var HttpMethod = zod.z.enum(["GET", "POST", "PUT", "DELETE", "PATCH", "HEAD", "OPTIONS"]);
52
253
  var httpRequestSchema = zod.z.object({
53
254
  url: zod.z.string().url().describe("The URL to make the request to"),
@@ -69,8 +270,8 @@ var httpPostSchema = zod.z.object({
69
270
  });
70
271
 
71
272
  // src/web/http/tools/http-client.ts
72
- function createHttpClientTool(defaultTimeout = 3e4, defaultHeaders = {}) {
73
- return core.toolBuilder().name("http-client").description("Make HTTP requests to web APIs and services. Supports GET, POST, PUT, DELETE, PATCH methods with custom headers and body.").category(core.ToolCategory.WEB).tags(["http", "api", "request", "web"]).schema(httpRequestSchema).implement(async (input) => {
273
+ function createHttpClientTool(defaultTimeout = 3e4, defaultHeaders = {}, destinationPolicy = {}) {
274
+ return core.toolBuilder().name("http-client").description("Make policy-checked HTTP requests to web APIs and services. Supports GET, POST, PUT, DELETE, PATCH methods with custom headers and body. Local, metadata, link-local, and private-network destinations are blocked by default.").category(core.ToolCategory.WEB).tags(["http", "api", "request", "web"]).schema(httpRequestSchema).implement(async (input) => {
74
275
  const config = {
75
276
  method: input.method,
76
277
  url: input.url,
@@ -81,37 +282,42 @@ function createHttpClientTool(defaultTimeout = 3e4, defaultHeaders = {}) {
81
282
  validateStatus: () => true
82
283
  // Don't throw on any status code
83
284
  };
84
- const response = await axios16__default.default(config);
285
+ const response = await requestWithDestinationPolicy(config, destinationPolicy);
85
286
  return {
86
287
  status: response.status,
87
288
  statusText: response.statusText,
88
289
  headers: response.headers,
89
290
  data: response.data,
90
- url: input.url,
291
+ url: response.config.url ?? input.url,
91
292
  method: input.method ?? "GET"
92
293
  };
93
294
  }).build();
94
295
  }
95
- function createHttpGetTool(defaultTimeout = 3e4, defaultHeaders = {}) {
96
- return core.toolBuilder().name("http-get").description("Make a simple HTTP GET request to a URL and return the response data.").category(core.ToolCategory.WEB).tags(["http", "get", "fetch", "web"]).schema(httpGetSchema).implement(async (input) => {
97
- const response = await axios16__default.default.get(input.url, {
296
+ function createHttpGetTool(defaultTimeout = 3e4, defaultHeaders = {}, destinationPolicy = {}) {
297
+ return core.toolBuilder().name("http-get").description("Make a policy-checked HTTP GET request to a URL and return the response data. Local, metadata, link-local, and private-network destinations are blocked by default.").category(core.ToolCategory.WEB).tags(["http", "get", "fetch", "web"]).schema(httpGetSchema).implement(async (input) => {
298
+ const response = await requestWithDestinationPolicy({
299
+ method: "GET",
300
+ url: input.url,
98
301
  headers: { ...defaultHeaders, ...input.headers },
99
302
  params: input.params,
100
303
  timeout: defaultTimeout
101
- });
304
+ }, destinationPolicy);
102
305
  return response.data;
103
306
  }).build();
104
307
  }
105
- function createHttpPostTool(defaultTimeout = 3e4, defaultHeaders = {}) {
106
- return core.toolBuilder().name("http-post").description("Make a simple HTTP POST request with JSON body and return the response data.").category(core.ToolCategory.WEB).tags(["http", "post", "api", "web"]).schema(httpPostSchema).implement(async (input) => {
107
- const response = await axios16__default.default.post(input.url, input.body, {
308
+ function createHttpPostTool(defaultTimeout = 3e4, defaultHeaders = {}, destinationPolicy = {}) {
309
+ return core.toolBuilder().name("http-post").description("Make a policy-checked HTTP POST request with JSON body and return the response data. Local, metadata, link-local, and private-network destinations are blocked by default.").category(core.ToolCategory.WEB).tags(["http", "post", "api", "web"]).schema(httpPostSchema).implement(async (input) => {
310
+ const response = await requestWithDestinationPolicy({
311
+ method: "POST",
312
+ url: input.url,
313
+ data: input.body,
108
314
  headers: {
109
315
  "Content-Type": "application/json",
110
316
  ...defaultHeaders,
111
317
  ...input.headers
112
318
  },
113
319
  timeout: defaultTimeout
114
- });
320
+ }, destinationPolicy);
115
321
  return response.data;
116
322
  }).build();
117
323
  }
@@ -122,11 +328,11 @@ var httpGet = createHttpGetTool();
122
328
  var httpPost = createHttpPostTool();
123
329
  var httpTools = [httpClient, httpGet, httpPost];
124
330
  function createHttpTools(config = {}) {
125
- const { defaultTimeout = 3e4, defaultHeaders = {} } = config;
331
+ const { defaultTimeout = 3e4, defaultHeaders = {}, destinationPolicy = {} } = config;
126
332
  return [
127
- createHttpClientTool(defaultTimeout, defaultHeaders),
128
- createHttpGetTool(defaultTimeout, defaultHeaders),
129
- createHttpPostTool(defaultTimeout, defaultHeaders)
333
+ createHttpClientTool(defaultTimeout, defaultHeaders, destinationPolicy),
334
+ createHttpGetTool(defaultTimeout, defaultHeaders, destinationPolicy),
335
+ createHttpPostTool(defaultTimeout, defaultHeaders, destinationPolicy)
130
336
  ];
131
337
  }
132
338
  var webScraperSchema = zod.z.object({
@@ -139,18 +345,20 @@ var webScraperSchema = zod.z.object({
139
345
  extractMetadata: zod.z.boolean().default(false).describe("Extract meta tags (title, description, etc.)"),
140
346
  timeout: zod.z.number().default(3e4).describe("Request timeout in milliseconds")
141
347
  });
142
- function createWebScraperTool(defaultTimeout = 3e4, userAgent = "Mozilla/5.0 (compatible; AgentForge/1.0; +https://agentforge.dev)") {
143
- return core.toolBuilder().name("web-scraper").description("Scrape and extract data from web pages. Can extract text, HTML, links, images, and use CSS selectors to target specific elements.").category(core.ToolCategory.WEB).tags(["scraper", "web", "html", "extract", "parse"]).schema(webScraperSchema).implement(async (input) => {
144
- const response = await axios16__default.default.get(input.url, {
348
+ function createWebScraperTool(defaultTimeout = 3e4, userAgent = "Mozilla/5.0 (compatible; AgentForge/1.0; +https://agentforge.dev)", destinationPolicy = {}) {
349
+ return core.toolBuilder().name("web-scraper").description("Scrape and extract data from policy-checked web pages. Local, metadata, link-local, and private-network destinations are blocked by default. Supports text, HTML, links, images, and CSS selectors.").category(core.ToolCategory.WEB).tags(["scraper", "web", "html", "extract", "parse"]).schema(webScraperSchema).implement(async (input) => {
350
+ const response = await requestWithDestinationPolicy({
351
+ method: "GET",
352
+ url: input.url,
145
353
  timeout: input.timeout || defaultTimeout,
146
354
  headers: {
147
355
  "User-Agent": userAgent
148
356
  }
149
- });
357
+ }, destinationPolicy);
150
358
  const html = response.data;
151
359
  const $ = cheerio__namespace.load(html);
152
360
  const result = {
153
- url: input.url
361
+ url: response.config.url ?? input.url
154
362
  };
155
363
  const $selected = input.selector ? $(input.selector) : $("body");
156
364
  if (input.extractText) {
@@ -215,8 +423,8 @@ function createWebScraperTool(defaultTimeout = 3e4, userAgent = "Mozilla/5.0 (co
215
423
  var webScraper = createWebScraperTool();
216
424
  var scraperTools = [webScraper];
217
425
  function createScraperTools(config = {}) {
218
- const { defaultTimeout = 3e4, userAgent = "Mozilla/5.0 (compatible; AgentForge/1.0; +https://agentforge.dev)" } = config;
219
- return [createWebScraperTool(defaultTimeout, userAgent)];
426
+ const { defaultTimeout = 3e4, userAgent = "Mozilla/5.0 (compatible; AgentForge/1.0; +https://agentforge.dev)", destinationPolicy = {} } = config;
427
+ return [createWebScraperTool(defaultTimeout, userAgent, destinationPolicy)];
220
428
  }
221
429
  var htmlParserSchema = zod.z.object({
222
430
  html: zod.z.string().describe("The HTML content to parse"),
@@ -490,7 +698,7 @@ function isRetryableError(error) {
490
698
  return false;
491
699
  }
492
700
  function sleep(ms) {
493
- return new Promise((resolve2) => setTimeout(resolve2, ms));
701
+ return new Promise((resolve3) => setTimeout(resolve3, ms));
494
702
  }
495
703
  async function retryWithBackoff(fn, config = DEFAULT_RETRY_CONFIG) {
496
704
  let lastError;
@@ -531,7 +739,7 @@ var DuckDuckGoProvider = class {
531
739
  async search(query, maxResults, timeout = DEFAULT_TIMEOUT) {
532
740
  return retryWithBackoff(async () => {
533
741
  try {
534
- const response = await axios16__default.default.get(
742
+ const response = await axios13__default.default.get(
535
743
  "https://api.duckduckgo.com/",
536
744
  {
537
745
  params: {
@@ -635,7 +843,7 @@ var SerperProvider = class {
635
843
  }
636
844
  return retryWithBackoff(async () => {
637
845
  try {
638
- const response = await axios16__default.default.post(
846
+ const response = await axios13__default.default.post(
639
847
  "https://google.serper.dev/search",
640
848
  {
641
849
  q: query,
@@ -1069,7 +1277,7 @@ function createSearchConfluenceTool(getAuth, getAuthHeader2, logger19) {
1069
1277
  logger19.info("search-confluence called", { query, limit });
1070
1278
  try {
1071
1279
  const { ATLASSIAN_SITE_URL } = getAuth();
1072
- const response = await axios16__default.default.get(`${ATLASSIAN_SITE_URL}/wiki/rest/api/content/search`, {
1280
+ const response = await axios13__default.default.get(`${ATLASSIAN_SITE_URL}/wiki/rest/api/content/search`, {
1073
1281
  headers: {
1074
1282
  Authorization: getAuthHeader2(),
1075
1283
  Accept: "application/json"
@@ -1131,7 +1339,7 @@ function createGetConfluencePageTool(getAuth, getAuthHeader2, logger19) {
1131
1339
  logger19.info("get-confluence-page called", { page_id });
1132
1340
  try {
1133
1341
  const { ATLASSIAN_SITE_URL } = getAuth();
1134
- const response = await axios16__default.default.get(`${ATLASSIAN_SITE_URL}/wiki/rest/api/content/${page_id}`, {
1342
+ const response = await axios13__default.default.get(`${ATLASSIAN_SITE_URL}/wiki/rest/api/content/${page_id}`, {
1135
1343
  headers: {
1136
1344
  Authorization: getAuthHeader2(),
1137
1345
  Accept: "application/json"
@@ -1182,7 +1390,7 @@ function createListConfluenceSpacesTool(getAuth, getAuthHeader2, logger19) {
1182
1390
  logger19.info("list-confluence-spaces called", { limit });
1183
1391
  try {
1184
1392
  const { ATLASSIAN_SITE_URL } = getAuth();
1185
- const response = await axios16__default.default.get(`${ATLASSIAN_SITE_URL}/wiki/rest/api/space`, {
1393
+ const response = await axios13__default.default.get(`${ATLASSIAN_SITE_URL}/wiki/rest/api/space`, {
1186
1394
  headers: {
1187
1395
  Authorization: getAuthHeader2(),
1188
1396
  Accept: "application/json"
@@ -1228,7 +1436,7 @@ function createGetSpacePagesTool(getAuth, getAuthHeader2, logger19) {
1228
1436
  logger19.info("get-space-pages called", { space_key, limit });
1229
1437
  try {
1230
1438
  const { ATLASSIAN_SITE_URL } = getAuth();
1231
- const response = await axios16__default.default.get(`${ATLASSIAN_SITE_URL}/wiki/rest/api/content`, {
1439
+ const response = await axios13__default.default.get(`${ATLASSIAN_SITE_URL}/wiki/rest/api/content`, {
1232
1440
  headers: {
1233
1441
  Authorization: getAuthHeader2(),
1234
1442
  Accept: "application/json"
@@ -1302,7 +1510,7 @@ function createCreateConfluencePageTool(getAuth, getAuthHeader2, logger19) {
1302
1510
  if (parent_page_id) {
1303
1511
  pageData.ancestors = [{ id: parent_page_id }];
1304
1512
  }
1305
- const response = await axios16__default.default.post(
1513
+ const response = await axios13__default.default.post(
1306
1514
  `${ATLASSIAN_SITE_URL}/wiki/rest/api/content`,
1307
1515
  pageData,
1308
1516
  {
@@ -1350,7 +1558,7 @@ function createUpdateConfluencePageTool(getAuth, getAuthHeader2, logger19) {
1350
1558
  logger19.info("update-confluence-page called", { page_id, title });
1351
1559
  try {
1352
1560
  const { ATLASSIAN_SITE_URL } = getAuth();
1353
- const getResponse = await axios16__default.default.get(
1561
+ const getResponse = await axios13__default.default.get(
1354
1562
  `${ATLASSIAN_SITE_URL}/wiki/rest/api/content/${page_id}`,
1355
1563
  {
1356
1564
  headers: {
@@ -1360,7 +1568,7 @@ function createUpdateConfluencePageTool(getAuth, getAuthHeader2, logger19) {
1360
1568
  }
1361
1569
  );
1362
1570
  const currentVersion = getResponse.data.version.number;
1363
- const updateResponse = await axios16__default.default.put(
1571
+ const updateResponse = await axios13__default.default.put(
1364
1572
  `${ATLASSIAN_SITE_URL}/wiki/rest/api/content/${page_id}`,
1365
1573
  {
1366
1574
  type: "page",
@@ -1421,7 +1629,7 @@ function createArchiveConfluencePageTool(getAuth, getAuthHeader2, logger19) {
1421
1629
  });
1422
1630
  try {
1423
1631
  const { ATLASSIAN_SITE_URL } = getAuth();
1424
- const getResponse = await axios16__default.default.get(
1632
+ const getResponse = await axios13__default.default.get(
1425
1633
  `${ATLASSIAN_SITE_URL}/wiki/rest/api/content/${page_id}`,
1426
1634
  {
1427
1635
  headers: {
@@ -1432,7 +1640,7 @@ function createArchiveConfluencePageTool(getAuth, getAuthHeader2, logger19) {
1432
1640
  );
1433
1641
  const currentVersion = getResponse.data.version.number;
1434
1642
  const pageData = getResponse.data;
1435
- await axios16__default.default.put(
1643
+ await axios13__default.default.put(
1436
1644
  `${ATLASSIAN_SITE_URL}/wiki/rest/api/content/${page_id}`,
1437
1645
  {
1438
1646
  version: { number: currentVersion + 1 },
@@ -1999,8 +2207,8 @@ var objectOmitSchema = zod.z.object({
1999
2207
  });
2000
2208
 
2001
2209
  // src/data/transformer/tools/shared.ts
2002
- function getNestedValue(value, path12) {
2003
- return path12.split(".").reduce((current, key) => {
2210
+ function getNestedValue(value, path13) {
2211
+ return path13.split(".").reduce((current, key) => {
2004
2212
  if (current == null) {
2005
2213
  return void 0;
2006
2214
  }
@@ -2390,7 +2598,7 @@ function isRetryableError2(error) {
2390
2598
  return false;
2391
2599
  }
2392
2600
  function sleep2(ms) {
2393
- return new Promise((resolve2) => setTimeout(resolve2, ms));
2601
+ return new Promise((resolve3) => setTimeout(resolve3, ms));
2394
2602
  }
2395
2603
  async function retryWithBackoff2(fn, config = DEFAULT_RETRY_CONFIG2) {
2396
2604
  let lastError;
@@ -2497,7 +2705,7 @@ var OpenAIEmbeddingProvider = class {
2497
2705
  const modelToUse = model || this.defaultModel;
2498
2706
  return retryWithBackoff2(async () => {
2499
2707
  try {
2500
- const response = await axios16__default.default.post(
2708
+ const response = await axios13__default.default.post(
2501
2709
  `${this.baseURL}/embeddings`,
2502
2710
  {
2503
2711
  input: text,
@@ -2561,7 +2769,7 @@ var OpenAIEmbeddingProvider = class {
2561
2769
  const modelToUse = model || this.defaultModel;
2562
2770
  return retryWithBackoff2(async () => {
2563
2771
  try {
2564
- const response = await axios16__default.default.post(
2772
+ const response = await axios13__default.default.post(
2565
2773
  `${this.baseURL}/embeddings`,
2566
2774
  {
2567
2775
  input: texts,
@@ -2641,7 +2849,7 @@ var CohereEmbeddingProvider = class {
2641
2849
  const modelToUse = model || this.defaultModel;
2642
2850
  return retryWithBackoff2(async () => {
2643
2851
  try {
2644
- const response = await axios16__default.default.post(
2852
+ const response = await axios13__default.default.post(
2645
2853
  `${this.baseUrl}/embed`,
2646
2854
  {
2647
2855
  texts,
@@ -2669,7 +2877,7 @@ var CohereEmbeddingProvider = class {
2669
2877
  } : void 0
2670
2878
  };
2671
2879
  } catch (error) {
2672
- if (axios16__default.default.isAxiosError(error)) {
2880
+ if (axios13__default.default.isAxiosError(error)) {
2673
2881
  const status = error.response?.status;
2674
2882
  const message = error.response?.data?.message || error.message;
2675
2883
  let wrappedError;
@@ -2709,7 +2917,7 @@ var HuggingFaceEmbeddingProvider = class {
2709
2917
  const modelToUse = model || this.defaultModel;
2710
2918
  return retryWithBackoff2(async () => {
2711
2919
  try {
2712
- const response = await axios16__default.default.post(
2920
+ const response = await axios13__default.default.post(
2713
2921
  `${this.baseUrl}/${modelToUse}`,
2714
2922
  {
2715
2923
  inputs: text,
@@ -2731,7 +2939,7 @@ var HuggingFaceEmbeddingProvider = class {
2731
2939
  dimensions: embedding.length
2732
2940
  };
2733
2941
  } catch (error) {
2734
- if (axios16__default.default.isAxiosError(error)) {
2942
+ if (axios13__default.default.isAxiosError(error)) {
2735
2943
  const status = error.response?.status;
2736
2944
  const message = error.response?.data?.error || error.message;
2737
2945
  let wrappedError;
@@ -2758,7 +2966,7 @@ var HuggingFaceEmbeddingProvider = class {
2758
2966
  const modelToUse = model || this.defaultModel;
2759
2967
  return retryWithBackoff2(async () => {
2760
2968
  try {
2761
- const response = await axios16__default.default.post(
2969
+ const response = await axios13__default.default.post(
2762
2970
  `${this.baseUrl}/${modelToUse}`,
2763
2971
  {
2764
2972
  inputs: texts,
@@ -2781,7 +2989,7 @@ var HuggingFaceEmbeddingProvider = class {
2781
2989
  dimensions
2782
2990
  };
2783
2991
  } catch (error) {
2784
- if (axios16__default.default.isAxiosError(error)) {
2992
+ if (axios13__default.default.isAxiosError(error)) {
2785
2993
  const status = error.response?.status;
2786
2994
  const message = error.response?.data?.error || error.message;
2787
2995
  let wrappedError;
@@ -2832,7 +3040,7 @@ var VoyageEmbeddingProvider = class {
2832
3040
  const modelToUse = model || this.defaultModel;
2833
3041
  return retryWithBackoff2(async () => {
2834
3042
  try {
2835
- const response = await axios16__default.default.post(
3043
+ const response = await axios13__default.default.post(
2836
3044
  `${this.baseUrl}/embeddings`,
2837
3045
  {
2838
3046
  input: texts,
@@ -2859,7 +3067,7 @@ var VoyageEmbeddingProvider = class {
2859
3067
  } : void 0
2860
3068
  };
2861
3069
  } catch (error) {
2862
- if (axios16__default.default.isAxiosError(error)) {
3070
+ if (axios13__default.default.isAxiosError(error)) {
2863
3071
  const status = error.response?.status;
2864
3072
  const message = error.response?.data?.message || error.message;
2865
3073
  let wrappedError;
@@ -2898,7 +3106,7 @@ var OllamaEmbeddingProvider = class {
2898
3106
  const modelToUse = model || this.defaultModel;
2899
3107
  return retryWithBackoff2(async () => {
2900
3108
  try {
2901
- const response = await axios16__default.default.post(
3109
+ const response = await axios13__default.default.post(
2902
3110
  `${this.baseUrl}/api/embeddings`,
2903
3111
  {
2904
3112
  model: modelToUse,
@@ -2917,7 +3125,7 @@ var OllamaEmbeddingProvider = class {
2917
3125
  dimensions: embedding.length
2918
3126
  };
2919
3127
  } catch (error) {
2920
- if (axios16__default.default.isAxiosError(error)) {
3128
+ if (axios13__default.default.isAxiosError(error)) {
2921
3129
  const status = error.response?.status;
2922
3130
  const message = error.response?.data?.error || error.message;
2923
3131
  let wrappedError;
@@ -3289,16 +3497,16 @@ function formatRelationship(rel) {
3289
3497
  properties: formatValue(rel.properties)
3290
3498
  };
3291
3499
  }
3292
- function formatPath(path12) {
3500
+ function formatPath(path13) {
3293
3501
  return {
3294
- start: formatNode(path12.start),
3295
- end: formatNode(path12.end),
3296
- segments: path12.segments.map((segment) => ({
3502
+ start: formatNode(path13.start),
3503
+ end: formatNode(path13.end),
3504
+ segments: path13.segments.map((segment) => ({
3297
3505
  start: formatNode(segment.start),
3298
3506
  relationship: formatRelationship(segment.relationship),
3299
3507
  end: formatNode(segment.end)
3300
3508
  })),
3301
- length: path12.length
3509
+ length: path13.length
3302
3510
  };
3303
3511
  }
3304
3512
  function formatResults(records) {
@@ -5475,8 +5683,8 @@ function toBatchErrorMessage(error) {
5475
5683
  return error instanceof Error ? error.message : String(error);
5476
5684
  }
5477
5685
  function delayBatchRetry(ms) {
5478
- return new Promise((resolve2) => {
5479
- setTimeout(resolve2, ms);
5686
+ return new Promise((resolve3) => {
5687
+ setTimeout(resolve3, ms);
5480
5688
  });
5481
5689
  }
5482
5690
 
@@ -5902,7 +6110,7 @@ function withTransactionTimeout(operation, timeoutMs, onTimeout) {
5902
6110
  if (!timeoutMs) {
5903
6111
  return operation;
5904
6112
  }
5905
- return new Promise((resolve2, reject) => {
6113
+ return new Promise((resolve3, reject) => {
5906
6114
  let timedOut = false;
5907
6115
  const timer = setTimeout(() => {
5908
6116
  timedOut = true;
@@ -5914,7 +6122,7 @@ function withTransactionTimeout(operation, timeoutMs, onTimeout) {
5914
6122
  return;
5915
6123
  }
5916
6124
  clearTimeout(timer);
5917
- resolve2(result);
6125
+ resolve3(result);
5918
6126
  }).catch((error) => {
5919
6127
  if (timedOut) {
5920
6128
  return;
@@ -9147,11 +9355,135 @@ var fileDeleteSchema = zod.z.object({
9147
9355
  var fileExistsSchema = zod.z.object({
9148
9356
  path: zod.z.string().describe("Path to check")
9149
9357
  });
9150
- function createFileReaderTool(defaultEncoding = "utf8") {
9358
+ var FileSystemPolicyError = class extends Error {
9359
+ constructor(message, operation, inputPath) {
9360
+ super(message);
9361
+ this.operation = operation;
9362
+ this.inputPath = inputPath;
9363
+ this.name = "FileSystemPolicyError";
9364
+ }
9365
+ code = "FILE_SYSTEM_POLICY_VIOLATION";
9366
+ };
9367
+ function containsTraversal(inputPath) {
9368
+ return inputPath.split(/[\\/]+/).includes("..");
9369
+ }
9370
+ function isWithinRoot(root, target) {
9371
+ const relative4 = path__namespace.relative(root, target);
9372
+ return relative4 === "" || !relative4.startsWith(`..${path__namespace.sep}`) && relative4 !== ".." && !path__namespace.isAbsolute(relative4);
9373
+ }
9374
+ async function resolveWithMissingTail(inputPath) {
9375
+ let currentPath = inputPath;
9376
+ const missingSegments = [];
9377
+ while (true) {
9378
+ try {
9379
+ const resolvedPath = await fs.promises.realpath(currentPath);
9380
+ return path__namespace.join(resolvedPath, ...missingSegments);
9381
+ } catch (error) {
9382
+ const code = error.code;
9383
+ if (code !== "ENOENT" && code !== "ENOTDIR") {
9384
+ throw error;
9385
+ }
9386
+ const parentPath = path__namespace.dirname(currentPath);
9387
+ if (parentPath === currentPath) {
9388
+ throw error;
9389
+ }
9390
+ missingSegments.unshift(path__namespace.basename(currentPath));
9391
+ currentPath = parentPath;
9392
+ }
9393
+ }
9394
+ }
9395
+ var FileSystemPolicy = class {
9396
+ allowedRoots;
9397
+ allowOutsideRoots;
9398
+ allowRootDeletion;
9399
+ baseDirectory;
9400
+ constructor(options = {}) {
9401
+ const configuredRoots = [
9402
+ ...options.allowedRoots ?? [],
9403
+ ...options.workspaceRoot ? [options.workspaceRoot] : []
9404
+ ];
9405
+ this.allowedRoots = [...new Set(configuredRoots.map((root) => path__namespace.resolve(root)))];
9406
+ this.baseDirectory = path__namespace.resolve(options.workspaceRoot ?? process.cwd());
9407
+ this.allowOutsideRoots = options.allowOutsideRoots ?? this.allowedRoots.length === 0;
9408
+ this.allowRootDeletion = options.allowRootDeletion ?? this.allowOutsideRoots;
9409
+ }
9410
+ async resolvePath(inputPath, operation) {
9411
+ const { resolvedPath } = await this.resolvePathWithRoots(inputPath, operation);
9412
+ return resolvedPath;
9413
+ }
9414
+ async resolveDeletePath(inputPath, recursive) {
9415
+ const { resolvedPath, resolvedRoots } = await this.resolvePathWithRoots(inputPath, "directory deletion");
9416
+ if (recursive && !this.allowRootDeletion && resolvedRoots.some((root) => root === resolvedPath)) {
9417
+ throw new FileSystemPolicyError(
9418
+ "Filesystem policy blocked directory deletion: recursive deletion of an allowed root is not permitted",
9419
+ "directory deletion",
9420
+ inputPath
9421
+ );
9422
+ }
9423
+ return resolvedPath;
9424
+ }
9425
+ async resolvePathWithRoots(inputPath, operation) {
9426
+ const absolutePath = path__namespace.resolve(this.baseDirectory, inputPath);
9427
+ if (this.allowOutsideRoots) {
9428
+ return { resolvedPath: absolutePath, resolvedRoots: [] };
9429
+ }
9430
+ if (containsTraversal(inputPath)) {
9431
+ throw new FileSystemPolicyError(
9432
+ `Filesystem policy blocked ${operation}: path traversal is not allowed`,
9433
+ operation,
9434
+ inputPath
9435
+ );
9436
+ }
9437
+ let resolvedPath;
9438
+ try {
9439
+ resolvedPath = await resolveWithMissingTail(absolutePath);
9440
+ } catch {
9441
+ throw new FileSystemPolicyError(
9442
+ `Filesystem policy could not resolve ${operation} path`,
9443
+ operation,
9444
+ inputPath
9445
+ );
9446
+ }
9447
+ const resolvedRoots = await this.resolveRoots(operation, inputPath);
9448
+ if (!resolvedRoots.some((root) => isWithinRoot(root, resolvedPath))) {
9449
+ throw new FileSystemPolicyError(
9450
+ `Filesystem policy blocked ${operation}: path is outside the allowed roots`,
9451
+ operation,
9452
+ inputPath
9453
+ );
9454
+ }
9455
+ return { resolvedPath, resolvedRoots };
9456
+ }
9457
+ async resolveRoots(operation, inputPath) {
9458
+ try {
9459
+ return await Promise.all(this.allowedRoots.map((root) => fs.promises.realpath(root)));
9460
+ } catch {
9461
+ throw new FileSystemPolicyError(
9462
+ `Filesystem policy could not resolve an allowed root for ${operation}`,
9463
+ operation,
9464
+ inputPath
9465
+ );
9466
+ }
9467
+ }
9468
+ };
9469
+ var DEFAULT_FILE_SYSTEM_POLICY = new FileSystemPolicy();
9470
+ function createFileSystemPolicy(options = {}) {
9471
+ return new FileSystemPolicy(options);
9472
+ }
9473
+ function resolveFileSystemPolicy(policy) {
9474
+ if (!policy) {
9475
+ return DEFAULT_FILE_SYSTEM_POLICY;
9476
+ }
9477
+ return policy instanceof FileSystemPolicy ? policy : createFileSystemPolicy(policy);
9478
+ }
9479
+
9480
+ // src/file/operations/tools/file-reader.ts
9481
+ function createFileReaderTool(defaultEncoding = "utf8", policy = DEFAULT_FILE_SYSTEM_POLICY) {
9151
9482
  return core.toolBuilder().name("file-reader").description("Read the contents of a file from the file system. Supports text and binary files with various encodings.").category(core.ToolCategory.FILE_SYSTEM).tags(["file", "read", "io", "filesystem"]).schema(fileReaderSchema).implementSafe(async (input) => {
9152
9483
  const encoding = input.encoding || defaultEncoding;
9153
- const content = await fs.promises.readFile(input.path, encoding);
9154
- const stats = await fs.promises.stat(input.path);
9484
+ const safePath = await policy.resolvePath(input.path, "file read");
9485
+ const content = await fs.promises.readFile(safePath, encoding);
9486
+ const stats = await fs.promises.stat(safePath);
9155
9487
  return {
9156
9488
  content,
9157
9489
  size: stats.size,
@@ -9160,16 +9492,17 @@ function createFileReaderTool(defaultEncoding = "utf8") {
9160
9492
  };
9161
9493
  }).build();
9162
9494
  }
9163
- function createFileWriterTool(defaultEncoding = "utf8", createDirsDefault = false) {
9495
+ function createFileWriterTool(defaultEncoding = "utf8", createDirsDefault = false, policy = DEFAULT_FILE_SYSTEM_POLICY) {
9164
9496
  return core.toolBuilder().name("file-writer").description("Write content to a file. Creates the file if it doesn't exist, or overwrites it if it does.").category(core.ToolCategory.FILE_SYSTEM).tags(["file", "write", "io", "filesystem"]).schema(fileWriterSchema).implementSafe(async (input) => {
9165
9497
  const encoding = input.encoding || defaultEncoding;
9166
9498
  const createDirs = input.createDirs ?? createDirsDefault;
9499
+ const safePath = await policy.resolvePath(input.path, "file write");
9167
9500
  if (createDirs) {
9168
- const dir = path7__namespace.dirname(input.path);
9501
+ const dir = path__namespace.dirname(safePath);
9169
9502
  await fs.promises.mkdir(dir, { recursive: true });
9170
9503
  }
9171
- await fs.promises.writeFile(input.path, input.content, encoding);
9172
- const stats = await fs.promises.stat(input.path);
9504
+ await fs.promises.writeFile(safePath, input.content, encoding);
9505
+ const stats = await fs.promises.stat(safePath);
9173
9506
  return {
9174
9507
  path: input.path,
9175
9508
  size: stats.size,
@@ -9177,31 +9510,34 @@ function createFileWriterTool(defaultEncoding = "utf8", createDirsDefault = fals
9177
9510
  };
9178
9511
  }).build();
9179
9512
  }
9180
- function createFileAppendTool(defaultEncoding = "utf8") {
9513
+ function createFileAppendTool(defaultEncoding = "utf8", policy = DEFAULT_FILE_SYSTEM_POLICY) {
9181
9514
  return core.toolBuilder().name("file-append").description("Append content to the end of a file. Creates the file if it doesn't exist.").category(core.ToolCategory.FILE_SYSTEM).tags(["file", "append", "io", "filesystem"]).schema(fileAppendSchema).implementSafe(async (input) => {
9182
9515
  const encoding = input.encoding || defaultEncoding;
9183
- await fs.promises.appendFile(input.path, input.content, encoding);
9184
- const stats = await fs.promises.stat(input.path);
9516
+ const safePath = await policy.resolvePath(input.path, "file append");
9517
+ await fs.promises.appendFile(safePath, input.content, encoding);
9518
+ const stats = await fs.promises.stat(safePath);
9185
9519
  return {
9186
9520
  path: input.path,
9187
9521
  size: stats.size
9188
9522
  };
9189
9523
  }).build();
9190
9524
  }
9191
- function createFileDeleteTool() {
9525
+ function createFileDeleteTool(policy = DEFAULT_FILE_SYSTEM_POLICY) {
9192
9526
  return core.toolBuilder().name("file-delete").description("Delete a file from the file system. Returns an error if the file doesn't exist.").category(core.ToolCategory.FILE_SYSTEM).tags(["file", "delete", "remove", "filesystem"]).schema(fileDeleteSchema).implementSafe(async (input) => {
9193
- await fs.promises.unlink(input.path);
9527
+ const safePath = await policy.resolvePath(input.path, "file deletion");
9528
+ await fs.promises.unlink(safePath);
9194
9529
  return {
9195
9530
  path: input.path,
9196
9531
  message: "File deleted successfully"
9197
9532
  };
9198
9533
  }).build();
9199
9534
  }
9200
- function createFileExistsTool() {
9535
+ function createFileExistsTool(policy = DEFAULT_FILE_SYSTEM_POLICY) {
9201
9536
  return core.toolBuilder().name("file-exists").description("Check if a file or directory exists at the specified path.").category(core.ToolCategory.FILE_SYSTEM).tags(["file", "exists", "check", "filesystem"]).schema(fileExistsSchema).implement(async (input) => {
9202
9537
  try {
9203
- await fs.promises.access(input.path);
9204
- const stats = await fs.promises.stat(input.path);
9538
+ const safePath = await policy.resolvePath(input.path, "file existence check");
9539
+ await fs.promises.access(safePath);
9540
+ const stats = await fs.promises.stat(safePath);
9205
9541
  return {
9206
9542
  exists: true,
9207
9543
  path: input.path,
@@ -9210,7 +9546,13 @@ function createFileExistsTool() {
9210
9546
  size: stats.size,
9211
9547
  modified: stats.mtime.toISOString()
9212
9548
  };
9213
- } catch {
9549
+ } catch (error) {
9550
+ if (error instanceof FileSystemPolicyError) {
9551
+ return {
9552
+ success: false,
9553
+ error: error.message
9554
+ };
9555
+ }
9214
9556
  return {
9215
9557
  exists: false,
9216
9558
  path: input.path
@@ -9235,14 +9577,16 @@ var fileOperationTools = [
9235
9577
  function createFileOperationTools(config = {}) {
9236
9578
  const {
9237
9579
  defaultEncoding = "utf8",
9238
- createDirsDefault = false
9580
+ createDirsDefault = false,
9581
+ policy: policyInput
9239
9582
  } = config;
9583
+ const policy = resolveFileSystemPolicy(policyInput);
9240
9584
  return [
9241
- createFileReaderTool(defaultEncoding),
9242
- createFileWriterTool(defaultEncoding, createDirsDefault),
9243
- createFileAppendTool(defaultEncoding),
9244
- createFileDeleteTool(),
9245
- createFileExistsTool()
9585
+ createFileReaderTool(defaultEncoding, policy),
9586
+ createFileWriterTool(defaultEncoding, createDirsDefault, policy),
9587
+ createFileAppendTool(defaultEncoding, policy),
9588
+ createFileDeleteTool(policy),
9589
+ createFileExistsTool(policy)
9246
9590
  ];
9247
9591
  }
9248
9592
  var directoryListSchema = zod.z.object({
@@ -9265,21 +9609,23 @@ var fileSearchSchema = zod.z.object({
9265
9609
  recursive: zod.z.boolean().default(true).describe("Search in subdirectories"),
9266
9610
  caseSensitive: zod.z.boolean().default(false).describe("Case-sensitive pattern matching")
9267
9611
  });
9268
- function createDirectoryListTool(defaultRecursive = false, defaultIncludeDetails = false) {
9612
+ function createDirectoryListTool(defaultRecursive = false, defaultIncludeDetails = false, policy = DEFAULT_FILE_SYSTEM_POLICY) {
9269
9613
  return core.toolBuilder().name("directory-list").description("List all files and directories in a directory. Can optionally include file details and filter by extension.").category(core.ToolCategory.FILE_SYSTEM).tags(["directory", "list", "files", "filesystem"]).schema(directoryListSchema).implementSafe(async (input) => {
9614
+ const safePath = await policy.resolvePath(input.path, "directory listing");
9615
+ const includeDetails = input.includeDetails ?? defaultIncludeDetails;
9270
9616
  const listFiles = async (dir, recursive2) => {
9271
9617
  const entries = await fs.promises.readdir(dir, { withFileTypes: true });
9272
9618
  const files2 = [];
9273
9619
  for (const entry of entries) {
9274
- const fullPath = path7__namespace.join(dir, entry.name);
9275
- const relativePath = path7__namespace.relative(input.path, fullPath);
9620
+ const fullPath = path__namespace.join(dir, entry.name);
9621
+ const relativePath = path__namespace.relative(safePath, fullPath);
9276
9622
  if (input.extension && !entry.name.endsWith(input.extension)) {
9277
9623
  if (!entry.isDirectory() || !recursive2) {
9278
9624
  continue;
9279
9625
  }
9280
9626
  }
9281
- if (input.includeDetails) {
9282
- const stats = await fs.promises.stat(fullPath);
9627
+ if (includeDetails) {
9628
+ const stats = await fs.promises.lstat(fullPath);
9283
9629
  files2.push({
9284
9630
  name: entry.name,
9285
9631
  path: relativePath,
@@ -9305,7 +9651,7 @@ function createDirectoryListTool(defaultRecursive = false, defaultIncludeDetails
9305
9651
  return files2;
9306
9652
  };
9307
9653
  const recursive = input.recursive ?? defaultRecursive;
9308
- const files = await listFiles(input.path, recursive);
9654
+ const files = await listFiles(safePath, recursive);
9309
9655
  return {
9310
9656
  path: input.path,
9311
9657
  files,
@@ -9313,37 +9659,40 @@ function createDirectoryListTool(defaultRecursive = false, defaultIncludeDetails
9313
9659
  };
9314
9660
  }).build();
9315
9661
  }
9316
- function createDirectoryCreateTool(defaultRecursive = true) {
9662
+ function createDirectoryCreateTool(defaultRecursive = true, policy = DEFAULT_FILE_SYSTEM_POLICY) {
9317
9663
  return core.toolBuilder().name("directory-create").description("Create a new directory. Can optionally create parent directories if they don't exist.").category(core.ToolCategory.FILE_SYSTEM).tags(["directory", "create", "mkdir", "filesystem"]).schema(directoryCreateSchema).implementSafe(async (input) => {
9318
9664
  const recursive = input.recursive ?? defaultRecursive;
9319
- await fs.promises.mkdir(input.path, { recursive });
9665
+ const safePath = await policy.resolvePath(input.path, "directory creation");
9666
+ await fs.promises.mkdir(safePath, { recursive });
9320
9667
  return {
9321
9668
  path: input.path,
9322
9669
  message: "Directory created successfully"
9323
9670
  };
9324
9671
  }).build();
9325
9672
  }
9326
- function createDirectoryDeleteTool(defaultRecursive = false) {
9673
+ function createDirectoryDeleteTool(defaultRecursive = false, policy = DEFAULT_FILE_SYSTEM_POLICY) {
9327
9674
  return core.toolBuilder().name("directory-delete").description("Delete a directory. Can optionally delete non-empty directories recursively.").category(core.ToolCategory.FILE_SYSTEM).tags(["directory", "delete", "remove", "filesystem"]).schema(directoryDeleteSchema).implementSafe(async (input) => {
9328
9675
  const recursive = input.recursive ?? defaultRecursive;
9329
- await fs.promises.rm(input.path, { recursive, force: false });
9676
+ const safePath = await policy.resolveDeletePath(input.path, recursive);
9677
+ await fs.promises.rm(safePath, { recursive, force: false });
9330
9678
  return {
9331
9679
  path: input.path,
9332
9680
  message: "Directory deleted successfully"
9333
9681
  };
9334
9682
  }).build();
9335
9683
  }
9336
- function createFileSearchTool(defaultRecursive = true, defaultCaseSensitive = false) {
9684
+ function createFileSearchTool(defaultRecursive = true, defaultCaseSensitive = false, policy = DEFAULT_FILE_SYSTEM_POLICY) {
9337
9685
  return core.toolBuilder().name("file-search").description("Search for files by name pattern in a directory. Supports wildcards and recursive search.").category(core.ToolCategory.FILE_SYSTEM).tags(["file", "search", "find", "filesystem"]).schema(fileSearchSchema).implementSafe(async (input) => {
9338
9686
  const recursive = input.recursive ?? defaultRecursive;
9339
9687
  const caseSensitive = input.caseSensitive ?? defaultCaseSensitive;
9688
+ const safeDirectory = await policy.resolvePath(input.directory, "file search");
9340
9689
  const searchFiles = async (dir) => {
9341
9690
  const entries = await fs.promises.readdir(dir, { withFileTypes: true });
9342
9691
  const matches2 = [];
9343
9692
  const regexPattern = input.pattern.replace(/\./g, "\\.").replace(/\*/g, ".*");
9344
9693
  const regex = new RegExp(`^${regexPattern}$`, caseSensitive ? "" : "i");
9345
9694
  for (const entry of entries) {
9346
- const fullPath = path7__namespace.join(dir, entry.name);
9695
+ const fullPath = path__namespace.join(dir, entry.name);
9347
9696
  if (entry.isFile() && regex.test(entry.name)) {
9348
9697
  matches2.push(fullPath);
9349
9698
  }
@@ -9354,7 +9703,7 @@ function createFileSearchTool(defaultRecursive = true, defaultCaseSensitive = fa
9354
9703
  }
9355
9704
  return matches2;
9356
9705
  };
9357
- const matches = await searchFiles(input.directory);
9706
+ const matches = await searchFiles(safeDirectory);
9358
9707
  return {
9359
9708
  directory: input.directory,
9360
9709
  pattern: input.pattern,
@@ -9379,15 +9728,17 @@ function createDirectoryOperationTools(config = {}) {
9379
9728
  const {
9380
9729
  defaultRecursive = false,
9381
9730
  defaultIncludeDetails = false,
9382
- defaultCaseSensitive = false
9731
+ defaultCaseSensitive = false,
9732
+ policy: policyInput
9383
9733
  } = config;
9734
+ const policy = resolveFileSystemPolicy(policyInput);
9384
9735
  return [
9385
- createDirectoryListTool(defaultRecursive, defaultIncludeDetails),
9386
- createDirectoryCreateTool(true),
9736
+ createDirectoryListTool(defaultRecursive, defaultIncludeDetails, policy),
9737
+ createDirectoryCreateTool(true, policy),
9387
9738
  // Always default to true for create
9388
- createDirectoryDeleteTool(false),
9739
+ createDirectoryDeleteTool(false, policy),
9389
9740
  // Always default to false for delete (safety)
9390
- createFileSearchTool(defaultRecursive, defaultCaseSensitive)
9741
+ createFileSearchTool(defaultRecursive, defaultCaseSensitive, policy)
9391
9742
  ];
9392
9743
  }
9393
9744
  var pathJoinSchema = zod.z.object({
@@ -9418,7 +9769,7 @@ var pathNormalizeSchema = zod.z.object({
9418
9769
  });
9419
9770
  function createPathJoinTool() {
9420
9771
  return core.toolBuilder().name("path-join").description("Join multiple path segments into a single path. Handles platform-specific separators.").category(core.ToolCategory.FILE_SYSTEM).tags(["path", "join", "filesystem"]).schema(pathJoinSchema).implement(async (input) => {
9421
- const joined = path7__namespace.join(...input.segments);
9772
+ const joined = path__namespace.join(...input.segments);
9422
9773
  return {
9423
9774
  path: joined,
9424
9775
  segments: input.segments
@@ -9427,59 +9778,59 @@ function createPathJoinTool() {
9427
9778
  }
9428
9779
  function createPathResolveTool() {
9429
9780
  return core.toolBuilder().name("path-resolve").description("Resolve a sequence of paths into an absolute path. Resolves relative paths from the current working directory.").category(core.ToolCategory.FILE_SYSTEM).tags(["path", "resolve", "absolute", "filesystem"]).schema(pathResolveSchema).implement(async (input) => {
9430
- const resolved = path7__namespace.resolve(...input.paths);
9781
+ const resolved = path__namespace.resolve(...input.paths);
9431
9782
  return {
9432
9783
  path: resolved,
9433
- isAbsolute: path7__namespace.isAbsolute(resolved)
9784
+ isAbsolute: path__namespace.isAbsolute(resolved)
9434
9785
  };
9435
9786
  }).build();
9436
9787
  }
9437
9788
  function createPathParseTool() {
9438
9789
  return core.toolBuilder().name("path-parse").description("Parse a file path into its components (directory, filename, extension, etc.).").category(core.ToolCategory.FILE_SYSTEM).tags(["path", "parse", "filesystem"]).schema(pathParseSchema).implement(async (input) => {
9439
- const parsed = path7__namespace.parse(input.path);
9790
+ const parsed = path__namespace.parse(input.path);
9440
9791
  return {
9441
9792
  root: parsed.root,
9442
9793
  dir: parsed.dir,
9443
9794
  base: parsed.base,
9444
9795
  name: parsed.name,
9445
9796
  ext: parsed.ext,
9446
- isAbsolute: path7__namespace.isAbsolute(input.path)
9797
+ isAbsolute: path__namespace.isAbsolute(input.path)
9447
9798
  };
9448
9799
  }).build();
9449
9800
  }
9450
9801
  function createPathBasenameTool() {
9451
9802
  return core.toolBuilder().name("path-basename").description("Get the last portion of a path (filename with extension). Optionally remove the extension.").category(core.ToolCategory.FILE_SYSTEM).tags(["path", "basename", "filename", "filesystem"]).schema(pathBasenameSchema).implement(async (input) => {
9452
- const basename4 = input.removeExtension ? path7__namespace.basename(input.path, path7__namespace.extname(input.path)) : path7__namespace.basename(input.path);
9803
+ const basename5 = input.removeExtension ? path__namespace.basename(input.path, path__namespace.extname(input.path)) : path__namespace.basename(input.path);
9453
9804
  return {
9454
- basename: basename4,
9455
- extension: path7__namespace.extname(input.path)
9805
+ basename: basename5,
9806
+ extension: path__namespace.extname(input.path)
9456
9807
  };
9457
9808
  }).build();
9458
9809
  }
9459
9810
  function createPathDirnameTool() {
9460
9811
  return core.toolBuilder().name("path-dirname").description("Get the directory name of a path (everything except the last portion).").category(core.ToolCategory.FILE_SYSTEM).tags(["path", "dirname", "directory", "filesystem"]).schema(pathDirnameSchema).implement(async (input) => {
9461
- const dirname3 = path7__namespace.dirname(input.path);
9812
+ const dirname4 = path__namespace.dirname(input.path);
9462
9813
  return {
9463
- dirname: dirname3,
9464
- basename: path7__namespace.basename(input.path)
9814
+ dirname: dirname4,
9815
+ basename: path__namespace.basename(input.path)
9465
9816
  };
9466
9817
  }).build();
9467
9818
  }
9468
9819
  function createPathExtensionTool() {
9469
9820
  return core.toolBuilder().name("path-extension").description('Get the file extension from a path (including the dot, e.g., ".txt").').category(core.ToolCategory.FILE_SYSTEM).tags(["path", "extension", "ext", "filesystem"]).schema(pathExtensionSchema).implement(async (input) => {
9470
- const ext = path7__namespace.extname(input.path);
9821
+ const ext = path__namespace.extname(input.path);
9471
9822
  return {
9472
9823
  extension: ext,
9473
9824
  hasExtension: ext.length > 0,
9474
- filename: path7__namespace.basename(input.path, ext)
9825
+ filename: path__namespace.basename(input.path, ext)
9475
9826
  };
9476
9827
  }).build();
9477
9828
  }
9478
9829
  function createPathRelativeTool() {
9479
9830
  return core.toolBuilder().name("path-relative").description("Get the relative path from one path to another.").category(core.ToolCategory.FILE_SYSTEM).tags(["path", "relative", "filesystem"]).schema(pathRelativeSchema).implement(async (input) => {
9480
- const relative3 = path7__namespace.relative(input.from, input.to);
9831
+ const relative4 = path__namespace.relative(input.from, input.to);
9481
9832
  return {
9482
- relativePath: relative3,
9833
+ relativePath: relative4,
9483
9834
  from: input.from,
9484
9835
  to: input.to
9485
9836
  };
@@ -9487,7 +9838,7 @@ function createPathRelativeTool() {
9487
9838
  }
9488
9839
  function createPathNormalizeTool() {
9489
9840
  return core.toolBuilder().name("path-normalize").description('Normalize a path by resolving ".." and "." segments and removing duplicate separators.').category(core.ToolCategory.FILE_SYSTEM).tags(["path", "normalize", "filesystem"]).schema(pathNormalizeSchema).implement(async (input) => {
9490
- const normalized = path7__namespace.normalize(input.path);
9841
+ const normalized = path__namespace.normalize(input.path);
9491
9842
  return {
9492
9843
  normalized,
9493
9844
  original: input.path
@@ -10395,14 +10746,19 @@ exports.CreditCardValidatorSchema = CreditCardValidatorSchema;
10395
10746
  exports.CurrentDateTimeSchema = CurrentDateTimeSchema;
10396
10747
  exports.DEFAULT_BATCH_SIZE = DEFAULT_BATCH_SIZE;
10397
10748
  exports.DEFAULT_CHUNK_SIZE = DEFAULT_CHUNK_SIZE;
10749
+ exports.DEFAULT_DESTINATION_POLICY = DEFAULT_DESTINATION_POLICY;
10750
+ exports.DEFAULT_FILE_SYSTEM_POLICY = DEFAULT_FILE_SYSTEM_POLICY;
10398
10751
  exports.DEFAULT_RETRY_CONFIG = DEFAULT_RETRY_CONFIG2;
10399
10752
  exports.DateArithmeticSchema = DateArithmeticSchema;
10400
10753
  exports.DateComparisonSchema = DateComparisonSchema;
10401
10754
  exports.DateDifferenceSchema = DateDifferenceSchema;
10402
10755
  exports.DateFormatterSchema = DateFormatterSchema;
10756
+ exports.DestinationPolicyError = DestinationPolicyError;
10403
10757
  exports.DuckDuckGoProvider = DuckDuckGoProvider;
10404
10758
  exports.EmailValidatorSchema = EmailValidatorSchema;
10405
10759
  exports.EmbeddingManager = EmbeddingManager;
10760
+ exports.FileSystemPolicy = FileSystemPolicy;
10761
+ exports.FileSystemPolicyError = FileSystemPolicyError;
10406
10762
  exports.HttpMethod = HttpMethod;
10407
10763
  exports.IpValidatorSchema = IpValidatorSchema;
10408
10764
  exports.MAX_BATCH_SIZE = MAX_BATCH_SIZE;
@@ -10433,6 +10789,7 @@ exports.arrayMapSchema = arrayMapSchema;
10433
10789
  exports.arraySort = arraySort;
10434
10790
  exports.arraySortSchema = arraySortSchema;
10435
10791
  exports.askHumanTool = askHumanTool;
10792
+ exports.assertDestinationAllowed = assertDestinationAllowed;
10436
10793
  exports.benchmarkBatchExecution = benchmarkBatchExecution;
10437
10794
  exports.benchmarkStreamingSelectMemory = benchmarkStreamingSelectMemory;
10438
10795
  exports.buildDeleteQuery = buildDeleteQuery;
@@ -10475,6 +10832,7 @@ exports.createFileExistsTool = createFileExistsTool;
10475
10832
  exports.createFileOperationTools = createFileOperationTools;
10476
10833
  exports.createFileReaderTool = createFileReaderTool;
10477
10834
  exports.createFileSearchTool = createFileSearchTool;
10835
+ exports.createFileSystemPolicy = createFileSystemPolicy;
10478
10836
  exports.createFileWriterTool = createFileWriterTool;
10479
10837
  exports.createHtmlParserTool = createHtmlParserTool;
10480
10838
  exports.createHtmlParserTools = createHtmlParserTools;
@@ -10682,6 +11040,8 @@ exports.relationalInsert = relationalInsert;
10682
11040
  exports.relationalQuery = relationalQuery;
10683
11041
  exports.relationalSelect = relationalSelect;
10684
11042
  exports.relationalUpdate = relationalUpdate;
11043
+ exports.requestWithDestinationPolicy = requestWithDestinationPolicy;
11044
+ exports.resolveFileSystemPolicy = resolveFileSystemPolicy;
10685
11045
  exports.retryWithBackoff = retryWithBackoff2;
10686
11046
  exports.scraperTools = scraperTools;
10687
11047
  exports.searchConfluence = searchConfluence;