@agentforge/tools 0.18.4 → 0.18.6

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.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import { toolBuilder, ToolCategory, LogLevel, createLogger, createTool } from '@agentforge/core';
2
2
  import { lookup } from 'dns/promises';
3
3
  import net from 'net';
4
- import axios11 from 'axios';
4
+ import axios5 from 'axios';
5
5
  import { z } from 'zod';
6
6
  import * as cheerio from 'cheerio';
7
7
  import { WebClient } from '@slack/web-api';
@@ -201,7 +201,7 @@ async function requestWithDestinationPolicy(config, policy = {}) {
201
201
  for (let redirectCount = 0; ; redirectCount += 1) {
202
202
  const validatedAddresses = await validateDestinationAllowed(currentUrl, effectivePolicy);
203
203
  currentConfig.lookup = createPinnedLookup(validatedAddresses);
204
- const response = await axios11(currentConfig);
204
+ const response = await axios5(currentConfig);
205
205
  if (!isRedirectStatus(response.status)) return response;
206
206
  if (effectivePolicy.allowRedirects === false) blockDestination(currentUrl, "redirect");
207
207
  if (redirectCount >= maxRedirects) throw new DestinationPolicyError(currentUrl, "redirect", `Maximum redirect count (${maxRedirects}) exceeded for ${currentUrl}`);
@@ -712,7 +712,7 @@ var DuckDuckGoProvider = class {
712
712
  async search(query, maxResults, timeout = DEFAULT_TIMEOUT) {
713
713
  try {
714
714
  return await retryWithBackoff(async () => {
715
- const response = await axios11.get("https://api.duckduckgo.com/", {
715
+ const response = await axios5.get("https://api.duckduckgo.com/", {
716
716
  params: {
717
717
  q: query,
718
718
  format: "json"
@@ -813,7 +813,7 @@ var SerperProvider = class {
813
813
  }
814
814
  try {
815
815
  return await retryWithBackoff(async () => {
816
- const response = await axios11.post(
816
+ const response = await axios5.post(
817
817
  "https://google.serper.dev/search",
818
818
  {
819
819
  q: query,
@@ -949,47 +949,33 @@ var webSearch = toolBuilder().name("web-search").description(
949
949
  };
950
950
  }
951
951
  }).build();
952
- function createGetConfiguredSlackClient(token, botName = "AgentForge Bot", botIcon = ":robot_face:") {
953
- let configuredClient = null;
954
- return function getConfiguredSlackClient() {
955
- if (!configuredClient) {
956
- const slackToken = token || process.env.SLACK_USER_TOKEN || process.env.SLACK_BOT_TOKEN;
957
- if (!slackToken) {
958
- throw new Error(
959
- "Slack token not configured. Please provide a token in config or set SLACK_USER_TOKEN or SLACK_BOT_TOKEN environment variable."
960
- );
952
+ function createLazySlackClient(resolveToken, config, missingTokenError) {
953
+ let client = null;
954
+ return () => {
955
+ const token = resolveToken();
956
+ if (!client) {
957
+ if (!token) {
958
+ throw new Error(missingTokenError);
961
959
  }
962
- configuredClient = new WebClient(slackToken);
960
+ client = new WebClient(token);
963
961
  }
964
- return {
965
- client: configuredClient,
966
- config: {
967
- token: token || process.env.SLACK_USER_TOKEN || process.env.SLACK_BOT_TOKEN || "",
968
- botName,
969
- botIcon
970
- }
971
- };
962
+ return { client, config: { token: token || "", ...config } };
972
963
  };
973
964
  }
974
- var defaultSlackClient = null;
965
+ function createGetConfiguredSlackClient(token, botName = "AgentForge Bot", botIcon = ":robot_face:") {
966
+ return createLazySlackClient(
967
+ () => token || process.env.SLACK_USER_TOKEN || process.env.SLACK_BOT_TOKEN,
968
+ { botName, botIcon },
969
+ "Slack token not configured. Please provide a token in config or set SLACK_USER_TOKEN or SLACK_BOT_TOKEN environment variable."
970
+ );
971
+ }
972
+ var getLazyDefaultSlackClient = createLazySlackClient(
973
+ () => process.env.SLACK_USER_TOKEN || process.env.SLACK_BOT_TOKEN,
974
+ { botName: "AgentForge Bot", botIcon: ":robot_face:" },
975
+ "Slack token not configured. Please set SLACK_USER_TOKEN or SLACK_BOT_TOKEN environment variable."
976
+ );
975
977
  function getDefaultSlackClient() {
976
- if (!defaultSlackClient) {
977
- const token = process.env.SLACK_USER_TOKEN || process.env.SLACK_BOT_TOKEN;
978
- if (!token) {
979
- throw new Error(
980
- "Slack token not configured. Please set SLACK_USER_TOKEN or SLACK_BOT_TOKEN environment variable."
981
- );
982
- }
983
- defaultSlackClient = new WebClient(token);
984
- }
985
- return {
986
- client: defaultSlackClient,
987
- config: {
988
- token: process.env.SLACK_USER_TOKEN || process.env.SLACK_BOT_TOKEN || "",
989
- botName: "AgentForge Bot",
990
- botIcon: ":robot_face:"
991
- }
992
- };
978
+ return getLazyDefaultSlackClient();
993
979
  }
994
980
  function createSendSlackMessageTool(getSlackClient, logger20) {
995
981
  return toolBuilder().name("send-slack-message").description("Send a message to a Slack channel for team communication and notifications").category(ToolCategory.WEB).tags(["slack", "messaging", "communication"]).usageNotes(
@@ -1216,38 +1202,91 @@ function createGetConfiguredAuth(apiKey, email, siteUrl) {
1216
1202
  return { ATLASSIAN_API_KEY, ATLASSIAN_EMAIL, ATLASSIAN_SITE_URL };
1217
1203
  };
1218
1204
  }
1219
- function createGetConfiguredAuthHeader(getConfiguredAuth) {
1220
- return function getConfiguredAuthHeader() {
1221
- const { ATLASSIAN_API_KEY, ATLASSIAN_EMAIL } = getConfiguredAuth();
1222
- const auth2 = Buffer.from(`${ATLASSIAN_EMAIL}:${ATLASSIAN_API_KEY}`).toString("base64");
1223
- return `Basic ${auth2}`;
1224
- };
1225
- }
1226
1205
  function getConfig() {
1227
1206
  const ATLASSIAN_API_KEY = process.env.ATLASSIAN_API_KEY || "";
1228
1207
  const ATLASSIAN_EMAIL = process.env.ATLASSIAN_EMAIL || "";
1229
1208
  const ATLASSIAN_SITE_URL = (process.env.ATLASSIAN_SITE_URL || "").replace(/\/$/, "");
1230
1209
  if (!ATLASSIAN_API_KEY || !ATLASSIAN_EMAIL || !ATLASSIAN_SITE_URL) {
1231
- throw new Error("Confluence credentials not configured. Set ATLASSIAN_API_KEY, ATLASSIAN_EMAIL, and ATLASSIAN_SITE_URL in .env");
1210
+ throw new Error(
1211
+ "Confluence credentials not configured. Set ATLASSIAN_API_KEY, ATLASSIAN_EMAIL, and ATLASSIAN_SITE_URL in .env"
1212
+ );
1232
1213
  }
1233
1214
  return { ATLASSIAN_API_KEY, ATLASSIAN_EMAIL, ATLASSIAN_SITE_URL };
1234
1215
  }
1235
- function getAuthHeader() {
1236
- const { ATLASSIAN_API_KEY, ATLASSIAN_EMAIL } = getConfig();
1237
- const auth2 = Buffer.from(`${ATLASSIAN_EMAIL}:${ATLASSIAN_API_KEY}`).toString("base64");
1238
- return `Basic ${auth2}`;
1216
+ function isRecord(value) {
1217
+ return typeof value === "object" && value !== null;
1239
1218
  }
1240
- function createSearchConfluenceTool(getAuth, getAuthHeader2, logger20) {
1241
- return 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(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(z.object({
1242
- query: z.string().describe("Search query or CQL expression (e.g., 'payment processing' or 'space=BL3 AND title~payment')"),
1243
- limit: z.number().optional().describe("Maximum number of results to return (default: 10, max: 25)")
1244
- })).implement(async ({ query, limit = 10 }) => {
1219
+ function describeError(error) {
1220
+ const errorRecord = isRecord(error) ? error : void 0;
1221
+ const response = isRecord(errorRecord?.response) ? errorRecord.response : void 0;
1222
+ const responseData = isRecord(response?.data) ? response.data : void 0;
1223
+ const providerMessage = typeof responseData?.message === "string" ? responseData.message : void 0;
1224
+ const errorMessage = typeof errorRecord?.message === "string" ? errorRecord.message : void 0;
1225
+ return {
1226
+ message: providerMessage || errorMessage,
1227
+ status: typeof response?.status === "number" ? response.status : void 0
1228
+ };
1229
+ }
1230
+ function authenticatedConfig(auth2, config = {}) {
1231
+ const credentials = Buffer.from(`${auth2.ATLASSIAN_EMAIL}:${auth2.ATLASSIAN_API_KEY}`).toString(
1232
+ "base64"
1233
+ );
1234
+ return {
1235
+ ...config,
1236
+ headers: {
1237
+ ...config.headers,
1238
+ Authorization: `Basic ${credentials}`
1239
+ }
1240
+ };
1241
+ }
1242
+ function createConfluenceRequest(getAuth) {
1243
+ return {
1244
+ async get(path13, config = {}) {
1245
+ const auth2 = getAuth();
1246
+ const response = await axios5.get(
1247
+ `${auth2.ATLASSIAN_SITE_URL}${path13}`,
1248
+ authenticatedConfig(auth2, config)
1249
+ );
1250
+ return { data: response.data, siteUrl: auth2.ATLASSIAN_SITE_URL };
1251
+ },
1252
+ async post(path13, data, config = {}) {
1253
+ const auth2 = getAuth();
1254
+ const response = await axios5.post(
1255
+ `${auth2.ATLASSIAN_SITE_URL}${path13}`,
1256
+ data,
1257
+ authenticatedConfig(auth2, config)
1258
+ );
1259
+ return { data: response.data, siteUrl: auth2.ATLASSIAN_SITE_URL };
1260
+ },
1261
+ async put(path13, data, config = {}) {
1262
+ const auth2 = getAuth();
1263
+ const response = await axios5.put(
1264
+ `${auth2.ATLASSIAN_SITE_URL}${path13}`,
1265
+ data,
1266
+ authenticatedConfig(auth2, config)
1267
+ );
1268
+ return { data: response.data, siteUrl: auth2.ATLASSIAN_SITE_URL };
1269
+ },
1270
+ describeError
1271
+ };
1272
+ }
1273
+ function createSearchConfluenceTool(request2, logger20) {
1274
+ return toolBuilder().name("search-confluence").description(
1275
+ "Search for pages in Confluence using keywords or CQL (Confluence Query Language). Returns matching pages with titles, IDs, and excerpts."
1276
+ ).category(ToolCategory.WEB).tag("confluence").tag("search").tag("knowledge-base").usageNotes(
1277
+ "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."
1278
+ ).suggests(["get-confluence-page"]).schema(
1279
+ z.object({
1280
+ query: z.string().describe(
1281
+ "Search query or CQL expression (e.g., 'payment processing' or 'space=BL3 AND title~payment')"
1282
+ ),
1283
+ limit: z.number().optional().describe("Maximum number of results to return (default: 10, max: 25)")
1284
+ })
1285
+ ).implement(async ({ query, limit = 10 }) => {
1245
1286
  logger20.info("search-confluence called", { query, limit });
1246
1287
  try {
1247
- const { ATLASSIAN_SITE_URL } = getAuth();
1248
- const response = await axios11.get(`${ATLASSIAN_SITE_URL}/wiki/rest/api/content/search`, {
1288
+ const response = await request2.get("/wiki/rest/api/content/search", {
1249
1289
  headers: {
1250
- Authorization: getAuthHeader2(),
1251
1290
  Accept: "application/json"
1252
1291
  },
1253
1292
  params: {
@@ -1256,22 +1295,24 @@ function createSearchConfluenceTool(getAuth, getAuthHeader2, logger20) {
1256
1295
  expand: "space,version"
1257
1296
  }
1258
1297
  });
1259
- const { ATLASSIAN_SITE_URL: siteUrl } = getAuth();
1260
1298
  const results = response.data.results.map((page) => ({
1261
1299
  id: page.id,
1262
1300
  title: page.title,
1263
1301
  type: page.type,
1264
1302
  space: page.space?.name || "Unknown",
1265
1303
  spaceKey: page.space?.key || "",
1266
- url: `${siteUrl}/wiki${page._links.webui}`,
1304
+ url: `${response.siteUrl}/wiki${page._links.webui}`,
1267
1305
  lastModified: page.version?.when || ""
1268
1306
  }));
1269
1307
  if (results.length === 0) {
1270
- logger20.warn("search-confluence returned NO RESULTS - this is a valid outcome, agent should not retry", {
1271
- query,
1272
- limit,
1273
- totalSize: response.data.totalSize
1274
- });
1308
+ logger20.warn(
1309
+ "search-confluence returned NO RESULTS - this is a valid outcome, agent should not retry",
1310
+ {
1311
+ query,
1312
+ limit,
1313
+ totalSize: response.data.totalSize
1314
+ }
1315
+ );
1275
1316
  } else {
1276
1317
  logger20.info("search-confluence result", {
1277
1318
  query,
@@ -1288,28 +1329,33 @@ function createSearchConfluenceTool(getAuth, getAuthHeader2, logger20) {
1288
1329
  results
1289
1330
  });
1290
1331
  } catch (error) {
1332
+ const description = request2.describeError(error);
1291
1333
  logger20.error("search-confluence error", {
1292
1334
  query,
1293
- error: error.response?.data?.message || error.message,
1294
- status: error.response?.status
1335
+ ...description.message !== void 0 ? { error: description.message } : {},
1336
+ ...description.status !== void 0 ? { status: description.status } : {}
1295
1337
  });
1296
1338
  return JSON.stringify({
1297
1339
  success: false,
1298
- error: error.response?.data?.message || error.message
1340
+ error: description.message
1299
1341
  });
1300
1342
  }
1301
1343
  }).build();
1302
1344
  }
1303
- function createGetConfluencePageTool(getAuth, getAuthHeader2, logger20) {
1304
- return 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(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(z.object({
1305
- page_id: z.string().describe("The Confluence page ID (from search results)")
1306
- })).implement(async ({ page_id }) => {
1345
+ function createGetConfluencePageTool(request2, logger20) {
1346
+ return toolBuilder().name("get-confluence-page").description(
1347
+ "Get the full content of a specific Confluence page by its ID. Returns the page title, content (in storage format), space, and metadata."
1348
+ ).category(ToolCategory.WEB).tag("confluence").tag("page").tag("content").usageNotes(
1349
+ "Use this after search-confluence to retrieve the full content of a specific page. The page ID can be found in search results."
1350
+ ).requires(["search-confluence"]).schema(
1351
+ z.object({
1352
+ page_id: z.string().describe("The Confluence page ID (from search results)")
1353
+ })
1354
+ ).implement(async ({ page_id }) => {
1307
1355
  logger20.info("get-confluence-page called", { page_id });
1308
1356
  try {
1309
- const { ATLASSIAN_SITE_URL } = getAuth();
1310
- const response = await axios11.get(`${ATLASSIAN_SITE_URL}/wiki/rest/api/content/${page_id}`, {
1357
+ const response = await request2.get(`/wiki/rest/api/content/${page_id}`, {
1311
1358
  headers: {
1312
- Authorization: getAuthHeader2(),
1313
1359
  Accept: "application/json"
1314
1360
  },
1315
1361
  params: {
@@ -1332,35 +1378,40 @@ function createGetConfluencePageTool(getAuth, getAuthHeader2, logger20) {
1332
1378
  space: page.space?.name || "Unknown",
1333
1379
  spaceKey: page.space?.key || "",
1334
1380
  content: page.body?.storage?.value || "",
1335
- url: `${ATLASSIAN_SITE_URL}/wiki${page._links.webui}`,
1381
+ url: `${response.siteUrl}/wiki${page._links.webui}`,
1336
1382
  created: page.history?.createdDate || "",
1337
1383
  lastModified: page.version?.when || "",
1338
1384
  version: page.version?.number || 1
1339
1385
  }
1340
1386
  });
1341
1387
  } catch (error) {
1388
+ const description = request2.describeError(error);
1342
1389
  logger20.error("get-confluence-page error", {
1343
1390
  page_id,
1344
- error: error.response?.data?.message || error.message,
1345
- status: error.response?.status
1391
+ ...description.message !== void 0 ? { error: description.message } : {},
1392
+ ...description.status !== void 0 ? { status: description.status } : {}
1346
1393
  });
1347
1394
  return JSON.stringify({
1348
1395
  success: false,
1349
- error: error.response?.data?.message || error.message
1396
+ error: description.message
1350
1397
  });
1351
1398
  }
1352
1399
  }).build();
1353
1400
  }
1354
- function createListConfluenceSpacesTool(getAuth, getAuthHeader2, logger20) {
1355
- return 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(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(z.object({
1356
- limit: z.number().optional().describe("Maximum number of spaces to return (default: 25)")
1357
- })).implement(async ({ limit = 25 }) => {
1401
+ function createListConfluenceSpacesTool(request2, logger20) {
1402
+ return toolBuilder().name("list-confluence-spaces").description(
1403
+ "List all available Confluence spaces. Returns space names, keys, types, and descriptions to help identify where to search for information."
1404
+ ).category(ToolCategory.WEB).tag("confluence").tag("spaces").tag("list").usageNotes(
1405
+ "Use this first to discover available spaces before searching. Helps narrow down searches to specific areas (e.g., 'AI', 'BL3', 'Finance')."
1406
+ ).follows(["search-confluence"]).schema(
1407
+ z.object({
1408
+ limit: z.number().optional().describe("Maximum number of spaces to return (default: 25)")
1409
+ })
1410
+ ).implement(async ({ limit = 25 }) => {
1358
1411
  logger20.info("list-confluence-spaces called", { limit });
1359
1412
  try {
1360
- const { ATLASSIAN_SITE_URL } = getAuth();
1361
- const response = await axios11.get(`${ATLASSIAN_SITE_URL}/wiki/rest/api/space`, {
1413
+ const response = await request2.get("/wiki/rest/api/space", {
1362
1414
  headers: {
1363
- Authorization: getAuthHeader2(),
1364
1415
  Accept: "application/json"
1365
1416
  },
1366
1417
  params: {
@@ -1372,7 +1423,7 @@ function createListConfluenceSpacesTool(getAuth, getAuthHeader2, logger20) {
1372
1423
  name: space.name,
1373
1424
  type: space.type,
1374
1425
  description: space.description?.plain?.value || "",
1375
- url: `${ATLASSIAN_SITE_URL}/wiki${space._links.webui}`
1426
+ url: `${response.siteUrl}/wiki${space._links.webui}`
1376
1427
  }));
1377
1428
  logger20.info("list-confluence-spaces result", {
1378
1429
  spaceCount: spaces.length,
@@ -1385,28 +1436,33 @@ function createListConfluenceSpacesTool(getAuth, getAuthHeader2, logger20) {
1385
1436
  spaces
1386
1437
  });
1387
1438
  } catch (error) {
1439
+ const description = request2.describeError(error);
1388
1440
  logger20.error("list-confluence-spaces error", {
1389
- error: error.response?.data?.message || error.message,
1390
- status: error.response?.status
1441
+ ...description.message !== void 0 ? { error: description.message } : {},
1442
+ ...description.status !== void 0 ? { status: description.status } : {}
1391
1443
  });
1392
1444
  return JSON.stringify({
1393
1445
  success: false,
1394
- error: error.response?.data?.message || error.message
1446
+ error: description.message
1395
1447
  });
1396
1448
  }
1397
1449
  }).build();
1398
1450
  }
1399
- function createGetSpacePagesTool(getAuth, getAuthHeader2, logger20) {
1400
- return 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(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(z.object({
1401
- space_key: z.string().describe("The space key (e.g., 'AI', 'BL3', 'FIN')"),
1402
- limit: z.number().optional().describe("Maximum number of pages to return (default: 25)")
1403
- })).implement(async ({ space_key, limit = 25 }) => {
1451
+ function createGetSpacePagesTool(request2, logger20) {
1452
+ return toolBuilder().name("get-space-pages").description(
1453
+ "Get all pages from a specific Confluence space by space key. Useful for browsing content in a particular area."
1454
+ ).category(ToolCategory.WEB).tag("confluence").tag("space").tag("pages").usageNotes(
1455
+ "Use this to explore all pages in a specific space. Get the space key from list-confluence-spaces first."
1456
+ ).requires(["list-confluence-spaces"]).schema(
1457
+ z.object({
1458
+ space_key: z.string().describe("The space key (e.g., 'AI', 'BL3', 'FIN')"),
1459
+ limit: z.number().optional().describe("Maximum number of pages to return (default: 25)")
1460
+ })
1461
+ ).implement(async ({ space_key, limit = 25 }) => {
1404
1462
  logger20.info("get-space-pages called", { space_key, limit });
1405
1463
  try {
1406
- const { ATLASSIAN_SITE_URL } = getAuth();
1407
- const response = await axios11.get(`${ATLASSIAN_SITE_URL}/wiki/rest/api/content`, {
1464
+ const response = await request2.get("/wiki/rest/api/content", {
1408
1465
  headers: {
1409
- Authorization: getAuthHeader2(),
1410
1466
  Accept: "application/json"
1411
1467
  },
1412
1468
  params: {
@@ -1419,14 +1475,17 @@ function createGetSpacePagesTool(getAuth, getAuthHeader2, logger20) {
1419
1475
  const pages = response.data.results.map((page) => ({
1420
1476
  id: page.id,
1421
1477
  title: page.title,
1422
- url: `${ATLASSIAN_SITE_URL}/wiki${page._links.webui}`,
1478
+ url: `${response.siteUrl}/wiki${page._links.webui}`,
1423
1479
  lastModified: page.version?.when || ""
1424
1480
  }));
1425
1481
  if (pages.length === 0) {
1426
- logger20.warn("get-space-pages returned NO PAGES - this is a valid outcome, agent should not retry", {
1427
- space_key,
1428
- limit
1429
- });
1482
+ logger20.warn(
1483
+ "get-space-pages returned NO PAGES - this is a valid outcome, agent should not retry",
1484
+ {
1485
+ space_key,
1486
+ limit
1487
+ }
1488
+ );
1430
1489
  } else {
1431
1490
  logger20.info("get-space-pages result", {
1432
1491
  space_key,
@@ -1442,28 +1501,38 @@ function createGetSpacePagesTool(getAuth, getAuthHeader2, logger20) {
1442
1501
  pages
1443
1502
  });
1444
1503
  } catch (error) {
1504
+ const description = request2.describeError(error);
1445
1505
  logger20.error("get-space-pages error", {
1446
1506
  space_key,
1447
- error: error.response?.data?.message || error.message,
1448
- status: error.response?.status
1507
+ ...description.message !== void 0 ? { error: description.message } : {},
1508
+ ...description.status !== void 0 ? { status: description.status } : {}
1449
1509
  });
1450
1510
  return JSON.stringify({
1451
1511
  success: false,
1452
- error: error.response?.data?.message || error.message
1512
+ error: description.message
1453
1513
  });
1454
1514
  }
1455
1515
  }).build();
1456
1516
  }
1457
- function createCreateConfluencePageTool(getAuth, getAuthHeader2, logger20) {
1458
- return 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(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(z.object({
1459
- space_key: z.string().describe("The space key where the page will be created (e.g., 'AI', 'BL3')"),
1460
- title: z.string().describe("The title of the new page"),
1461
- content: z.string().describe("The page content in HTML format (Confluence storage format)"),
1462
- parent_page_id: z.string().optional().describe("Optional parent page ID to create this as a child page")
1463
- })).implement(async ({ space_key, title, content, parent_page_id }) => {
1464
- logger20.info("create-confluence-page called", { space_key, title, hasParent: !!parent_page_id });
1517
+ function createCreateConfluencePageTool(request2, logger20) {
1518
+ return toolBuilder().name("create-confluence-page").description(
1519
+ "Create a new page in a Confluence space. Requires space key, page title, and content (in HTML storage format)."
1520
+ ).category(ToolCategory.WEB).tag("confluence").tag("create").tag("write").usageNotes(
1521
+ "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."
1522
+ ).requires(["list-confluence-spaces"]).schema(
1523
+ z.object({
1524
+ space_key: z.string().describe("The space key where the page will be created (e.g., 'AI', 'BL3')"),
1525
+ title: z.string().describe("The title of the new page"),
1526
+ content: z.string().describe("The page content in HTML format (Confluence storage format)"),
1527
+ parent_page_id: z.string().optional().describe("Optional parent page ID to create this as a child page")
1528
+ })
1529
+ ).implement(async ({ space_key, title, content, parent_page_id }) => {
1530
+ logger20.info("create-confluence-page called", {
1531
+ space_key,
1532
+ title,
1533
+ hasParent: !!parent_page_id
1534
+ });
1465
1535
  try {
1466
- const { ATLASSIAN_SITE_URL } = getAuth();
1467
1536
  const pageData = {
1468
1537
  type: "page",
1469
1538
  title,
@@ -1478,16 +1547,11 @@ function createCreateConfluencePageTool(getAuth, getAuthHeader2, logger20) {
1478
1547
  if (parent_page_id) {
1479
1548
  pageData.ancestors = [{ id: parent_page_id }];
1480
1549
  }
1481
- const response = await axios11.post(
1482
- `${ATLASSIAN_SITE_URL}/wiki/rest/api/content`,
1483
- pageData,
1484
- {
1485
- headers: {
1486
- Authorization: getAuthHeader2(),
1487
- "Content-Type": "application/json"
1488
- }
1550
+ const response = await request2.post("/wiki/rest/api/content", pageData, {
1551
+ headers: {
1552
+ "Content-Type": "application/json"
1489
1553
  }
1490
- );
1554
+ });
1491
1555
  logger20.info("create-confluence-page result", {
1492
1556
  page_id: response.data.id,
1493
1557
  title: response.data.title,
@@ -1499,45 +1563,45 @@ function createCreateConfluencePageTool(getAuth, getAuthHeader2, logger20) {
1499
1563
  id: response.data.id,
1500
1564
  title: response.data.title,
1501
1565
  space: space_key,
1502
- url: `${ATLASSIAN_SITE_URL}/wiki${response.data._links.webui}`,
1566
+ url: `${response.siteUrl}/wiki${response.data._links.webui}`,
1503
1567
  version: response.data.version?.number || 1
1504
1568
  }
1505
1569
  });
1506
1570
  } catch (error) {
1571
+ const description = request2.describeError(error);
1507
1572
  logger20.error("create-confluence-page error", {
1508
1573
  space_key,
1509
1574
  title,
1510
- error: error.response?.data?.message || error.message,
1511
- status: error.response?.status
1575
+ ...description.message !== void 0 ? { error: description.message } : {},
1576
+ ...description.status !== void 0 ? { status: description.status } : {}
1512
1577
  });
1513
1578
  return JSON.stringify({
1514
1579
  success: false,
1515
- error: error.response?.data?.message || error.message
1580
+ error: description.message
1516
1581
  });
1517
1582
  }
1518
1583
  }).build();
1519
1584
  }
1520
- function createUpdateConfluencePageTool(getAuth, getAuthHeader2, logger20) {
1521
- return toolBuilder().name("update-confluence-page").description("Update an existing Confluence page's content. Requires page ID, new title, and new content.").category(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(z.object({
1522
- page_id: z.string().describe("The ID of the page to update"),
1523
- title: z.string().describe("The new title for the page"),
1524
- content: z.string().describe("The new content in HTML format (Confluence storage format)")
1525
- })).implement(async ({ page_id, title, content }) => {
1585
+ function createUpdateConfluencePageTool(request2, logger20) {
1586
+ return toolBuilder().name("update-confluence-page").description(
1587
+ "Update an existing Confluence page's content. Requires page ID, new title, and new content."
1588
+ ).category(ToolCategory.WEB).tag("confluence").tag("update").tag("write").usageNotes(
1589
+ "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."
1590
+ ).requires(["get-confluence-page"]).schema(
1591
+ z.object({
1592
+ page_id: z.string().describe("The ID of the page to update"),
1593
+ title: z.string().describe("The new title for the page"),
1594
+ content: z.string().describe("The new content in HTML format (Confluence storage format)")
1595
+ })
1596
+ ).implement(async ({ page_id, title, content }) => {
1526
1597
  logger20.info("update-confluence-page called", { page_id, title });
1527
1598
  try {
1528
- const { ATLASSIAN_SITE_URL } = getAuth();
1529
- const getResponse = await axios11.get(
1530
- `${ATLASSIAN_SITE_URL}/wiki/rest/api/content/${page_id}`,
1531
- {
1532
- headers: {
1533
- Authorization: getAuthHeader2()
1534
- },
1535
- params: { expand: "version" }
1536
- }
1537
- );
1599
+ const getResponse = await request2.get(`/wiki/rest/api/content/${page_id}`, {
1600
+ params: { expand: "version" }
1601
+ });
1538
1602
  const currentVersion = getResponse.data.version.number;
1539
- const updateResponse = await axios11.put(
1540
- `${ATLASSIAN_SITE_URL}/wiki/rest/api/content/${page_id}`,
1603
+ const updateResponse = await request2.put(
1604
+ `/wiki/rest/api/content/${page_id}`,
1541
1605
  {
1542
1606
  type: "page",
1543
1607
  title,
@@ -1551,7 +1615,6 @@ function createUpdateConfluencePageTool(getAuth, getAuthHeader2, logger20) {
1551
1615
  },
1552
1616
  {
1553
1617
  headers: {
1554
- Authorization: getAuthHeader2(),
1555
1618
  "Content-Type": "application/json"
1556
1619
  }
1557
1620
  }
@@ -1567,49 +1630,49 @@ function createUpdateConfluencePageTool(getAuth, getAuthHeader2, logger20) {
1567
1630
  page: {
1568
1631
  id: updateResponse.data.id,
1569
1632
  title: updateResponse.data.title,
1570
- url: `${ATLASSIAN_SITE_URL}/wiki${updateResponse.data._links.webui}`,
1633
+ url: `${updateResponse.siteUrl}/wiki${updateResponse.data._links.webui}`,
1571
1634
  version: updateResponse.data.version.number,
1572
1635
  previousVersion: currentVersion
1573
1636
  }
1574
1637
  });
1575
1638
  } catch (error) {
1639
+ const description = request2.describeError(error);
1576
1640
  logger20.error("update-confluence-page error", {
1577
1641
  page_id,
1578
1642
  title,
1579
- error: error.response?.data?.message || error.message,
1580
- status: error.response?.status
1643
+ ...description.message !== void 0 ? { error: description.message } : {},
1644
+ ...description.status !== void 0 ? { status: description.status } : {}
1581
1645
  });
1582
1646
  return JSON.stringify({
1583
1647
  success: false,
1584
- error: error.response?.data?.message || error.message
1648
+ error: description.message
1585
1649
  });
1586
1650
  }
1587
1651
  }).build();
1588
1652
  }
1589
- function createArchiveConfluencePageTool(getAuth, getAuthHeader2, logger20) {
1590
- return 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(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(z.object({
1591
- page_id: z.string().describe("The ID of the page to archive"),
1592
- reason: z.string().optional().describe("Optional reason for archiving (for audit trail)")
1593
- })).implement(async ({ page_id, reason }) => {
1653
+ function createArchiveConfluencePageTool(request2, logger20) {
1654
+ return toolBuilder().name("archive-confluence-page").description(
1655
+ "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."
1656
+ ).category(ToolCategory.WEB).tag("confluence").tag("archive").tag("delete").usageNotes(
1657
+ "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."
1658
+ ).conflicts(["create-confluence-page"]).schema(
1659
+ z.object({
1660
+ page_id: z.string().describe("The ID of the page to archive"),
1661
+ reason: z.string().optional().describe("Optional reason for archiving (for audit trail)")
1662
+ })
1663
+ ).implement(async ({ page_id, reason }) => {
1594
1664
  logger20.info("archive-confluence-page called", {
1595
1665
  page_id,
1596
1666
  ...reason ? { reason } : {}
1597
1667
  });
1598
1668
  try {
1599
- const { ATLASSIAN_SITE_URL } = getAuth();
1600
- const getResponse = await axios11.get(
1601
- `${ATLASSIAN_SITE_URL}/wiki/rest/api/content/${page_id}`,
1602
- {
1603
- headers: {
1604
- Authorization: getAuthHeader2()
1605
- },
1606
- params: { expand: "version,body.storage,space" }
1607
- }
1608
- );
1669
+ const getResponse = await request2.get(`/wiki/rest/api/content/${page_id}`, {
1670
+ params: { expand: "version,body.storage,space" }
1671
+ });
1609
1672
  const currentVersion = getResponse.data.version.number;
1610
1673
  const pageData = getResponse.data;
1611
- await axios11.put(
1612
- `${ATLASSIAN_SITE_URL}/wiki/rest/api/content/${page_id}`,
1674
+ await request2.put(
1675
+ `/wiki/rest/api/content/${page_id}`,
1613
1676
  {
1614
1677
  version: { number: currentVersion + 1 },
1615
1678
  title: pageData.title,
@@ -1620,7 +1683,6 @@ function createArchiveConfluencePageTool(getAuth, getAuthHeader2, logger20) {
1620
1683
  },
1621
1684
  {
1622
1685
  headers: {
1623
- Authorization: getAuthHeader2(),
1624
1686
  "Content-Type": "application/json"
1625
1687
  }
1626
1688
  }
@@ -1643,14 +1705,15 @@ function createArchiveConfluencePageTool(getAuth, getAuthHeader2, logger20) {
1643
1705
  }
1644
1706
  });
1645
1707
  } catch (error) {
1708
+ const description = request2.describeError(error);
1646
1709
  logger20.error("archive-confluence-page error", {
1647
1710
  page_id,
1648
- error: error.response?.data?.message || error.message,
1649
- status: error.response?.status
1711
+ ...description.message !== void 0 ? { error: description.message } : {},
1712
+ ...description.status !== void 0 ? { status: description.status } : {}
1650
1713
  });
1651
1714
  return JSON.stringify({
1652
1715
  success: false,
1653
- error: error.response?.data?.message || error.message
1716
+ error: description.message
1654
1717
  });
1655
1718
  }
1656
1719
  }).build();
@@ -1659,13 +1722,14 @@ function createArchiveConfluencePageTool(getAuth, getAuthHeader2, logger20) {
1659
1722
  // src/web/confluence/index.ts
1660
1723
  var logLevel2 = process.env.LOG_LEVEL?.toLowerCase() || LogLevel.INFO;
1661
1724
  var logger2 = createLogger("[tools:confluence]", { level: logLevel2 });
1662
- var searchConfluence = createSearchConfluenceTool(getConfig, getAuthHeader, logger2);
1663
- var getConfluencePage = createGetConfluencePageTool(getConfig, getAuthHeader, logger2);
1664
- var listConfluenceSpaces = createListConfluenceSpacesTool(getConfig, getAuthHeader, logger2);
1665
- var getSpacePages = createGetSpacePagesTool(getConfig, getAuthHeader, logger2);
1666
- var createConfluencePage = createCreateConfluencePageTool(getConfig, getAuthHeader, logger2);
1667
- var updateConfluencePage = createUpdateConfluencePageTool(getConfig, getAuthHeader, logger2);
1668
- var archiveConfluencePage = createArchiveConfluencePageTool(getConfig, getAuthHeader, logger2);
1725
+ var request = createConfluenceRequest(getConfig);
1726
+ var searchConfluence = createSearchConfluenceTool(request, logger2);
1727
+ var getConfluencePage = createGetConfluencePageTool(request, logger2);
1728
+ var listConfluenceSpaces = createListConfluenceSpacesTool(request, logger2);
1729
+ var getSpacePages = createGetSpacePagesTool(request, logger2);
1730
+ var createConfluencePage = createCreateConfluencePageTool(request, logger2);
1731
+ var updateConfluencePage = createUpdateConfluencePageTool(request, logger2);
1732
+ var archiveConfluencePage = createArchiveConfluencePageTool(request, logger2);
1669
1733
  var confluenceTools = [
1670
1734
  // Read tools
1671
1735
  searchConfluence,
@@ -1678,22 +1742,17 @@ var confluenceTools = [
1678
1742
  archiveConfluencePage
1679
1743
  ];
1680
1744
  function createConfluenceTools(config = {}) {
1681
- const {
1682
- apiKey,
1683
- email,
1684
- siteUrl,
1685
- logLevel: customLogLevel
1686
- } = config;
1745
+ const { apiKey, email, siteUrl, logLevel: customLogLevel } = config;
1687
1746
  const getConfiguredAuth = createGetConfiguredAuth(apiKey, email, siteUrl);
1688
- const getConfiguredAuthHeader = createGetConfiguredAuthHeader(getConfiguredAuth);
1747
+ const request2 = createConfluenceRequest(getConfiguredAuth);
1689
1748
  const toolLogger = customLogLevel ? createLogger("tools:confluence", { level: customLogLevel }) : logger2;
1690
- const searchConfluence2 = createSearchConfluenceTool(getConfiguredAuth, getConfiguredAuthHeader, toolLogger);
1691
- const getConfluencePage2 = createGetConfluencePageTool(getConfiguredAuth, getConfiguredAuthHeader, toolLogger);
1692
- const listConfluenceSpaces2 = createListConfluenceSpacesTool(getConfiguredAuth, getConfiguredAuthHeader, toolLogger);
1693
- const getSpacePages2 = createGetSpacePagesTool(getConfiguredAuth, getConfiguredAuthHeader, toolLogger);
1694
- const createConfluencePage2 = createCreateConfluencePageTool(getConfiguredAuth, getConfiguredAuthHeader, toolLogger);
1695
- const updateConfluencePage2 = createUpdateConfluencePageTool(getConfiguredAuth, getConfiguredAuthHeader, toolLogger);
1696
- const archiveConfluencePage2 = createArchiveConfluencePageTool(getConfiguredAuth, getConfiguredAuthHeader, toolLogger);
1749
+ const searchConfluence2 = createSearchConfluenceTool(request2, toolLogger);
1750
+ const getConfluencePage2 = createGetConfluencePageTool(request2, toolLogger);
1751
+ const listConfluenceSpaces2 = createListConfluenceSpacesTool(request2, toolLogger);
1752
+ const getSpacePages2 = createGetSpacePagesTool(request2, toolLogger);
1753
+ const createConfluencePage2 = createCreateConfluencePageTool(request2, toolLogger);
1754
+ const updateConfluencePage2 = createUpdateConfluencePageTool(request2, toolLogger);
1755
+ const archiveConfluencePage2 = createArchiveConfluencePageTool(request2, toolLogger);
1697
1756
  return {
1698
1757
  searchConfluence: searchConfluence2,
1699
1758
  getConfluencePage: getConfluencePage2,
@@ -1884,14 +1943,14 @@ function createJsonStringifyTool(defaultIndent = 2, defaultPretty = false) {
1884
1943
  };
1885
1944
  }).build();
1886
1945
  }
1887
- function isRecord(value) {
1946
+ function isRecord2(value) {
1888
1947
  return typeof value === "object" && value !== null && !Array.isArray(value);
1889
1948
  }
1890
1949
  function getPathValue(current, part) {
1891
1950
  const arrayMatch = part.match(/^(\w+)\[(\d+)\]$/);
1892
1951
  if (arrayMatch) {
1893
1952
  const [, key, index] = arrayMatch;
1894
- if (!isRecord(current)) {
1953
+ if (!isRecord2(current)) {
1895
1954
  return void 0;
1896
1955
  }
1897
1956
  const candidate = current[key];
@@ -1900,7 +1959,7 @@ function getPathValue(current, part) {
1900
1959
  }
1901
1960
  return candidate[parseInt(index, 10)];
1902
1961
  }
1903
- if (!isRecord(current)) {
1962
+ if (!isRecord2(current)) {
1904
1963
  return void 0;
1905
1964
  }
1906
1965
  return current[part];
@@ -2556,7 +2615,7 @@ async function initializeFromEnv() {
2556
2615
  // src/data/neo4j/embeddings/utils.ts
2557
2616
  function withProviderErrorMetadata(error, message) {
2558
2617
  const wrappedError = new Error(message);
2559
- if (!isRecord2(error)) return wrappedError;
2618
+ if (!isRecord3(error)) return wrappedError;
2560
2619
  if (typeof error.code === "string") wrappedError.code = error.code;
2561
2620
  if (error.response !== void 0) wrappedError.response = error.response;
2562
2621
  return wrappedError;
@@ -2569,12 +2628,12 @@ var DEFAULT_RETRY_CONFIG2 = {
2569
2628
  // 10 seconds
2570
2629
  backoffMultiplier: 2
2571
2630
  };
2572
- function isRecord2(value) {
2631
+ function isRecord3(value) {
2573
2632
  return typeof value === "object" && value !== null;
2574
2633
  }
2575
2634
  function isRetryableError2(error) {
2576
- if (!isRecord2(error)) return false;
2577
- const response = isRecord2(error.response) ? error.response : void 0;
2635
+ if (!isRecord3(error)) return false;
2636
+ const response = isRecord3(error.response) ? error.response : void 0;
2578
2637
  const status = typeof response?.status === "number" ? response.status : void 0;
2579
2638
  const code = typeof error.code === "string" ? error.code : void 0;
2580
2639
  const message = typeof error.message === "string" ? error.message : void 0;
@@ -2700,7 +2759,7 @@ var OpenAIEmbeddingProvider = class {
2700
2759
  const modelToUse = model || this.defaultModel;
2701
2760
  return retryWithBackoff2(async () => {
2702
2761
  try {
2703
- const response = await axios11.post(
2762
+ const response = await axios5.post(
2704
2763
  `${this.baseURL}/embeddings`,
2705
2764
  {
2706
2765
  input: text,
@@ -2730,7 +2789,7 @@ var OpenAIEmbeddingProvider = class {
2730
2789
  };
2731
2790
  } catch (error) {
2732
2791
  let message;
2733
- const response = axios11.isAxiosError(error) ? error.response : void 0;
2792
+ const response = axios5.isAxiosError(error) ? error.response : void 0;
2734
2793
  if (response?.status === 401) {
2735
2794
  message = "Invalid OpenAI API key. Get your key at https://platform.openai.com/api-keys";
2736
2795
  } else if (response?.status === 429) {
@@ -2757,7 +2816,7 @@ var OpenAIEmbeddingProvider = class {
2757
2816
  const modelToUse = model || this.defaultModel;
2758
2817
  return retryWithBackoff2(async () => {
2759
2818
  try {
2760
- const response = await axios11.post(
2819
+ const response = await axios5.post(
2761
2820
  `${this.baseURL}/embeddings`,
2762
2821
  {
2763
2822
  input: texts,
@@ -2788,7 +2847,7 @@ var OpenAIEmbeddingProvider = class {
2788
2847
  };
2789
2848
  } catch (error) {
2790
2849
  let message;
2791
- const response = axios11.isAxiosError(error) ? error.response : void 0;
2850
+ const response = axios5.isAxiosError(error) ? error.response : void 0;
2792
2851
  if (response?.status === 401) {
2793
2852
  message = "Invalid OpenAI API key. Get your key at https://platform.openai.com/api-keys";
2794
2853
  } else if (response?.status === 429) {
@@ -2830,7 +2889,7 @@ var CohereEmbeddingProvider = class {
2830
2889
  const modelToUse = model || this.defaultModel;
2831
2890
  return retryWithBackoff2(async () => {
2832
2891
  try {
2833
- const response = await axios11.post(
2892
+ const response = await axios5.post(
2834
2893
  `${this.baseUrl}/embed`,
2835
2894
  {
2836
2895
  texts,
@@ -2858,7 +2917,7 @@ var CohereEmbeddingProvider = class {
2858
2917
  } : void 0
2859
2918
  };
2860
2919
  } catch (error) {
2861
- if (axios11.isAxiosError(error)) {
2920
+ if (axios5.isAxiosError(error)) {
2862
2921
  const status = error.response?.status;
2863
2922
  const providerMessage = error.response?.data?.message || error.message;
2864
2923
  let message;
@@ -2893,80 +2952,54 @@ var HuggingFaceEmbeddingProvider = class {
2893
2952
  return !!this.apiKey;
2894
2953
  }
2895
2954
  async generateEmbedding(text, model) {
2896
- const modelToUse = model || this.defaultModel;
2897
- return retryWithBackoff2(async () => {
2898
- try {
2899
- const response = await axios11.post(
2900
- `${this.baseUrl}/${modelToUse}`,
2901
- {
2902
- inputs: text,
2903
- options: {
2904
- wait_for_model: true
2905
- }
2906
- },
2907
- {
2908
- headers: {
2909
- "Authorization": `Bearer ${this.apiKey}`,
2910
- "Content-Type": "application/json"
2911
- }
2912
- }
2913
- );
2914
- const embedding = Array.isArray(response.data) ? response.data : response.data[0];
2915
- return {
2916
- embedding,
2917
- model: modelToUse,
2918
- dimensions: embedding.length
2919
- };
2920
- } catch (error) {
2921
- if (axios11.isAxiosError(error)) {
2922
- const status = error.response?.status;
2923
- const message = error.response?.data?.error || error.message;
2924
- let messageText;
2925
- if (status === 401) {
2926
- messageText = `HuggingFace API authentication failed. Please check your HUGGINGFACE_API_KEY. ${message}`;
2927
- } else if (status === 429) {
2928
- messageText = `HuggingFace API rate limit exceeded. ${message}`;
2929
- } else if (status === 400) {
2930
- messageText = `HuggingFace API request invalid: ${message}`;
2931
- } else if (status === 503) {
2932
- messageText = `HuggingFace model is loading. Please retry in a moment. ${message}`;
2933
- } else {
2934
- messageText = `HuggingFace API error (${status}): ${message}`;
2935
- }
2936
- throw withProviderErrorMetadata(error, messageText);
2937
- }
2938
- throw error;
2939
- }
2940
- });
2955
+ const { data, model: modelToUse } = await this.requestEmbeddings(
2956
+ text,
2957
+ model
2958
+ );
2959
+ const embedding = Array.isArray(data) ? data : data[0];
2960
+ return {
2961
+ embedding,
2962
+ model: modelToUse,
2963
+ dimensions: embedding.length
2964
+ };
2941
2965
  }
2942
2966
  async generateBatchEmbeddings(texts, model) {
2967
+ const { data: embeddings, model: modelToUse } = await this.requestEmbeddings(
2968
+ texts,
2969
+ model
2970
+ );
2971
+ const dimensions = embeddings[0]?.length || 0;
2972
+ return {
2973
+ embeddings,
2974
+ model: modelToUse,
2975
+ dimensions
2976
+ };
2977
+ }
2978
+ async requestEmbeddings(inputs, model) {
2943
2979
  const modelToUse = model || this.defaultModel;
2944
2980
  return retryWithBackoff2(async () => {
2945
2981
  try {
2946
- const response = await axios11.post(
2982
+ const response = await axios5.post(
2947
2983
  `${this.baseUrl}/${modelToUse}`,
2948
2984
  {
2949
- inputs: texts,
2985
+ inputs,
2950
2986
  options: {
2951
2987
  wait_for_model: true
2952
2988
  }
2953
2989
  },
2954
2990
  {
2955
2991
  headers: {
2956
- "Authorization": `Bearer ${this.apiKey}`,
2992
+ Authorization: `Bearer ${this.apiKey}`,
2957
2993
  "Content-Type": "application/json"
2958
2994
  }
2959
2995
  }
2960
2996
  );
2961
- const embeddings = response.data;
2962
- const dimensions = embeddings[0]?.length || 0;
2963
2997
  return {
2964
- embeddings,
2965
- model: modelToUse,
2966
- dimensions
2998
+ data: response.data,
2999
+ model: modelToUse
2967
3000
  };
2968
3001
  } catch (error) {
2969
- if (axios11.isAxiosError(error)) {
3002
+ if (axios5.isAxiosError(error)) {
2970
3003
  const status = error.response?.status;
2971
3004
  const message = error.response?.data?.error || error.message;
2972
3005
  let messageText;
@@ -3015,7 +3048,7 @@ var VoyageEmbeddingProvider = class {
3015
3048
  const modelToUse = model || this.defaultModel;
3016
3049
  return retryWithBackoff2(async () => {
3017
3050
  try {
3018
- const response = await axios11.post(
3051
+ const response = await axios5.post(
3019
3052
  `${this.baseUrl}/embeddings`,
3020
3053
  {
3021
3054
  input: texts,
@@ -3042,7 +3075,7 @@ var VoyageEmbeddingProvider = class {
3042
3075
  } : void 0
3043
3076
  };
3044
3077
  } catch (error) {
3045
- if (axios11.isAxiosError(error)) {
3078
+ if (axios5.isAxiosError(error)) {
3046
3079
  const status = error.response?.status;
3047
3080
  const message = error.response?.data?.message || error.message;
3048
3081
  let messageText;
@@ -3079,7 +3112,7 @@ var OllamaEmbeddingProvider = class {
3079
3112
  const modelToUse = model || this.defaultModel;
3080
3113
  return retryWithBackoff2(async () => {
3081
3114
  try {
3082
- const response = await axios11.post(
3115
+ const response = await axios5.post(
3083
3116
  `${this.baseUrl}/api/embeddings`,
3084
3117
  {
3085
3118
  model: modelToUse,
@@ -3098,7 +3131,7 @@ var OllamaEmbeddingProvider = class {
3098
3131
  dimensions: embedding.length
3099
3132
  };
3100
3133
  } catch (error) {
3101
- if (axios11.isAxiosError(error)) {
3134
+ if (axios5.isAxiosError(error)) {
3102
3135
  const status = error.response?.status;
3103
3136
  const message = error.response?.data?.error || error.message;
3104
3137
  let messageText;
@@ -3491,6 +3524,16 @@ function formatResults(records) {
3491
3524
  });
3492
3525
  }
3493
3526
 
3527
+ // src/data/neo4j/tools/session-owner.ts
3528
+ async function withNeo4jSession(database, operation) {
3529
+ const session = neo4jPool.getSession(database);
3530
+ try {
3531
+ return await operation(session);
3532
+ } finally {
3533
+ await session.close();
3534
+ }
3535
+ }
3536
+
3494
3537
  // src/data/neo4j/tools/neo4j-query.ts
3495
3538
  var logger4 = createLogger("agentforge:tools:neo4j:query");
3496
3539
  function createNeo4jQueryTool() {
@@ -3511,8 +3554,7 @@ function createNeo4jQueryTool() {
3511
3554
  ...input.database ? { database: input.database } : {}
3512
3555
  });
3513
3556
  try {
3514
- const session = neo4jPool.getSession(input.database);
3515
- try {
3557
+ return await withNeo4jSession(input.database, async (session) => {
3516
3558
  const result = await session.run(input.cypher, input.parameters || {});
3517
3559
  const formattedResults = formatResults(result.records);
3518
3560
  const duration = Date.now() - startTime;
@@ -3541,9 +3583,7 @@ function createNeo4jQueryTool() {
3541
3583
  }
3542
3584
  }
3543
3585
  };
3544
- } finally {
3545
- await session.close();
3546
- }
3586
+ });
3547
3587
  } catch (error) {
3548
3588
  const duration = Date.now() - startTime;
3549
3589
  const errorMessage = error instanceof Error ? error.message : "Failed to execute query";
@@ -3571,8 +3611,7 @@ function createNeo4jGetSchemaTool() {
3571
3611
  };
3572
3612
  }
3573
3613
  try {
3574
- const session = neo4jPool.getSession(input.database);
3575
- try {
3614
+ return await withNeo4jSession(input.database, async (session) => {
3576
3615
  const labelsResult = await session.run("CALL db.labels()");
3577
3616
  const nodeLabels = labelsResult.records.map((r) => r.get("label"));
3578
3617
  const relTypesResult = await session.run("CALL db.relationshipTypes()");
@@ -3612,9 +3651,7 @@ function createNeo4jGetSchemaTool() {
3612
3651
  totalIndexes: indexes.length
3613
3652
  }
3614
3653
  };
3615
- } finally {
3616
- await session.close();
3617
- }
3654
+ });
3618
3655
  } catch (error) {
3619
3656
  return {
3620
3657
  success: false,
@@ -3693,8 +3730,7 @@ function createNeo4jFindNodesTool() {
3693
3730
  }
3694
3731
  try {
3695
3732
  const safeLabel = validateLabel(input.label);
3696
- const session = neo4jPool.getSession(input.database);
3697
- try {
3733
+ return await withNeo4jSession(input.database, async (session) => {
3698
3734
  let cypher = `MATCH (n:${safeLabel})`;
3699
3735
  let parameters = {};
3700
3736
  if (input.properties && Object.keys(input.properties).length > 0) {
@@ -3716,9 +3752,7 @@ function createNeo4jFindNodesTool() {
3716
3752
  limit: input.limit
3717
3753
  }
3718
3754
  };
3719
- } finally {
3720
- await session.close();
3721
- }
3755
+ });
3722
3756
  } catch (error) {
3723
3757
  return {
3724
3758
  success: false,
@@ -3744,8 +3778,7 @@ function createNeo4jTraverseTool() {
3744
3778
  try {
3745
3779
  const safeDirection = validateDirection(input.direction || "outgoing");
3746
3780
  const safeRelType = input.relationshipType ? validateRelationshipType(input.relationshipType) : null;
3747
- const session = neo4jPool.getSession(input.database);
3748
- try {
3781
+ return await withNeo4jSession(input.database, async (session) => {
3749
3782
  let relPattern = "";
3750
3783
  if (safeDirection === "OUTGOING") {
3751
3784
  relPattern = safeRelType ? `-[r:${safeRelType}*1..${input.maxDepth}]->` : `-[r*1..${input.maxDepth}]->`;
@@ -3782,9 +3815,7 @@ function createNeo4jTraverseTool() {
3782
3815
  maxDepth: input.maxDepth
3783
3816
  }
3784
3817
  };
3785
- } finally {
3786
- await session.close();
3787
- }
3818
+ });
3788
3819
  } catch (error) {
3789
3820
  return {
3790
3821
  success: false,
@@ -3809,8 +3840,7 @@ function createNeo4jVectorSearchTool() {
3809
3840
  };
3810
3841
  }
3811
3842
  try {
3812
- const session = neo4jPool.getSession(input.database);
3813
- try {
3843
+ return await withNeo4jSession(input.database, async (session) => {
3814
3844
  const cypher = `
3815
3845
  CALL db.index.vector.queryNodes($indexName, $limit, $queryVector)
3816
3846
  YIELD node, score
@@ -3837,9 +3867,7 @@ function createNeo4jVectorSearchTool() {
3837
3867
  limit: input.limit
3838
3868
  }
3839
3869
  };
3840
- } finally {
3841
- await session.close();
3842
- }
3870
+ });
3843
3871
  } catch (error) {
3844
3872
  const errorMessage = error instanceof Error ? error.message : "Failed to perform vector search";
3845
3873
  let helpText = "";
@@ -3885,8 +3913,7 @@ function createNeo4jVectorSearchWithEmbeddingTool() {
3885
3913
  });
3886
3914
  try {
3887
3915
  const embeddingResult = await embeddingManager.generateEmbedding(input.queryText, input.model);
3888
- const session = neo4jPool.getSession(input.database);
3889
- try {
3916
+ return await withNeo4jSession(input.database, async (session) => {
3890
3917
  const cypher = `
3891
3918
  CALL db.index.vector.queryNodes($indexName, $limit, $queryVector)
3892
3919
  YIELD node, score
@@ -3928,9 +3955,7 @@ function createNeo4jVectorSearchWithEmbeddingTool() {
3928
3955
  usage: embeddingResult.usage
3929
3956
  }
3930
3957
  };
3931
- } finally {
3932
- await session.close();
3933
- }
3958
+ });
3934
3959
  } catch (error) {
3935
3960
  const duration = Date.now() - startTime;
3936
3961
  const errorMessage = error instanceof Error ? error.message : "Failed to perform vector search with embedding";
@@ -3986,8 +4011,7 @@ function createNeo4jCreateNodeWithEmbeddingTool() {
3986
4011
  const embeddingResult = await embeddingManager.generateEmbedding(textToEmbed, input.model);
3987
4012
  const safeLabel = validateLabel(input.label);
3988
4013
  const safeEmbeddingProp = validatePropertyKey(input.embeddingProperty || "embedding");
3989
- const session = neo4jPool.getSession(input.database);
3990
- try {
4014
+ return await withNeo4jSession(input.database, async (session) => {
3991
4015
  const allProperties = {
3992
4016
  ...input.properties
3993
4017
  };
@@ -4021,9 +4045,7 @@ function createNeo4jCreateNodeWithEmbeddingTool() {
4021
4045
  },
4022
4046
  message: `Created node with label '${input.label}' and ${embeddingResult.dimensions}-dimensional embedding`
4023
4047
  };
4024
- } finally {
4025
- await session.close();
4026
- }
4048
+ });
4027
4049
  } catch (error) {
4028
4050
  const errorMessage = error instanceof Error ? error.message : "Failed to create node with embedding";
4029
4051
  let helpText = "";
@@ -7111,15 +7133,6 @@ function formatTableName(table) {
7111
7133
  return table.schema ? `${table.schema}.${table.name}` : table.name;
7112
7134
  }
7113
7135
 
7114
- // src/data/relational/schema/type-mapper-normalization.ts
7115
- function normaliseDbType(raw) {
7116
- let type = raw.toLowerCase().trim();
7117
- type = type.replace(/\[\]$/, "");
7118
- type = type.replace(/\([\d,\s]+\)/, "");
7119
- type = type.replace(/\s+unsigned$/, "");
7120
- return type.trim();
7121
- }
7122
-
7123
7136
  // src/data/relational/schema/type-mapper-vendor-maps.ts
7124
7137
  var POSTGRES_TYPE_MAP = {
7125
7138
  smallint: "number",
@@ -7295,6 +7308,13 @@ function getVendorTypeMap(vendor) {
7295
7308
  function usesStringPrecisionFallback(vendor, normalised) {
7296
7309
  return (normalised === "bigint" || normalised === "int8" || normalised === "bigserial") && (vendor === "postgresql" || vendor === "mysql");
7297
7310
  }
7311
+ function normaliseDbType(raw) {
7312
+ let type = raw.toLowerCase().trim();
7313
+ type = type.replace(/\[\]$/, "");
7314
+ type = type.replace(/\([\d,\s]+\)/, "");
7315
+ type = type.replace(/\s+unsigned$/, "");
7316
+ return type.trim();
7317
+ }
7298
7318
  var logger14 = createLogger("agentforge:tools:data:relational:schema-diff");
7299
7319
  function diffSchemas(before, after) {
7300
7320
  const tableDiffs = [];
@@ -10310,49 +10330,57 @@ var fileSearchSchema = z.object({
10310
10330
  recursive: z.boolean().default(true).describe("Search in subdirectories"),
10311
10331
  caseSensitive: z.boolean().default(false).describe("Case-sensitive pattern matching")
10312
10332
  });
10333
+ async function* traverseDirectory(directory, recursive) {
10334
+ const entries = await promises.readdir(directory, { withFileTypes: true });
10335
+ for (const entry of entries) {
10336
+ const fullPath = path.join(directory, entry.name);
10337
+ const isDirectory = entry.isDirectory();
10338
+ yield {
10339
+ name: entry.name,
10340
+ fullPath,
10341
+ isFile: entry.isFile(),
10342
+ isDirectory
10343
+ };
10344
+ if (recursive && isDirectory) {
10345
+ yield* traverseDirectory(fullPath, true);
10346
+ }
10347
+ }
10348
+ }
10349
+
10350
+ // src/file/directory/tools/directory-list.ts
10313
10351
  function createDirectoryListTool(defaultRecursive = false, defaultIncludeDetails = false, policy = DEFAULT_FILE_SYSTEM_POLICY) {
10314
10352
  return toolBuilder().name("directory-list").description("List all files and directories in a directory. Can optionally include file details and filter by extension.").category(ToolCategory.FILE_SYSTEM).tags(["directory", "list", "files", "filesystem"]).schema(directoryListSchema).implementSafe(async (input) => {
10315
10353
  const safePath = await policy.resolvePath(input.path, "directory listing");
10316
10354
  const includeDetails = input.includeDetails ?? defaultIncludeDetails;
10317
- const listFiles = async (dir, recursive2) => {
10318
- const entries = await promises.readdir(dir, { withFileTypes: true });
10319
- const files2 = [];
10320
- for (const entry of entries) {
10321
- const fullPath = path.join(dir, entry.name);
10322
- const relativePath = path.relative(safePath, fullPath);
10323
- if (input.extension && !entry.name.endsWith(input.extension)) {
10324
- if (!entry.isDirectory() || !recursive2) {
10325
- continue;
10326
- }
10327
- }
10328
- if (includeDetails) {
10329
- const stats = await promises.lstat(fullPath);
10330
- files2.push({
10331
- name: entry.name,
10332
- path: relativePath,
10333
- fullPath,
10334
- isFile: entry.isFile(),
10335
- isDirectory: entry.isDirectory(),
10336
- size: stats.size,
10337
- modified: stats.mtime.toISOString()
10338
- });
10339
- } else {
10340
- files2.push({
10341
- name: entry.name,
10342
- path: relativePath,
10343
- isFile: entry.isFile(),
10344
- isDirectory: entry.isDirectory()
10345
- });
10346
- }
10347
- if (recursive2 && entry.isDirectory()) {
10348
- const subFiles = await listFiles(fullPath, true);
10349
- files2.push(...subFiles);
10355
+ const recursive = input.recursive ?? defaultRecursive;
10356
+ const files = [];
10357
+ for await (const entry of traverseDirectory(safePath, recursive)) {
10358
+ const relativePath = path.relative(safePath, entry.fullPath);
10359
+ if (input.extension && !entry.name.endsWith(input.extension)) {
10360
+ if (!entry.isDirectory || !recursive) {
10361
+ continue;
10350
10362
  }
10351
10363
  }
10352
- return files2;
10353
- };
10354
- const recursive = input.recursive ?? defaultRecursive;
10355
- const files = await listFiles(safePath, recursive);
10364
+ if (includeDetails) {
10365
+ const stats = await promises.lstat(entry.fullPath);
10366
+ files.push({
10367
+ name: entry.name,
10368
+ path: relativePath,
10369
+ fullPath: entry.fullPath,
10370
+ isFile: entry.isFile,
10371
+ isDirectory: entry.isDirectory,
10372
+ size: stats.size,
10373
+ modified: stats.mtime.toISOString()
10374
+ });
10375
+ } else {
10376
+ files.push({
10377
+ name: entry.name,
10378
+ path: relativePath,
10379
+ isFile: entry.isFile,
10380
+ isDirectory: entry.isDirectory
10381
+ });
10382
+ }
10383
+ }
10356
10384
  const result = {
10357
10385
  path: input.path,
10358
10386
  files,
@@ -10388,24 +10416,14 @@ function createFileSearchTool(defaultRecursive = true, defaultCaseSensitive = fa
10388
10416
  const recursive = input.recursive ?? defaultRecursive;
10389
10417
  const caseSensitive = input.caseSensitive ?? defaultCaseSensitive;
10390
10418
  const safeDirectory = await policy.resolvePath(input.directory, "file search");
10391
- const searchFiles = async (dir) => {
10392
- const entries = await promises.readdir(dir, { withFileTypes: true });
10393
- const matches2 = [];
10394
- const regexPattern = input.pattern.replace(/\./g, "\\.").replace(/\*/g, ".*");
10395
- const regex = new RegExp(`^${regexPattern}$`, caseSensitive ? "" : "i");
10396
- for (const entry of entries) {
10397
- const fullPath = path.join(dir, entry.name);
10398
- if (entry.isFile() && regex.test(entry.name)) {
10399
- matches2.push(fullPath);
10400
- }
10401
- if (recursive && entry.isDirectory()) {
10402
- const subMatches = await searchFiles(fullPath);
10403
- matches2.push(...subMatches);
10404
- }
10419
+ const regexPattern = input.pattern.replace(/\./g, "\\.").replace(/\*/g, ".*");
10420
+ const regex = new RegExp(`^${regexPattern}$`, caseSensitive ? "" : "i");
10421
+ const matches = [];
10422
+ for await (const entry of traverseDirectory(safeDirectory, recursive)) {
10423
+ if (entry.isFile && regex.test(entry.name)) {
10424
+ matches.push(entry.fullPath);
10405
10425
  }
10406
- return matches2;
10407
- };
10408
- const matches = await searchFiles(safeDirectory);
10426
+ }
10409
10427
  return {
10410
10428
  directory: input.directory,
10411
10429
  pattern: input.pattern,
@@ -11367,11 +11385,11 @@ var AskHumanInputSchema = z.object({
11367
11385
  });
11368
11386
  var logLevel3 = process.env.LOG_LEVEL?.toLowerCase() || LogLevel.INFO;
11369
11387
  var logger19 = createLogger("agentforge:tools:agent:ask-human", { level: logLevel3 });
11370
- function isRecord3(value) {
11388
+ function isRecord4(value) {
11371
11389
  return typeof value === "object" && value !== null;
11372
11390
  }
11373
11391
  function resolveInterrupt(module) {
11374
- if (!isRecord3(module)) {
11392
+ if (!isRecord4(module)) {
11375
11393
  return void 0;
11376
11394
  }
11377
11395
  const candidate = module.interrupt;