@agentforge/tools 0.18.4 → 0.18.5

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
@@ -3,7 +3,7 @@
3
3
  var core = require('@agentforge/core');
4
4
  var promises = require('dns/promises');
5
5
  var net = require('net');
6
- var axios11 = require('axios');
6
+ var axios5 = require('axios');
7
7
  var zod = require('zod');
8
8
  var cheerio = require('cheerio');
9
9
  var webApi = require('@slack/web-api');
@@ -41,7 +41,7 @@ function _interopNamespace(e) {
41
41
  }
42
42
 
43
43
  var net__default = /*#__PURE__*/_interopDefault(net);
44
- var axios11__default = /*#__PURE__*/_interopDefault(axios11);
44
+ var axios5__default = /*#__PURE__*/_interopDefault(axios5);
45
45
  var cheerio__namespace = /*#__PURE__*/_interopNamespace(cheerio);
46
46
  var neo4j__default = /*#__PURE__*/_interopDefault(neo4j);
47
47
  var path__namespace = /*#__PURE__*/_interopNamespace(path);
@@ -229,7 +229,7 @@ async function requestWithDestinationPolicy(config, policy = {}) {
229
229
  for (let redirectCount = 0; ; redirectCount += 1) {
230
230
  const validatedAddresses = await validateDestinationAllowed(currentUrl, effectivePolicy);
231
231
  currentConfig.lookup = createPinnedLookup(validatedAddresses);
232
- const response = await axios11__default.default(currentConfig);
232
+ const response = await axios5__default.default(currentConfig);
233
233
  if (!isRedirectStatus(response.status)) return response;
234
234
  if (effectivePolicy.allowRedirects === false) blockDestination(currentUrl, "redirect");
235
235
  if (redirectCount >= maxRedirects) throw new DestinationPolicyError(currentUrl, "redirect", `Maximum redirect count (${maxRedirects}) exceeded for ${currentUrl}`);
@@ -740,7 +740,7 @@ var DuckDuckGoProvider = class {
740
740
  async search(query, maxResults, timeout = DEFAULT_TIMEOUT) {
741
741
  try {
742
742
  return await retryWithBackoff(async () => {
743
- const response = await axios11__default.default.get("https://api.duckduckgo.com/", {
743
+ const response = await axios5__default.default.get("https://api.duckduckgo.com/", {
744
744
  params: {
745
745
  q: query,
746
746
  format: "json"
@@ -841,7 +841,7 @@ var SerperProvider = class {
841
841
  }
842
842
  try {
843
843
  return await retryWithBackoff(async () => {
844
- const response = await axios11__default.default.post(
844
+ const response = await axios5__default.default.post(
845
845
  "https://google.serper.dev/search",
846
846
  {
847
847
  q: query,
@@ -1244,38 +1244,91 @@ function createGetConfiguredAuth(apiKey, email, siteUrl) {
1244
1244
  return { ATLASSIAN_API_KEY, ATLASSIAN_EMAIL, ATLASSIAN_SITE_URL };
1245
1245
  };
1246
1246
  }
1247
- function createGetConfiguredAuthHeader(getConfiguredAuth) {
1248
- return function getConfiguredAuthHeader() {
1249
- const { ATLASSIAN_API_KEY, ATLASSIAN_EMAIL } = getConfiguredAuth();
1250
- const auth2 = Buffer.from(`${ATLASSIAN_EMAIL}:${ATLASSIAN_API_KEY}`).toString("base64");
1251
- return `Basic ${auth2}`;
1252
- };
1253
- }
1254
1247
  function getConfig() {
1255
1248
  const ATLASSIAN_API_KEY = process.env.ATLASSIAN_API_KEY || "";
1256
1249
  const ATLASSIAN_EMAIL = process.env.ATLASSIAN_EMAIL || "";
1257
1250
  const ATLASSIAN_SITE_URL = (process.env.ATLASSIAN_SITE_URL || "").replace(/\/$/, "");
1258
1251
  if (!ATLASSIAN_API_KEY || !ATLASSIAN_EMAIL || !ATLASSIAN_SITE_URL) {
1259
- throw new Error("Confluence credentials not configured. Set ATLASSIAN_API_KEY, ATLASSIAN_EMAIL, and ATLASSIAN_SITE_URL in .env");
1252
+ throw new Error(
1253
+ "Confluence credentials not configured. Set ATLASSIAN_API_KEY, ATLASSIAN_EMAIL, and ATLASSIAN_SITE_URL in .env"
1254
+ );
1260
1255
  }
1261
1256
  return { ATLASSIAN_API_KEY, ATLASSIAN_EMAIL, ATLASSIAN_SITE_URL };
1262
1257
  }
1263
- function getAuthHeader() {
1264
- const { ATLASSIAN_API_KEY, ATLASSIAN_EMAIL } = getConfig();
1265
- const auth2 = Buffer.from(`${ATLASSIAN_EMAIL}:${ATLASSIAN_API_KEY}`).toString("base64");
1266
- return `Basic ${auth2}`;
1258
+ function isRecord(value) {
1259
+ return typeof value === "object" && value !== null;
1260
+ }
1261
+ function describeError(error) {
1262
+ const errorRecord = isRecord(error) ? error : void 0;
1263
+ const response = isRecord(errorRecord?.response) ? errorRecord.response : void 0;
1264
+ const responseData = isRecord(response?.data) ? response.data : void 0;
1265
+ const providerMessage = typeof responseData?.message === "string" ? responseData.message : void 0;
1266
+ const errorMessage = typeof errorRecord?.message === "string" ? errorRecord.message : void 0;
1267
+ return {
1268
+ message: providerMessage || errorMessage,
1269
+ status: typeof response?.status === "number" ? response.status : void 0
1270
+ };
1267
1271
  }
1268
- function createSearchConfluenceTool(getAuth, getAuthHeader2, logger20) {
1269
- return core.toolBuilder().name("search-confluence").description("Search for pages in Confluence using keywords or CQL (Confluence Query Language). Returns matching pages with titles, IDs, and excerpts.").category(core.ToolCategory.WEB).tag("confluence").tag("search").tag("knowledge-base").usageNotes("Use this to find relevant documentation, policies, or information in Confluence. You can search by keywords or use CQL for advanced queries (e.g., 'space=AI AND type=page'). Use get-confluence-page to retrieve full content of specific pages.").suggests(["get-confluence-page"]).schema(zod.z.object({
1270
- query: zod.z.string().describe("Search query or CQL expression (e.g., 'payment processing' or 'space=BL3 AND title~payment')"),
1271
- limit: zod.z.number().optional().describe("Maximum number of results to return (default: 10, max: 25)")
1272
- })).implement(async ({ query, limit = 10 }) => {
1272
+ function authenticatedConfig(auth2, config = {}) {
1273
+ const credentials = Buffer.from(`${auth2.ATLASSIAN_EMAIL}:${auth2.ATLASSIAN_API_KEY}`).toString(
1274
+ "base64"
1275
+ );
1276
+ return {
1277
+ ...config,
1278
+ headers: {
1279
+ ...config.headers,
1280
+ Authorization: `Basic ${credentials}`
1281
+ }
1282
+ };
1283
+ }
1284
+ function createConfluenceRequest(getAuth) {
1285
+ return {
1286
+ async get(path13, config = {}) {
1287
+ const auth2 = getAuth();
1288
+ const response = await axios5__default.default.get(
1289
+ `${auth2.ATLASSIAN_SITE_URL}${path13}`,
1290
+ authenticatedConfig(auth2, config)
1291
+ );
1292
+ return { data: response.data, siteUrl: auth2.ATLASSIAN_SITE_URL };
1293
+ },
1294
+ async post(path13, data, config = {}) {
1295
+ const auth2 = getAuth();
1296
+ const response = await axios5__default.default.post(
1297
+ `${auth2.ATLASSIAN_SITE_URL}${path13}`,
1298
+ data,
1299
+ authenticatedConfig(auth2, config)
1300
+ );
1301
+ return { data: response.data, siteUrl: auth2.ATLASSIAN_SITE_URL };
1302
+ },
1303
+ async put(path13, data, config = {}) {
1304
+ const auth2 = getAuth();
1305
+ const response = await axios5__default.default.put(
1306
+ `${auth2.ATLASSIAN_SITE_URL}${path13}`,
1307
+ data,
1308
+ authenticatedConfig(auth2, config)
1309
+ );
1310
+ return { data: response.data, siteUrl: auth2.ATLASSIAN_SITE_URL };
1311
+ },
1312
+ describeError
1313
+ };
1314
+ }
1315
+ function createSearchConfluenceTool(request2, logger20) {
1316
+ return core.toolBuilder().name("search-confluence").description(
1317
+ "Search for pages in Confluence using keywords or CQL (Confluence Query Language). Returns matching pages with titles, IDs, and excerpts."
1318
+ ).category(core.ToolCategory.WEB).tag("confluence").tag("search").tag("knowledge-base").usageNotes(
1319
+ "Use this to find relevant documentation, policies, or information in Confluence. You can search by keywords or use CQL for advanced queries (e.g., 'space=AI AND type=page'). Use get-confluence-page to retrieve full content of specific pages."
1320
+ ).suggests(["get-confluence-page"]).schema(
1321
+ zod.z.object({
1322
+ query: zod.z.string().describe(
1323
+ "Search query or CQL expression (e.g., 'payment processing' or 'space=BL3 AND title~payment')"
1324
+ ),
1325
+ limit: zod.z.number().optional().describe("Maximum number of results to return (default: 10, max: 25)")
1326
+ })
1327
+ ).implement(async ({ query, limit = 10 }) => {
1273
1328
  logger20.info("search-confluence called", { query, limit });
1274
1329
  try {
1275
- const { ATLASSIAN_SITE_URL } = getAuth();
1276
- const response = await axios11__default.default.get(`${ATLASSIAN_SITE_URL}/wiki/rest/api/content/search`, {
1330
+ const response = await request2.get("/wiki/rest/api/content/search", {
1277
1331
  headers: {
1278
- Authorization: getAuthHeader2(),
1279
1332
  Accept: "application/json"
1280
1333
  },
1281
1334
  params: {
@@ -1284,22 +1337,24 @@ function createSearchConfluenceTool(getAuth, getAuthHeader2, logger20) {
1284
1337
  expand: "space,version"
1285
1338
  }
1286
1339
  });
1287
- const { ATLASSIAN_SITE_URL: siteUrl } = getAuth();
1288
1340
  const results = response.data.results.map((page) => ({
1289
1341
  id: page.id,
1290
1342
  title: page.title,
1291
1343
  type: page.type,
1292
1344
  space: page.space?.name || "Unknown",
1293
1345
  spaceKey: page.space?.key || "",
1294
- url: `${siteUrl}/wiki${page._links.webui}`,
1346
+ url: `${response.siteUrl}/wiki${page._links.webui}`,
1295
1347
  lastModified: page.version?.when || ""
1296
1348
  }));
1297
1349
  if (results.length === 0) {
1298
- logger20.warn("search-confluence returned NO RESULTS - this is a valid outcome, agent should not retry", {
1299
- query,
1300
- limit,
1301
- totalSize: response.data.totalSize
1302
- });
1350
+ logger20.warn(
1351
+ "search-confluence returned NO RESULTS - this is a valid outcome, agent should not retry",
1352
+ {
1353
+ query,
1354
+ limit,
1355
+ totalSize: response.data.totalSize
1356
+ }
1357
+ );
1303
1358
  } else {
1304
1359
  logger20.info("search-confluence result", {
1305
1360
  query,
@@ -1316,28 +1371,33 @@ function createSearchConfluenceTool(getAuth, getAuthHeader2, logger20) {
1316
1371
  results
1317
1372
  });
1318
1373
  } catch (error) {
1374
+ const description = request2.describeError(error);
1319
1375
  logger20.error("search-confluence error", {
1320
1376
  query,
1321
- error: error.response?.data?.message || error.message,
1322
- status: error.response?.status
1377
+ ...description.message !== void 0 ? { error: description.message } : {},
1378
+ ...description.status !== void 0 ? { status: description.status } : {}
1323
1379
  });
1324
1380
  return JSON.stringify({
1325
1381
  success: false,
1326
- error: error.response?.data?.message || error.message
1382
+ error: description.message
1327
1383
  });
1328
1384
  }
1329
1385
  }).build();
1330
1386
  }
1331
- function createGetConfluencePageTool(getAuth, getAuthHeader2, logger20) {
1332
- return core.toolBuilder().name("get-confluence-page").description("Get the full content of a specific Confluence page by its ID. Returns the page title, content (in storage format), space, and metadata.").category(core.ToolCategory.WEB).tag("confluence").tag("page").tag("content").usageNotes("Use this after search-confluence to retrieve the full content of a specific page. The page ID can be found in search results.").requires(["search-confluence"]).schema(zod.z.object({
1333
- page_id: zod.z.string().describe("The Confluence page ID (from search results)")
1334
- })).implement(async ({ page_id }) => {
1387
+ function createGetConfluencePageTool(request2, logger20) {
1388
+ return core.toolBuilder().name("get-confluence-page").description(
1389
+ "Get the full content of a specific Confluence page by its ID. Returns the page title, content (in storage format), space, and metadata."
1390
+ ).category(core.ToolCategory.WEB).tag("confluence").tag("page").tag("content").usageNotes(
1391
+ "Use this after search-confluence to retrieve the full content of a specific page. The page ID can be found in search results."
1392
+ ).requires(["search-confluence"]).schema(
1393
+ zod.z.object({
1394
+ page_id: zod.z.string().describe("The Confluence page ID (from search results)")
1395
+ })
1396
+ ).implement(async ({ page_id }) => {
1335
1397
  logger20.info("get-confluence-page called", { page_id });
1336
1398
  try {
1337
- const { ATLASSIAN_SITE_URL } = getAuth();
1338
- const response = await axios11__default.default.get(`${ATLASSIAN_SITE_URL}/wiki/rest/api/content/${page_id}`, {
1399
+ const response = await request2.get(`/wiki/rest/api/content/${page_id}`, {
1339
1400
  headers: {
1340
- Authorization: getAuthHeader2(),
1341
1401
  Accept: "application/json"
1342
1402
  },
1343
1403
  params: {
@@ -1360,35 +1420,40 @@ function createGetConfluencePageTool(getAuth, getAuthHeader2, logger20) {
1360
1420
  space: page.space?.name || "Unknown",
1361
1421
  spaceKey: page.space?.key || "",
1362
1422
  content: page.body?.storage?.value || "",
1363
- url: `${ATLASSIAN_SITE_URL}/wiki${page._links.webui}`,
1423
+ url: `${response.siteUrl}/wiki${page._links.webui}`,
1364
1424
  created: page.history?.createdDate || "",
1365
1425
  lastModified: page.version?.when || "",
1366
1426
  version: page.version?.number || 1
1367
1427
  }
1368
1428
  });
1369
1429
  } catch (error) {
1430
+ const description = request2.describeError(error);
1370
1431
  logger20.error("get-confluence-page error", {
1371
1432
  page_id,
1372
- error: error.response?.data?.message || error.message,
1373
- status: error.response?.status
1433
+ ...description.message !== void 0 ? { error: description.message } : {},
1434
+ ...description.status !== void 0 ? { status: description.status } : {}
1374
1435
  });
1375
1436
  return JSON.stringify({
1376
1437
  success: false,
1377
- error: error.response?.data?.message || error.message
1438
+ error: description.message
1378
1439
  });
1379
1440
  }
1380
1441
  }).build();
1381
1442
  }
1382
- function createListConfluenceSpacesTool(getAuth, getAuthHeader2, logger20) {
1383
- return core.toolBuilder().name("list-confluence-spaces").description("List all available Confluence spaces. Returns space names, keys, types, and descriptions to help identify where to search for information.").category(core.ToolCategory.WEB).tag("confluence").tag("spaces").tag("list").usageNotes("Use this first to discover available spaces before searching. Helps narrow down searches to specific areas (e.g., 'AI', 'BL3', 'Finance').").follows(["search-confluence"]).schema(zod.z.object({
1384
- limit: zod.z.number().optional().describe("Maximum number of spaces to return (default: 25)")
1385
- })).implement(async ({ limit = 25 }) => {
1443
+ function createListConfluenceSpacesTool(request2, logger20) {
1444
+ return core.toolBuilder().name("list-confluence-spaces").description(
1445
+ "List all available Confluence spaces. Returns space names, keys, types, and descriptions to help identify where to search for information."
1446
+ ).category(core.ToolCategory.WEB).tag("confluence").tag("spaces").tag("list").usageNotes(
1447
+ "Use this first to discover available spaces before searching. Helps narrow down searches to specific areas (e.g., 'AI', 'BL3', 'Finance')."
1448
+ ).follows(["search-confluence"]).schema(
1449
+ zod.z.object({
1450
+ limit: zod.z.number().optional().describe("Maximum number of spaces to return (default: 25)")
1451
+ })
1452
+ ).implement(async ({ limit = 25 }) => {
1386
1453
  logger20.info("list-confluence-spaces called", { limit });
1387
1454
  try {
1388
- const { ATLASSIAN_SITE_URL } = getAuth();
1389
- const response = await axios11__default.default.get(`${ATLASSIAN_SITE_URL}/wiki/rest/api/space`, {
1455
+ const response = await request2.get("/wiki/rest/api/space", {
1390
1456
  headers: {
1391
- Authorization: getAuthHeader2(),
1392
1457
  Accept: "application/json"
1393
1458
  },
1394
1459
  params: {
@@ -1400,7 +1465,7 @@ function createListConfluenceSpacesTool(getAuth, getAuthHeader2, logger20) {
1400
1465
  name: space.name,
1401
1466
  type: space.type,
1402
1467
  description: space.description?.plain?.value || "",
1403
- url: `${ATLASSIAN_SITE_URL}/wiki${space._links.webui}`
1468
+ url: `${response.siteUrl}/wiki${space._links.webui}`
1404
1469
  }));
1405
1470
  logger20.info("list-confluence-spaces result", {
1406
1471
  spaceCount: spaces.length,
@@ -1413,28 +1478,33 @@ function createListConfluenceSpacesTool(getAuth, getAuthHeader2, logger20) {
1413
1478
  spaces
1414
1479
  });
1415
1480
  } catch (error) {
1481
+ const description = request2.describeError(error);
1416
1482
  logger20.error("list-confluence-spaces error", {
1417
- error: error.response?.data?.message || error.message,
1418
- status: error.response?.status
1483
+ ...description.message !== void 0 ? { error: description.message } : {},
1484
+ ...description.status !== void 0 ? { status: description.status } : {}
1419
1485
  });
1420
1486
  return JSON.stringify({
1421
1487
  success: false,
1422
- error: error.response?.data?.message || error.message
1488
+ error: description.message
1423
1489
  });
1424
1490
  }
1425
1491
  }).build();
1426
1492
  }
1427
- function createGetSpacePagesTool(getAuth, getAuthHeader2, logger20) {
1428
- return core.toolBuilder().name("get-space-pages").description("Get all pages from a specific Confluence space by space key. Useful for browsing content in a particular area.").category(core.ToolCategory.WEB).tag("confluence").tag("space").tag("pages").usageNotes("Use this to explore all pages in a specific space. Get the space key from list-confluence-spaces first.").requires(["list-confluence-spaces"]).schema(zod.z.object({
1429
- space_key: zod.z.string().describe("The space key (e.g., 'AI', 'BL3', 'FIN')"),
1430
- limit: zod.z.number().optional().describe("Maximum number of pages to return (default: 25)")
1431
- })).implement(async ({ space_key, limit = 25 }) => {
1493
+ function createGetSpacePagesTool(request2, logger20) {
1494
+ return core.toolBuilder().name("get-space-pages").description(
1495
+ "Get all pages from a specific Confluence space by space key. Useful for browsing content in a particular area."
1496
+ ).category(core.ToolCategory.WEB).tag("confluence").tag("space").tag("pages").usageNotes(
1497
+ "Use this to explore all pages in a specific space. Get the space key from list-confluence-spaces first."
1498
+ ).requires(["list-confluence-spaces"]).schema(
1499
+ zod.z.object({
1500
+ space_key: zod.z.string().describe("The space key (e.g., 'AI', 'BL3', 'FIN')"),
1501
+ limit: zod.z.number().optional().describe("Maximum number of pages to return (default: 25)")
1502
+ })
1503
+ ).implement(async ({ space_key, limit = 25 }) => {
1432
1504
  logger20.info("get-space-pages called", { space_key, limit });
1433
1505
  try {
1434
- const { ATLASSIAN_SITE_URL } = getAuth();
1435
- const response = await axios11__default.default.get(`${ATLASSIAN_SITE_URL}/wiki/rest/api/content`, {
1506
+ const response = await request2.get("/wiki/rest/api/content", {
1436
1507
  headers: {
1437
- Authorization: getAuthHeader2(),
1438
1508
  Accept: "application/json"
1439
1509
  },
1440
1510
  params: {
@@ -1447,14 +1517,17 @@ function createGetSpacePagesTool(getAuth, getAuthHeader2, logger20) {
1447
1517
  const pages = response.data.results.map((page) => ({
1448
1518
  id: page.id,
1449
1519
  title: page.title,
1450
- url: `${ATLASSIAN_SITE_URL}/wiki${page._links.webui}`,
1520
+ url: `${response.siteUrl}/wiki${page._links.webui}`,
1451
1521
  lastModified: page.version?.when || ""
1452
1522
  }));
1453
1523
  if (pages.length === 0) {
1454
- logger20.warn("get-space-pages returned NO PAGES - this is a valid outcome, agent should not retry", {
1455
- space_key,
1456
- limit
1457
- });
1524
+ logger20.warn(
1525
+ "get-space-pages returned NO PAGES - this is a valid outcome, agent should not retry",
1526
+ {
1527
+ space_key,
1528
+ limit
1529
+ }
1530
+ );
1458
1531
  } else {
1459
1532
  logger20.info("get-space-pages result", {
1460
1533
  space_key,
@@ -1470,28 +1543,38 @@ function createGetSpacePagesTool(getAuth, getAuthHeader2, logger20) {
1470
1543
  pages
1471
1544
  });
1472
1545
  } catch (error) {
1546
+ const description = request2.describeError(error);
1473
1547
  logger20.error("get-space-pages error", {
1474
1548
  space_key,
1475
- error: error.response?.data?.message || error.message,
1476
- status: error.response?.status
1549
+ ...description.message !== void 0 ? { error: description.message } : {},
1550
+ ...description.status !== void 0 ? { status: description.status } : {}
1477
1551
  });
1478
1552
  return JSON.stringify({
1479
1553
  success: false,
1480
- error: error.response?.data?.message || error.message
1554
+ error: description.message
1481
1555
  });
1482
1556
  }
1483
1557
  }).build();
1484
1558
  }
1485
- function createCreateConfluencePageTool(getAuth, getAuthHeader2, logger20) {
1486
- return core.toolBuilder().name("create-confluence-page").description("Create a new page in a Confluence space. Requires space key, page title, and content (in HTML storage format).").category(core.ToolCategory.WEB).tag("confluence").tag("create").tag("write").usageNotes("Use this to create new documentation pages. Content should be in Confluence storage format (HTML). Get the space key from list-confluence-spaces first. Be mindful of creating duplicate pages.").requires(["list-confluence-spaces"]).schema(zod.z.object({
1487
- space_key: zod.z.string().describe("The space key where the page will be created (e.g., 'AI', 'BL3')"),
1488
- title: zod.z.string().describe("The title of the new page"),
1489
- content: zod.z.string().describe("The page content in HTML format (Confluence storage format)"),
1490
- parent_page_id: zod.z.string().optional().describe("Optional parent page ID to create this as a child page")
1491
- })).implement(async ({ space_key, title, content, parent_page_id }) => {
1492
- logger20.info("create-confluence-page called", { space_key, title, hasParent: !!parent_page_id });
1559
+ function createCreateConfluencePageTool(request2, logger20) {
1560
+ return core.toolBuilder().name("create-confluence-page").description(
1561
+ "Create a new page in a Confluence space. Requires space key, page title, and content (in HTML storage format)."
1562
+ ).category(core.ToolCategory.WEB).tag("confluence").tag("create").tag("write").usageNotes(
1563
+ "Use this to create new documentation pages. Content should be in Confluence storage format (HTML). Get the space key from list-confluence-spaces first. Be mindful of creating duplicate pages."
1564
+ ).requires(["list-confluence-spaces"]).schema(
1565
+ zod.z.object({
1566
+ space_key: zod.z.string().describe("The space key where the page will be created (e.g., 'AI', 'BL3')"),
1567
+ title: zod.z.string().describe("The title of the new page"),
1568
+ content: zod.z.string().describe("The page content in HTML format (Confluence storage format)"),
1569
+ parent_page_id: zod.z.string().optional().describe("Optional parent page ID to create this as a child page")
1570
+ })
1571
+ ).implement(async ({ space_key, title, content, parent_page_id }) => {
1572
+ logger20.info("create-confluence-page called", {
1573
+ space_key,
1574
+ title,
1575
+ hasParent: !!parent_page_id
1576
+ });
1493
1577
  try {
1494
- const { ATLASSIAN_SITE_URL } = getAuth();
1495
1578
  const pageData = {
1496
1579
  type: "page",
1497
1580
  title,
@@ -1506,16 +1589,11 @@ function createCreateConfluencePageTool(getAuth, getAuthHeader2, logger20) {
1506
1589
  if (parent_page_id) {
1507
1590
  pageData.ancestors = [{ id: parent_page_id }];
1508
1591
  }
1509
- const response = await axios11__default.default.post(
1510
- `${ATLASSIAN_SITE_URL}/wiki/rest/api/content`,
1511
- pageData,
1512
- {
1513
- headers: {
1514
- Authorization: getAuthHeader2(),
1515
- "Content-Type": "application/json"
1516
- }
1592
+ const response = await request2.post("/wiki/rest/api/content", pageData, {
1593
+ headers: {
1594
+ "Content-Type": "application/json"
1517
1595
  }
1518
- );
1596
+ });
1519
1597
  logger20.info("create-confluence-page result", {
1520
1598
  page_id: response.data.id,
1521
1599
  title: response.data.title,
@@ -1527,45 +1605,45 @@ function createCreateConfluencePageTool(getAuth, getAuthHeader2, logger20) {
1527
1605
  id: response.data.id,
1528
1606
  title: response.data.title,
1529
1607
  space: space_key,
1530
- url: `${ATLASSIAN_SITE_URL}/wiki${response.data._links.webui}`,
1608
+ url: `${response.siteUrl}/wiki${response.data._links.webui}`,
1531
1609
  version: response.data.version?.number || 1
1532
1610
  }
1533
1611
  });
1534
1612
  } catch (error) {
1613
+ const description = request2.describeError(error);
1535
1614
  logger20.error("create-confluence-page error", {
1536
1615
  space_key,
1537
1616
  title,
1538
- error: error.response?.data?.message || error.message,
1539
- status: error.response?.status
1617
+ ...description.message !== void 0 ? { error: description.message } : {},
1618
+ ...description.status !== void 0 ? { status: description.status } : {}
1540
1619
  });
1541
1620
  return JSON.stringify({
1542
1621
  success: false,
1543
- error: error.response?.data?.message || error.message
1622
+ error: description.message
1544
1623
  });
1545
1624
  }
1546
1625
  }).build();
1547
1626
  }
1548
- function createUpdateConfluencePageTool(getAuth, getAuthHeader2, logger20) {
1549
- return core.toolBuilder().name("update-confluence-page").description("Update an existing Confluence page's content. Requires page ID, new title, and new content.").category(core.ToolCategory.WEB).tag("confluence").tag("update").tag("write").usageNotes("Use this to update existing documentation. You must provide the page ID (from search results). The tool will automatically handle version incrementing. Always get the current page content first to avoid overwriting important information.").requires(["get-confluence-page"]).schema(zod.z.object({
1550
- page_id: zod.z.string().describe("The ID of the page to update"),
1551
- title: zod.z.string().describe("The new title for the page"),
1552
- content: zod.z.string().describe("The new content in HTML format (Confluence storage format)")
1553
- })).implement(async ({ page_id, title, content }) => {
1627
+ function createUpdateConfluencePageTool(request2, logger20) {
1628
+ return core.toolBuilder().name("update-confluence-page").description(
1629
+ "Update an existing Confluence page's content. Requires page ID, new title, and new content."
1630
+ ).category(core.ToolCategory.WEB).tag("confluence").tag("update").tag("write").usageNotes(
1631
+ "Use this to update existing documentation. You must provide the page ID (from search results). The tool will automatically handle version incrementing. Always get the current page content first to avoid overwriting important information."
1632
+ ).requires(["get-confluence-page"]).schema(
1633
+ zod.z.object({
1634
+ page_id: zod.z.string().describe("The ID of the page to update"),
1635
+ title: zod.z.string().describe("The new title for the page"),
1636
+ content: zod.z.string().describe("The new content in HTML format (Confluence storage format)")
1637
+ })
1638
+ ).implement(async ({ page_id, title, content }) => {
1554
1639
  logger20.info("update-confluence-page called", { page_id, title });
1555
1640
  try {
1556
- const { ATLASSIAN_SITE_URL } = getAuth();
1557
- const getResponse = await axios11__default.default.get(
1558
- `${ATLASSIAN_SITE_URL}/wiki/rest/api/content/${page_id}`,
1559
- {
1560
- headers: {
1561
- Authorization: getAuthHeader2()
1562
- },
1563
- params: { expand: "version" }
1564
- }
1565
- );
1641
+ const getResponse = await request2.get(`/wiki/rest/api/content/${page_id}`, {
1642
+ params: { expand: "version" }
1643
+ });
1566
1644
  const currentVersion = getResponse.data.version.number;
1567
- const updateResponse = await axios11__default.default.put(
1568
- `${ATLASSIAN_SITE_URL}/wiki/rest/api/content/${page_id}`,
1645
+ const updateResponse = await request2.put(
1646
+ `/wiki/rest/api/content/${page_id}`,
1569
1647
  {
1570
1648
  type: "page",
1571
1649
  title,
@@ -1579,7 +1657,6 @@ function createUpdateConfluencePageTool(getAuth, getAuthHeader2, logger20) {
1579
1657
  },
1580
1658
  {
1581
1659
  headers: {
1582
- Authorization: getAuthHeader2(),
1583
1660
  "Content-Type": "application/json"
1584
1661
  }
1585
1662
  }
@@ -1595,49 +1672,49 @@ function createUpdateConfluencePageTool(getAuth, getAuthHeader2, logger20) {
1595
1672
  page: {
1596
1673
  id: updateResponse.data.id,
1597
1674
  title: updateResponse.data.title,
1598
- url: `${ATLASSIAN_SITE_URL}/wiki${updateResponse.data._links.webui}`,
1675
+ url: `${updateResponse.siteUrl}/wiki${updateResponse.data._links.webui}`,
1599
1676
  version: updateResponse.data.version.number,
1600
1677
  previousVersion: currentVersion
1601
1678
  }
1602
1679
  });
1603
1680
  } catch (error) {
1681
+ const description = request2.describeError(error);
1604
1682
  logger20.error("update-confluence-page error", {
1605
1683
  page_id,
1606
1684
  title,
1607
- error: error.response?.data?.message || error.message,
1608
- status: error.response?.status
1685
+ ...description.message !== void 0 ? { error: description.message } : {},
1686
+ ...description.status !== void 0 ? { status: description.status } : {}
1609
1687
  });
1610
1688
  return JSON.stringify({
1611
1689
  success: false,
1612
- error: error.response?.data?.message || error.message
1690
+ error: description.message
1613
1691
  });
1614
1692
  }
1615
1693
  }).build();
1616
1694
  }
1617
- function createArchiveConfluencePageTool(getAuth, getAuthHeader2, logger20) {
1618
- return core.toolBuilder().name("archive-confluence-page").description("Archive a Confluence page by moving it to trash. The page can be restored by space admins. Note: UI may require a note explaining why the page was archived.").category(core.ToolCategory.WEB).tag("confluence").tag("archive").tag("delete").usageNotes("Use this to archive outdated or obsolete documentation. The page is moved to trash, not permanently deleted. Space admins can restore it if needed. Be very careful - only archive pages that are truly obsolete.").conflicts(["create-confluence-page"]).schema(zod.z.object({
1619
- page_id: zod.z.string().describe("The ID of the page to archive"),
1620
- reason: zod.z.string().optional().describe("Optional reason for archiving (for audit trail)")
1621
- })).implement(async ({ page_id, reason }) => {
1695
+ function createArchiveConfluencePageTool(request2, logger20) {
1696
+ return core.toolBuilder().name("archive-confluence-page").description(
1697
+ "Archive a Confluence page by moving it to trash. The page can be restored by space admins. Note: UI may require a note explaining why the page was archived."
1698
+ ).category(core.ToolCategory.WEB).tag("confluence").tag("archive").tag("delete").usageNotes(
1699
+ "Use this to archive outdated or obsolete documentation. The page is moved to trash, not permanently deleted. Space admins can restore it if needed. Be very careful - only archive pages that are truly obsolete."
1700
+ ).conflicts(["create-confluence-page"]).schema(
1701
+ zod.z.object({
1702
+ page_id: zod.z.string().describe("The ID of the page to archive"),
1703
+ reason: zod.z.string().optional().describe("Optional reason for archiving (for audit trail)")
1704
+ })
1705
+ ).implement(async ({ page_id, reason }) => {
1622
1706
  logger20.info("archive-confluence-page called", {
1623
1707
  page_id,
1624
1708
  ...reason ? { reason } : {}
1625
1709
  });
1626
1710
  try {
1627
- const { ATLASSIAN_SITE_URL } = getAuth();
1628
- const getResponse = await axios11__default.default.get(
1629
- `${ATLASSIAN_SITE_URL}/wiki/rest/api/content/${page_id}`,
1630
- {
1631
- headers: {
1632
- Authorization: getAuthHeader2()
1633
- },
1634
- params: { expand: "version,body.storage,space" }
1635
- }
1636
- );
1711
+ const getResponse = await request2.get(`/wiki/rest/api/content/${page_id}`, {
1712
+ params: { expand: "version,body.storage,space" }
1713
+ });
1637
1714
  const currentVersion = getResponse.data.version.number;
1638
1715
  const pageData = getResponse.data;
1639
- await axios11__default.default.put(
1640
- `${ATLASSIAN_SITE_URL}/wiki/rest/api/content/${page_id}`,
1716
+ await request2.put(
1717
+ `/wiki/rest/api/content/${page_id}`,
1641
1718
  {
1642
1719
  version: { number: currentVersion + 1 },
1643
1720
  title: pageData.title,
@@ -1648,7 +1725,6 @@ function createArchiveConfluencePageTool(getAuth, getAuthHeader2, logger20) {
1648
1725
  },
1649
1726
  {
1650
1727
  headers: {
1651
- Authorization: getAuthHeader2(),
1652
1728
  "Content-Type": "application/json"
1653
1729
  }
1654
1730
  }
@@ -1671,14 +1747,15 @@ function createArchiveConfluencePageTool(getAuth, getAuthHeader2, logger20) {
1671
1747
  }
1672
1748
  });
1673
1749
  } catch (error) {
1750
+ const description = request2.describeError(error);
1674
1751
  logger20.error("archive-confluence-page error", {
1675
1752
  page_id,
1676
- error: error.response?.data?.message || error.message,
1677
- status: error.response?.status
1753
+ ...description.message !== void 0 ? { error: description.message } : {},
1754
+ ...description.status !== void 0 ? { status: description.status } : {}
1678
1755
  });
1679
1756
  return JSON.stringify({
1680
1757
  success: false,
1681
- error: error.response?.data?.message || error.message
1758
+ error: description.message
1682
1759
  });
1683
1760
  }
1684
1761
  }).build();
@@ -1687,13 +1764,14 @@ function createArchiveConfluencePageTool(getAuth, getAuthHeader2, logger20) {
1687
1764
  // src/web/confluence/index.ts
1688
1765
  var logLevel2 = process.env.LOG_LEVEL?.toLowerCase() || core.LogLevel.INFO;
1689
1766
  var logger2 = core.createLogger("[tools:confluence]", { level: logLevel2 });
1690
- var searchConfluence = createSearchConfluenceTool(getConfig, getAuthHeader, logger2);
1691
- var getConfluencePage = createGetConfluencePageTool(getConfig, getAuthHeader, logger2);
1692
- var listConfluenceSpaces = createListConfluenceSpacesTool(getConfig, getAuthHeader, logger2);
1693
- var getSpacePages = createGetSpacePagesTool(getConfig, getAuthHeader, logger2);
1694
- var createConfluencePage = createCreateConfluencePageTool(getConfig, getAuthHeader, logger2);
1695
- var updateConfluencePage = createUpdateConfluencePageTool(getConfig, getAuthHeader, logger2);
1696
- var archiveConfluencePage = createArchiveConfluencePageTool(getConfig, getAuthHeader, logger2);
1767
+ var request = createConfluenceRequest(getConfig);
1768
+ var searchConfluence = createSearchConfluenceTool(request, logger2);
1769
+ var getConfluencePage = createGetConfluencePageTool(request, logger2);
1770
+ var listConfluenceSpaces = createListConfluenceSpacesTool(request, logger2);
1771
+ var getSpacePages = createGetSpacePagesTool(request, logger2);
1772
+ var createConfluencePage = createCreateConfluencePageTool(request, logger2);
1773
+ var updateConfluencePage = createUpdateConfluencePageTool(request, logger2);
1774
+ var archiveConfluencePage = createArchiveConfluencePageTool(request, logger2);
1697
1775
  var confluenceTools = [
1698
1776
  // Read tools
1699
1777
  searchConfluence,
@@ -1706,22 +1784,17 @@ var confluenceTools = [
1706
1784
  archiveConfluencePage
1707
1785
  ];
1708
1786
  function createConfluenceTools(config = {}) {
1709
- const {
1710
- apiKey,
1711
- email,
1712
- siteUrl,
1713
- logLevel: customLogLevel
1714
- } = config;
1787
+ const { apiKey, email, siteUrl, logLevel: customLogLevel } = config;
1715
1788
  const getConfiguredAuth = createGetConfiguredAuth(apiKey, email, siteUrl);
1716
- const getConfiguredAuthHeader = createGetConfiguredAuthHeader(getConfiguredAuth);
1789
+ const request2 = createConfluenceRequest(getConfiguredAuth);
1717
1790
  const toolLogger = customLogLevel ? core.createLogger("tools:confluence", { level: customLogLevel }) : logger2;
1718
- const searchConfluence2 = createSearchConfluenceTool(getConfiguredAuth, getConfiguredAuthHeader, toolLogger);
1719
- const getConfluencePage2 = createGetConfluencePageTool(getConfiguredAuth, getConfiguredAuthHeader, toolLogger);
1720
- const listConfluenceSpaces2 = createListConfluenceSpacesTool(getConfiguredAuth, getConfiguredAuthHeader, toolLogger);
1721
- const getSpacePages2 = createGetSpacePagesTool(getConfiguredAuth, getConfiguredAuthHeader, toolLogger);
1722
- const createConfluencePage2 = createCreateConfluencePageTool(getConfiguredAuth, getConfiguredAuthHeader, toolLogger);
1723
- const updateConfluencePage2 = createUpdateConfluencePageTool(getConfiguredAuth, getConfiguredAuthHeader, toolLogger);
1724
- const archiveConfluencePage2 = createArchiveConfluencePageTool(getConfiguredAuth, getConfiguredAuthHeader, toolLogger);
1791
+ const searchConfluence2 = createSearchConfluenceTool(request2, toolLogger);
1792
+ const getConfluencePage2 = createGetConfluencePageTool(request2, toolLogger);
1793
+ const listConfluenceSpaces2 = createListConfluenceSpacesTool(request2, toolLogger);
1794
+ const getSpacePages2 = createGetSpacePagesTool(request2, toolLogger);
1795
+ const createConfluencePage2 = createCreateConfluencePageTool(request2, toolLogger);
1796
+ const updateConfluencePage2 = createUpdateConfluencePageTool(request2, toolLogger);
1797
+ const archiveConfluencePage2 = createArchiveConfluencePageTool(request2, toolLogger);
1725
1798
  return {
1726
1799
  searchConfluence: searchConfluence2,
1727
1800
  getConfluencePage: getConfluencePage2,
@@ -1912,14 +1985,14 @@ function createJsonStringifyTool(defaultIndent = 2, defaultPretty = false) {
1912
1985
  };
1913
1986
  }).build();
1914
1987
  }
1915
- function isRecord(value) {
1988
+ function isRecord2(value) {
1916
1989
  return typeof value === "object" && value !== null && !Array.isArray(value);
1917
1990
  }
1918
1991
  function getPathValue(current, part) {
1919
1992
  const arrayMatch = part.match(/^(\w+)\[(\d+)\]$/);
1920
1993
  if (arrayMatch) {
1921
1994
  const [, key, index] = arrayMatch;
1922
- if (!isRecord(current)) {
1995
+ if (!isRecord2(current)) {
1923
1996
  return void 0;
1924
1997
  }
1925
1998
  const candidate = current[key];
@@ -1928,7 +2001,7 @@ function getPathValue(current, part) {
1928
2001
  }
1929
2002
  return candidate[parseInt(index, 10)];
1930
2003
  }
1931
- if (!isRecord(current)) {
2004
+ if (!isRecord2(current)) {
1932
2005
  return void 0;
1933
2006
  }
1934
2007
  return current[part];
@@ -2584,7 +2657,7 @@ async function initializeFromEnv() {
2584
2657
  // src/data/neo4j/embeddings/utils.ts
2585
2658
  function withProviderErrorMetadata(error, message) {
2586
2659
  const wrappedError = new Error(message);
2587
- if (!isRecord2(error)) return wrappedError;
2660
+ if (!isRecord3(error)) return wrappedError;
2588
2661
  if (typeof error.code === "string") wrappedError.code = error.code;
2589
2662
  if (error.response !== void 0) wrappedError.response = error.response;
2590
2663
  return wrappedError;
@@ -2597,12 +2670,12 @@ var DEFAULT_RETRY_CONFIG2 = {
2597
2670
  // 10 seconds
2598
2671
  backoffMultiplier: 2
2599
2672
  };
2600
- function isRecord2(value) {
2673
+ function isRecord3(value) {
2601
2674
  return typeof value === "object" && value !== null;
2602
2675
  }
2603
2676
  function isRetryableError2(error) {
2604
- if (!isRecord2(error)) return false;
2605
- const response = isRecord2(error.response) ? error.response : void 0;
2677
+ if (!isRecord3(error)) return false;
2678
+ const response = isRecord3(error.response) ? error.response : void 0;
2606
2679
  const status = typeof response?.status === "number" ? response.status : void 0;
2607
2680
  const code = typeof error.code === "string" ? error.code : void 0;
2608
2681
  const message = typeof error.message === "string" ? error.message : void 0;
@@ -2728,7 +2801,7 @@ var OpenAIEmbeddingProvider = class {
2728
2801
  const modelToUse = model || this.defaultModel;
2729
2802
  return retryWithBackoff2(async () => {
2730
2803
  try {
2731
- const response = await axios11__default.default.post(
2804
+ const response = await axios5__default.default.post(
2732
2805
  `${this.baseURL}/embeddings`,
2733
2806
  {
2734
2807
  input: text,
@@ -2758,7 +2831,7 @@ var OpenAIEmbeddingProvider = class {
2758
2831
  };
2759
2832
  } catch (error) {
2760
2833
  let message;
2761
- const response = axios11__default.default.isAxiosError(error) ? error.response : void 0;
2834
+ const response = axios5__default.default.isAxiosError(error) ? error.response : void 0;
2762
2835
  if (response?.status === 401) {
2763
2836
  message = "Invalid OpenAI API key. Get your key at https://platform.openai.com/api-keys";
2764
2837
  } else if (response?.status === 429) {
@@ -2785,7 +2858,7 @@ var OpenAIEmbeddingProvider = class {
2785
2858
  const modelToUse = model || this.defaultModel;
2786
2859
  return retryWithBackoff2(async () => {
2787
2860
  try {
2788
- const response = await axios11__default.default.post(
2861
+ const response = await axios5__default.default.post(
2789
2862
  `${this.baseURL}/embeddings`,
2790
2863
  {
2791
2864
  input: texts,
@@ -2816,7 +2889,7 @@ var OpenAIEmbeddingProvider = class {
2816
2889
  };
2817
2890
  } catch (error) {
2818
2891
  let message;
2819
- const response = axios11__default.default.isAxiosError(error) ? error.response : void 0;
2892
+ const response = axios5__default.default.isAxiosError(error) ? error.response : void 0;
2820
2893
  if (response?.status === 401) {
2821
2894
  message = "Invalid OpenAI API key. Get your key at https://platform.openai.com/api-keys";
2822
2895
  } else if (response?.status === 429) {
@@ -2858,7 +2931,7 @@ var CohereEmbeddingProvider = class {
2858
2931
  const modelToUse = model || this.defaultModel;
2859
2932
  return retryWithBackoff2(async () => {
2860
2933
  try {
2861
- const response = await axios11__default.default.post(
2934
+ const response = await axios5__default.default.post(
2862
2935
  `${this.baseUrl}/embed`,
2863
2936
  {
2864
2937
  texts,
@@ -2886,7 +2959,7 @@ var CohereEmbeddingProvider = class {
2886
2959
  } : void 0
2887
2960
  };
2888
2961
  } catch (error) {
2889
- if (axios11__default.default.isAxiosError(error)) {
2962
+ if (axios5__default.default.isAxiosError(error)) {
2890
2963
  const status = error.response?.status;
2891
2964
  const providerMessage = error.response?.data?.message || error.message;
2892
2965
  let message;
@@ -2924,7 +2997,7 @@ var HuggingFaceEmbeddingProvider = class {
2924
2997
  const modelToUse = model || this.defaultModel;
2925
2998
  return retryWithBackoff2(async () => {
2926
2999
  try {
2927
- const response = await axios11__default.default.post(
3000
+ const response = await axios5__default.default.post(
2928
3001
  `${this.baseUrl}/${modelToUse}`,
2929
3002
  {
2930
3003
  inputs: text,
@@ -2946,7 +3019,7 @@ var HuggingFaceEmbeddingProvider = class {
2946
3019
  dimensions: embedding.length
2947
3020
  };
2948
3021
  } catch (error) {
2949
- if (axios11__default.default.isAxiosError(error)) {
3022
+ if (axios5__default.default.isAxiosError(error)) {
2950
3023
  const status = error.response?.status;
2951
3024
  const message = error.response?.data?.error || error.message;
2952
3025
  let messageText;
@@ -2971,7 +3044,7 @@ var HuggingFaceEmbeddingProvider = class {
2971
3044
  const modelToUse = model || this.defaultModel;
2972
3045
  return retryWithBackoff2(async () => {
2973
3046
  try {
2974
- const response = await axios11__default.default.post(
3047
+ const response = await axios5__default.default.post(
2975
3048
  `${this.baseUrl}/${modelToUse}`,
2976
3049
  {
2977
3050
  inputs: texts,
@@ -2994,7 +3067,7 @@ var HuggingFaceEmbeddingProvider = class {
2994
3067
  dimensions
2995
3068
  };
2996
3069
  } catch (error) {
2997
- if (axios11__default.default.isAxiosError(error)) {
3070
+ if (axios5__default.default.isAxiosError(error)) {
2998
3071
  const status = error.response?.status;
2999
3072
  const message = error.response?.data?.error || error.message;
3000
3073
  let messageText;
@@ -3043,7 +3116,7 @@ var VoyageEmbeddingProvider = class {
3043
3116
  const modelToUse = model || this.defaultModel;
3044
3117
  return retryWithBackoff2(async () => {
3045
3118
  try {
3046
- const response = await axios11__default.default.post(
3119
+ const response = await axios5__default.default.post(
3047
3120
  `${this.baseUrl}/embeddings`,
3048
3121
  {
3049
3122
  input: texts,
@@ -3070,7 +3143,7 @@ var VoyageEmbeddingProvider = class {
3070
3143
  } : void 0
3071
3144
  };
3072
3145
  } catch (error) {
3073
- if (axios11__default.default.isAxiosError(error)) {
3146
+ if (axios5__default.default.isAxiosError(error)) {
3074
3147
  const status = error.response?.status;
3075
3148
  const message = error.response?.data?.message || error.message;
3076
3149
  let messageText;
@@ -3107,7 +3180,7 @@ var OllamaEmbeddingProvider = class {
3107
3180
  const modelToUse = model || this.defaultModel;
3108
3181
  return retryWithBackoff2(async () => {
3109
3182
  try {
3110
- const response = await axios11__default.default.post(
3183
+ const response = await axios5__default.default.post(
3111
3184
  `${this.baseUrl}/api/embeddings`,
3112
3185
  {
3113
3186
  model: modelToUse,
@@ -3126,7 +3199,7 @@ var OllamaEmbeddingProvider = class {
3126
3199
  dimensions: embedding.length
3127
3200
  };
3128
3201
  } catch (error) {
3129
- if (axios11__default.default.isAxiosError(error)) {
3202
+ if (axios5__default.default.isAxiosError(error)) {
3130
3203
  const status = error.response?.status;
3131
3204
  const message = error.response?.data?.error || error.message;
3132
3205
  let messageText;
@@ -3519,6 +3592,16 @@ function formatResults(records) {
3519
3592
  });
3520
3593
  }
3521
3594
 
3595
+ // src/data/neo4j/tools/session-owner.ts
3596
+ async function withNeo4jSession(database, operation) {
3597
+ const session = neo4jPool.getSession(database);
3598
+ try {
3599
+ return await operation(session);
3600
+ } finally {
3601
+ await session.close();
3602
+ }
3603
+ }
3604
+
3522
3605
  // src/data/neo4j/tools/neo4j-query.ts
3523
3606
  var logger4 = core.createLogger("agentforge:tools:neo4j:query");
3524
3607
  function createNeo4jQueryTool() {
@@ -3539,8 +3622,7 @@ function createNeo4jQueryTool() {
3539
3622
  ...input.database ? { database: input.database } : {}
3540
3623
  });
3541
3624
  try {
3542
- const session = neo4jPool.getSession(input.database);
3543
- try {
3625
+ return await withNeo4jSession(input.database, async (session) => {
3544
3626
  const result = await session.run(input.cypher, input.parameters || {});
3545
3627
  const formattedResults = formatResults(result.records);
3546
3628
  const duration = Date.now() - startTime;
@@ -3569,9 +3651,7 @@ function createNeo4jQueryTool() {
3569
3651
  }
3570
3652
  }
3571
3653
  };
3572
- } finally {
3573
- await session.close();
3574
- }
3654
+ });
3575
3655
  } catch (error) {
3576
3656
  const duration = Date.now() - startTime;
3577
3657
  const errorMessage = error instanceof Error ? error.message : "Failed to execute query";
@@ -3599,8 +3679,7 @@ function createNeo4jGetSchemaTool() {
3599
3679
  };
3600
3680
  }
3601
3681
  try {
3602
- const session = neo4jPool.getSession(input.database);
3603
- try {
3682
+ return await withNeo4jSession(input.database, async (session) => {
3604
3683
  const labelsResult = await session.run("CALL db.labels()");
3605
3684
  const nodeLabels = labelsResult.records.map((r) => r.get("label"));
3606
3685
  const relTypesResult = await session.run("CALL db.relationshipTypes()");
@@ -3640,9 +3719,7 @@ function createNeo4jGetSchemaTool() {
3640
3719
  totalIndexes: indexes.length
3641
3720
  }
3642
3721
  };
3643
- } finally {
3644
- await session.close();
3645
- }
3722
+ });
3646
3723
  } catch (error) {
3647
3724
  return {
3648
3725
  success: false,
@@ -3721,8 +3798,7 @@ function createNeo4jFindNodesTool() {
3721
3798
  }
3722
3799
  try {
3723
3800
  const safeLabel = validateLabel(input.label);
3724
- const session = neo4jPool.getSession(input.database);
3725
- try {
3801
+ return await withNeo4jSession(input.database, async (session) => {
3726
3802
  let cypher = `MATCH (n:${safeLabel})`;
3727
3803
  let parameters = {};
3728
3804
  if (input.properties && Object.keys(input.properties).length > 0) {
@@ -3744,9 +3820,7 @@ function createNeo4jFindNodesTool() {
3744
3820
  limit: input.limit
3745
3821
  }
3746
3822
  };
3747
- } finally {
3748
- await session.close();
3749
- }
3823
+ });
3750
3824
  } catch (error) {
3751
3825
  return {
3752
3826
  success: false,
@@ -3772,8 +3846,7 @@ function createNeo4jTraverseTool() {
3772
3846
  try {
3773
3847
  const safeDirection = validateDirection(input.direction || "outgoing");
3774
3848
  const safeRelType = input.relationshipType ? validateRelationshipType(input.relationshipType) : null;
3775
- const session = neo4jPool.getSession(input.database);
3776
- try {
3849
+ return await withNeo4jSession(input.database, async (session) => {
3777
3850
  let relPattern = "";
3778
3851
  if (safeDirection === "OUTGOING") {
3779
3852
  relPattern = safeRelType ? `-[r:${safeRelType}*1..${input.maxDepth}]->` : `-[r*1..${input.maxDepth}]->`;
@@ -3810,9 +3883,7 @@ function createNeo4jTraverseTool() {
3810
3883
  maxDepth: input.maxDepth
3811
3884
  }
3812
3885
  };
3813
- } finally {
3814
- await session.close();
3815
- }
3886
+ });
3816
3887
  } catch (error) {
3817
3888
  return {
3818
3889
  success: false,
@@ -3837,8 +3908,7 @@ function createNeo4jVectorSearchTool() {
3837
3908
  };
3838
3909
  }
3839
3910
  try {
3840
- const session = neo4jPool.getSession(input.database);
3841
- try {
3911
+ return await withNeo4jSession(input.database, async (session) => {
3842
3912
  const cypher = `
3843
3913
  CALL db.index.vector.queryNodes($indexName, $limit, $queryVector)
3844
3914
  YIELD node, score
@@ -3865,9 +3935,7 @@ function createNeo4jVectorSearchTool() {
3865
3935
  limit: input.limit
3866
3936
  }
3867
3937
  };
3868
- } finally {
3869
- await session.close();
3870
- }
3938
+ });
3871
3939
  } catch (error) {
3872
3940
  const errorMessage = error instanceof Error ? error.message : "Failed to perform vector search";
3873
3941
  let helpText = "";
@@ -3913,8 +3981,7 @@ function createNeo4jVectorSearchWithEmbeddingTool() {
3913
3981
  });
3914
3982
  try {
3915
3983
  const embeddingResult = await embeddingManager.generateEmbedding(input.queryText, input.model);
3916
- const session = neo4jPool.getSession(input.database);
3917
- try {
3984
+ return await withNeo4jSession(input.database, async (session) => {
3918
3985
  const cypher = `
3919
3986
  CALL db.index.vector.queryNodes($indexName, $limit, $queryVector)
3920
3987
  YIELD node, score
@@ -3956,9 +4023,7 @@ function createNeo4jVectorSearchWithEmbeddingTool() {
3956
4023
  usage: embeddingResult.usage
3957
4024
  }
3958
4025
  };
3959
- } finally {
3960
- await session.close();
3961
- }
4026
+ });
3962
4027
  } catch (error) {
3963
4028
  const duration = Date.now() - startTime;
3964
4029
  const errorMessage = error instanceof Error ? error.message : "Failed to perform vector search with embedding";
@@ -4014,8 +4079,7 @@ function createNeo4jCreateNodeWithEmbeddingTool() {
4014
4079
  const embeddingResult = await embeddingManager.generateEmbedding(textToEmbed, input.model);
4015
4080
  const safeLabel = validateLabel(input.label);
4016
4081
  const safeEmbeddingProp = validatePropertyKey(input.embeddingProperty || "embedding");
4017
- const session = neo4jPool.getSession(input.database);
4018
- try {
4082
+ return await withNeo4jSession(input.database, async (session) => {
4019
4083
  const allProperties = {
4020
4084
  ...input.properties
4021
4085
  };
@@ -4049,9 +4113,7 @@ function createNeo4jCreateNodeWithEmbeddingTool() {
4049
4113
  },
4050
4114
  message: `Created node with label '${input.label}' and ${embeddingResult.dimensions}-dimensional embedding`
4051
4115
  };
4052
- } finally {
4053
- await session.close();
4054
- }
4116
+ });
4055
4117
  } catch (error) {
4056
4118
  const errorMessage = error instanceof Error ? error.message : "Failed to create node with embedding";
4057
4119
  let helpText = "";
@@ -10338,49 +10400,57 @@ var fileSearchSchema = zod.z.object({
10338
10400
  recursive: zod.z.boolean().default(true).describe("Search in subdirectories"),
10339
10401
  caseSensitive: zod.z.boolean().default(false).describe("Case-sensitive pattern matching")
10340
10402
  });
10403
+ async function* traverseDirectory(directory, recursive) {
10404
+ const entries = await fs.promises.readdir(directory, { withFileTypes: true });
10405
+ for (const entry of entries) {
10406
+ const fullPath = path__namespace.join(directory, entry.name);
10407
+ const isDirectory = entry.isDirectory();
10408
+ yield {
10409
+ name: entry.name,
10410
+ fullPath,
10411
+ isFile: entry.isFile(),
10412
+ isDirectory
10413
+ };
10414
+ if (recursive && isDirectory) {
10415
+ yield* traverseDirectory(fullPath, true);
10416
+ }
10417
+ }
10418
+ }
10419
+
10420
+ // src/file/directory/tools/directory-list.ts
10341
10421
  function createDirectoryListTool(defaultRecursive = false, defaultIncludeDetails = false, policy = DEFAULT_FILE_SYSTEM_POLICY) {
10342
10422
  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) => {
10343
10423
  const safePath = await policy.resolvePath(input.path, "directory listing");
10344
10424
  const includeDetails = input.includeDetails ?? defaultIncludeDetails;
10345
- const listFiles = async (dir, recursive2) => {
10346
- const entries = await fs.promises.readdir(dir, { withFileTypes: true });
10347
- const files2 = [];
10348
- for (const entry of entries) {
10349
- const fullPath = path__namespace.join(dir, entry.name);
10350
- const relativePath = path__namespace.relative(safePath, fullPath);
10351
- if (input.extension && !entry.name.endsWith(input.extension)) {
10352
- if (!entry.isDirectory() || !recursive2) {
10353
- continue;
10354
- }
10355
- }
10356
- if (includeDetails) {
10357
- const stats = await fs.promises.lstat(fullPath);
10358
- files2.push({
10359
- name: entry.name,
10360
- path: relativePath,
10361
- fullPath,
10362
- isFile: entry.isFile(),
10363
- isDirectory: entry.isDirectory(),
10364
- size: stats.size,
10365
- modified: stats.mtime.toISOString()
10366
- });
10367
- } else {
10368
- files2.push({
10369
- name: entry.name,
10370
- path: relativePath,
10371
- isFile: entry.isFile(),
10372
- isDirectory: entry.isDirectory()
10373
- });
10374
- }
10375
- if (recursive2 && entry.isDirectory()) {
10376
- const subFiles = await listFiles(fullPath, true);
10377
- files2.push(...subFiles);
10425
+ const recursive = input.recursive ?? defaultRecursive;
10426
+ const files = [];
10427
+ for await (const entry of traverseDirectory(safePath, recursive)) {
10428
+ const relativePath = path__namespace.relative(safePath, entry.fullPath);
10429
+ if (input.extension && !entry.name.endsWith(input.extension)) {
10430
+ if (!entry.isDirectory || !recursive) {
10431
+ continue;
10378
10432
  }
10379
10433
  }
10380
- return files2;
10381
- };
10382
- const recursive = input.recursive ?? defaultRecursive;
10383
- const files = await listFiles(safePath, recursive);
10434
+ if (includeDetails) {
10435
+ const stats = await fs.promises.lstat(entry.fullPath);
10436
+ files.push({
10437
+ name: entry.name,
10438
+ path: relativePath,
10439
+ fullPath: entry.fullPath,
10440
+ isFile: entry.isFile,
10441
+ isDirectory: entry.isDirectory,
10442
+ size: stats.size,
10443
+ modified: stats.mtime.toISOString()
10444
+ });
10445
+ } else {
10446
+ files.push({
10447
+ name: entry.name,
10448
+ path: relativePath,
10449
+ isFile: entry.isFile,
10450
+ isDirectory: entry.isDirectory
10451
+ });
10452
+ }
10453
+ }
10384
10454
  const result = {
10385
10455
  path: input.path,
10386
10456
  files,
@@ -10416,24 +10486,14 @@ function createFileSearchTool(defaultRecursive = true, defaultCaseSensitive = fa
10416
10486
  const recursive = input.recursive ?? defaultRecursive;
10417
10487
  const caseSensitive = input.caseSensitive ?? defaultCaseSensitive;
10418
10488
  const safeDirectory = await policy.resolvePath(input.directory, "file search");
10419
- const searchFiles = async (dir) => {
10420
- const entries = await fs.promises.readdir(dir, { withFileTypes: true });
10421
- const matches2 = [];
10422
- const regexPattern = input.pattern.replace(/\./g, "\\.").replace(/\*/g, ".*");
10423
- const regex = new RegExp(`^${regexPattern}$`, caseSensitive ? "" : "i");
10424
- for (const entry of entries) {
10425
- const fullPath = path__namespace.join(dir, entry.name);
10426
- if (entry.isFile() && regex.test(entry.name)) {
10427
- matches2.push(fullPath);
10428
- }
10429
- if (recursive && entry.isDirectory()) {
10430
- const subMatches = await searchFiles(fullPath);
10431
- matches2.push(...subMatches);
10432
- }
10489
+ const regexPattern = input.pattern.replace(/\./g, "\\.").replace(/\*/g, ".*");
10490
+ const regex = new RegExp(`^${regexPattern}$`, caseSensitive ? "" : "i");
10491
+ const matches = [];
10492
+ for await (const entry of traverseDirectory(safeDirectory, recursive)) {
10493
+ if (entry.isFile && regex.test(entry.name)) {
10494
+ matches.push(entry.fullPath);
10433
10495
  }
10434
- return matches2;
10435
- };
10436
- const matches = await searchFiles(safeDirectory);
10496
+ }
10437
10497
  return {
10438
10498
  directory: input.directory,
10439
10499
  pattern: input.pattern,
@@ -11395,11 +11455,11 @@ var AskHumanInputSchema = zod.z.object({
11395
11455
  });
11396
11456
  var logLevel3 = process.env.LOG_LEVEL?.toLowerCase() || core.LogLevel.INFO;
11397
11457
  var logger19 = core.createLogger("agentforge:tools:agent:ask-human", { level: logLevel3 });
11398
- function isRecord3(value) {
11458
+ function isRecord4(value) {
11399
11459
  return typeof value === "object" && value !== null;
11400
11460
  }
11401
11461
  function resolveInterrupt(module) {
11402
- if (!isRecord3(module)) {
11462
+ if (!isRecord4(module)) {
11403
11463
  return void 0;
11404
11464
  }
11405
11465
  const candidate = module.interrupt;