@agentforge/tools 0.18.3 → 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 +360 -305
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +118 -322
- package/dist/index.d.ts +118 -322
- package/dist/index.js +359 -304
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
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
|
|
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
|
|
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}`);
|
|
@@ -710,26 +710,23 @@ var DuckDuckGoProvider = class {
|
|
|
710
710
|
* @param timeout - Request timeout in milliseconds (default: 30000)
|
|
711
711
|
*/
|
|
712
712
|
async search(query, maxResults, timeout = DEFAULT_TIMEOUT) {
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
const response = await
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
timeout
|
|
726
|
-
}
|
|
727
|
-
);
|
|
713
|
+
try {
|
|
714
|
+
return await retryWithBackoff(async () => {
|
|
715
|
+
const response = await axios5.get("https://api.duckduckgo.com/", {
|
|
716
|
+
params: {
|
|
717
|
+
q: query,
|
|
718
|
+
format: "json"
|
|
719
|
+
},
|
|
720
|
+
headers: {
|
|
721
|
+
"User-Agent": "Mozilla/5.0 (compatible; AgentForge/1.0; +https://github.com/agentforge)"
|
|
722
|
+
},
|
|
723
|
+
timeout
|
|
724
|
+
});
|
|
728
725
|
return this.normalizeResults(response.data, maxResults);
|
|
729
|
-
}
|
|
730
|
-
|
|
731
|
-
}
|
|
732
|
-
}
|
|
726
|
+
});
|
|
727
|
+
} catch (error) {
|
|
728
|
+
throw new Error(`DuckDuckGo search failed: ${error.message}`);
|
|
729
|
+
}
|
|
733
730
|
}
|
|
734
731
|
/**
|
|
735
732
|
* Normalize DuckDuckGo response to SearchResult[]
|
|
@@ -814,9 +811,9 @@ var SerperProvider = class {
|
|
|
814
811
|
"Serper API key not found. Set SERPER_API_KEY environment variable. Get your key at https://serper.dev"
|
|
815
812
|
);
|
|
816
813
|
}
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
const response = await
|
|
814
|
+
try {
|
|
815
|
+
return await retryWithBackoff(async () => {
|
|
816
|
+
const response = await axios5.post(
|
|
820
817
|
"https://google.serper.dev/search",
|
|
821
818
|
{
|
|
822
819
|
q: query,
|
|
@@ -831,20 +828,18 @@ var SerperProvider = class {
|
|
|
831
828
|
}
|
|
832
829
|
);
|
|
833
830
|
return this.normalizeResults(response.data, maxResults);
|
|
834
|
-
}
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
);
|
|
839
|
-
}
|
|
840
|
-
if (error.response?.status === 429) {
|
|
841
|
-
throw new Error(
|
|
842
|
-
"Serper API rate limit exceeded. Please try again later or upgrade your plan at https://serper.dev"
|
|
843
|
-
);
|
|
844
|
-
}
|
|
845
|
-
throw new Error(`Serper search failed: ${error.message}`);
|
|
831
|
+
});
|
|
832
|
+
} catch (error) {
|
|
833
|
+
if (error.response?.status === 401) {
|
|
834
|
+
throw new Error("Invalid Serper API key. Get your key at https://serper.dev");
|
|
846
835
|
}
|
|
847
|
-
|
|
836
|
+
if (error.response?.status === 429) {
|
|
837
|
+
throw new Error(
|
|
838
|
+
"Serper API rate limit exceeded. Please try again later or upgrade your plan at https://serper.dev"
|
|
839
|
+
);
|
|
840
|
+
}
|
|
841
|
+
throw new Error(`Serper search failed: ${error.message}`);
|
|
842
|
+
}
|
|
848
843
|
}
|
|
849
844
|
/**
|
|
850
845
|
* Normalize Serper response to SearchResult[]
|
|
@@ -1221,38 +1216,91 @@ function createGetConfiguredAuth(apiKey, email, siteUrl) {
|
|
|
1221
1216
|
return { ATLASSIAN_API_KEY, ATLASSIAN_EMAIL, ATLASSIAN_SITE_URL };
|
|
1222
1217
|
};
|
|
1223
1218
|
}
|
|
1224
|
-
function createGetConfiguredAuthHeader(getConfiguredAuth) {
|
|
1225
|
-
return function getConfiguredAuthHeader() {
|
|
1226
|
-
const { ATLASSIAN_API_KEY, ATLASSIAN_EMAIL } = getConfiguredAuth();
|
|
1227
|
-
const auth2 = Buffer.from(`${ATLASSIAN_EMAIL}:${ATLASSIAN_API_KEY}`).toString("base64");
|
|
1228
|
-
return `Basic ${auth2}`;
|
|
1229
|
-
};
|
|
1230
|
-
}
|
|
1231
1219
|
function getConfig() {
|
|
1232
1220
|
const ATLASSIAN_API_KEY = process.env.ATLASSIAN_API_KEY || "";
|
|
1233
1221
|
const ATLASSIAN_EMAIL = process.env.ATLASSIAN_EMAIL || "";
|
|
1234
1222
|
const ATLASSIAN_SITE_URL = (process.env.ATLASSIAN_SITE_URL || "").replace(/\/$/, "");
|
|
1235
1223
|
if (!ATLASSIAN_API_KEY || !ATLASSIAN_EMAIL || !ATLASSIAN_SITE_URL) {
|
|
1236
|
-
throw new Error(
|
|
1224
|
+
throw new Error(
|
|
1225
|
+
"Confluence credentials not configured. Set ATLASSIAN_API_KEY, ATLASSIAN_EMAIL, and ATLASSIAN_SITE_URL in .env"
|
|
1226
|
+
);
|
|
1237
1227
|
}
|
|
1238
1228
|
return { ATLASSIAN_API_KEY, ATLASSIAN_EMAIL, ATLASSIAN_SITE_URL };
|
|
1239
1229
|
}
|
|
1240
|
-
function
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1230
|
+
function isRecord(value) {
|
|
1231
|
+
return typeof value === "object" && value !== null;
|
|
1232
|
+
}
|
|
1233
|
+
function describeError(error) {
|
|
1234
|
+
const errorRecord = isRecord(error) ? error : void 0;
|
|
1235
|
+
const response = isRecord(errorRecord?.response) ? errorRecord.response : void 0;
|
|
1236
|
+
const responseData = isRecord(response?.data) ? response.data : void 0;
|
|
1237
|
+
const providerMessage = typeof responseData?.message === "string" ? responseData.message : void 0;
|
|
1238
|
+
const errorMessage = typeof errorRecord?.message === "string" ? errorRecord.message : void 0;
|
|
1239
|
+
return {
|
|
1240
|
+
message: providerMessage || errorMessage,
|
|
1241
|
+
status: typeof response?.status === "number" ? response.status : void 0
|
|
1242
|
+
};
|
|
1244
1243
|
}
|
|
1245
|
-
function
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1244
|
+
function authenticatedConfig(auth2, config = {}) {
|
|
1245
|
+
const credentials = Buffer.from(`${auth2.ATLASSIAN_EMAIL}:${auth2.ATLASSIAN_API_KEY}`).toString(
|
|
1246
|
+
"base64"
|
|
1247
|
+
);
|
|
1248
|
+
return {
|
|
1249
|
+
...config,
|
|
1250
|
+
headers: {
|
|
1251
|
+
...config.headers,
|
|
1252
|
+
Authorization: `Basic ${credentials}`
|
|
1253
|
+
}
|
|
1254
|
+
};
|
|
1255
|
+
}
|
|
1256
|
+
function createConfluenceRequest(getAuth) {
|
|
1257
|
+
return {
|
|
1258
|
+
async get(path13, config = {}) {
|
|
1259
|
+
const auth2 = getAuth();
|
|
1260
|
+
const response = await axios5.get(
|
|
1261
|
+
`${auth2.ATLASSIAN_SITE_URL}${path13}`,
|
|
1262
|
+
authenticatedConfig(auth2, config)
|
|
1263
|
+
);
|
|
1264
|
+
return { data: response.data, siteUrl: auth2.ATLASSIAN_SITE_URL };
|
|
1265
|
+
},
|
|
1266
|
+
async post(path13, data, config = {}) {
|
|
1267
|
+
const auth2 = getAuth();
|
|
1268
|
+
const response = await axios5.post(
|
|
1269
|
+
`${auth2.ATLASSIAN_SITE_URL}${path13}`,
|
|
1270
|
+
data,
|
|
1271
|
+
authenticatedConfig(auth2, config)
|
|
1272
|
+
);
|
|
1273
|
+
return { data: response.data, siteUrl: auth2.ATLASSIAN_SITE_URL };
|
|
1274
|
+
},
|
|
1275
|
+
async put(path13, data, config = {}) {
|
|
1276
|
+
const auth2 = getAuth();
|
|
1277
|
+
const response = await axios5.put(
|
|
1278
|
+
`${auth2.ATLASSIAN_SITE_URL}${path13}`,
|
|
1279
|
+
data,
|
|
1280
|
+
authenticatedConfig(auth2, config)
|
|
1281
|
+
);
|
|
1282
|
+
return { data: response.data, siteUrl: auth2.ATLASSIAN_SITE_URL };
|
|
1283
|
+
},
|
|
1284
|
+
describeError
|
|
1285
|
+
};
|
|
1286
|
+
}
|
|
1287
|
+
function createSearchConfluenceTool(request2, logger20) {
|
|
1288
|
+
return toolBuilder().name("search-confluence").description(
|
|
1289
|
+
"Search for pages in Confluence using keywords or CQL (Confluence Query Language). Returns matching pages with titles, IDs, and excerpts."
|
|
1290
|
+
).category(ToolCategory.WEB).tag("confluence").tag("search").tag("knowledge-base").usageNotes(
|
|
1291
|
+
"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."
|
|
1292
|
+
).suggests(["get-confluence-page"]).schema(
|
|
1293
|
+
z.object({
|
|
1294
|
+
query: z.string().describe(
|
|
1295
|
+
"Search query or CQL expression (e.g., 'payment processing' or 'space=BL3 AND title~payment')"
|
|
1296
|
+
),
|
|
1297
|
+
limit: z.number().optional().describe("Maximum number of results to return (default: 10, max: 25)")
|
|
1298
|
+
})
|
|
1299
|
+
).implement(async ({ query, limit = 10 }) => {
|
|
1250
1300
|
logger20.info("search-confluence called", { query, limit });
|
|
1251
1301
|
try {
|
|
1252
|
-
const
|
|
1253
|
-
const response = await axios11.get(`${ATLASSIAN_SITE_URL}/wiki/rest/api/content/search`, {
|
|
1302
|
+
const response = await request2.get("/wiki/rest/api/content/search", {
|
|
1254
1303
|
headers: {
|
|
1255
|
-
Authorization: getAuthHeader2(),
|
|
1256
1304
|
Accept: "application/json"
|
|
1257
1305
|
},
|
|
1258
1306
|
params: {
|
|
@@ -1261,22 +1309,24 @@ function createSearchConfluenceTool(getAuth, getAuthHeader2, logger20) {
|
|
|
1261
1309
|
expand: "space,version"
|
|
1262
1310
|
}
|
|
1263
1311
|
});
|
|
1264
|
-
const { ATLASSIAN_SITE_URL: siteUrl } = getAuth();
|
|
1265
1312
|
const results = response.data.results.map((page) => ({
|
|
1266
1313
|
id: page.id,
|
|
1267
1314
|
title: page.title,
|
|
1268
1315
|
type: page.type,
|
|
1269
1316
|
space: page.space?.name || "Unknown",
|
|
1270
1317
|
spaceKey: page.space?.key || "",
|
|
1271
|
-
url: `${siteUrl}/wiki${page._links.webui}`,
|
|
1318
|
+
url: `${response.siteUrl}/wiki${page._links.webui}`,
|
|
1272
1319
|
lastModified: page.version?.when || ""
|
|
1273
1320
|
}));
|
|
1274
1321
|
if (results.length === 0) {
|
|
1275
|
-
logger20.warn(
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1322
|
+
logger20.warn(
|
|
1323
|
+
"search-confluence returned NO RESULTS - this is a valid outcome, agent should not retry",
|
|
1324
|
+
{
|
|
1325
|
+
query,
|
|
1326
|
+
limit,
|
|
1327
|
+
totalSize: response.data.totalSize
|
|
1328
|
+
}
|
|
1329
|
+
);
|
|
1280
1330
|
} else {
|
|
1281
1331
|
logger20.info("search-confluence result", {
|
|
1282
1332
|
query,
|
|
@@ -1293,28 +1343,33 @@ function createSearchConfluenceTool(getAuth, getAuthHeader2, logger20) {
|
|
|
1293
1343
|
results
|
|
1294
1344
|
});
|
|
1295
1345
|
} catch (error) {
|
|
1346
|
+
const description = request2.describeError(error);
|
|
1296
1347
|
logger20.error("search-confluence error", {
|
|
1297
1348
|
query,
|
|
1298
|
-
error:
|
|
1299
|
-
status:
|
|
1349
|
+
...description.message !== void 0 ? { error: description.message } : {},
|
|
1350
|
+
...description.status !== void 0 ? { status: description.status } : {}
|
|
1300
1351
|
});
|
|
1301
1352
|
return JSON.stringify({
|
|
1302
1353
|
success: false,
|
|
1303
|
-
error:
|
|
1354
|
+
error: description.message
|
|
1304
1355
|
});
|
|
1305
1356
|
}
|
|
1306
1357
|
}).build();
|
|
1307
1358
|
}
|
|
1308
|
-
function createGetConfluencePageTool(
|
|
1309
|
-
return toolBuilder().name("get-confluence-page").description(
|
|
1310
|
-
|
|
1311
|
-
|
|
1359
|
+
function createGetConfluencePageTool(request2, logger20) {
|
|
1360
|
+
return toolBuilder().name("get-confluence-page").description(
|
|
1361
|
+
"Get the full content of a specific Confluence page by its ID. Returns the page title, content (in storage format), space, and metadata."
|
|
1362
|
+
).category(ToolCategory.WEB).tag("confluence").tag("page").tag("content").usageNotes(
|
|
1363
|
+
"Use this after search-confluence to retrieve the full content of a specific page. The page ID can be found in search results."
|
|
1364
|
+
).requires(["search-confluence"]).schema(
|
|
1365
|
+
z.object({
|
|
1366
|
+
page_id: z.string().describe("The Confluence page ID (from search results)")
|
|
1367
|
+
})
|
|
1368
|
+
).implement(async ({ page_id }) => {
|
|
1312
1369
|
logger20.info("get-confluence-page called", { page_id });
|
|
1313
1370
|
try {
|
|
1314
|
-
const
|
|
1315
|
-
const response = await axios11.get(`${ATLASSIAN_SITE_URL}/wiki/rest/api/content/${page_id}`, {
|
|
1371
|
+
const response = await request2.get(`/wiki/rest/api/content/${page_id}`, {
|
|
1316
1372
|
headers: {
|
|
1317
|
-
Authorization: getAuthHeader2(),
|
|
1318
1373
|
Accept: "application/json"
|
|
1319
1374
|
},
|
|
1320
1375
|
params: {
|
|
@@ -1337,35 +1392,40 @@ function createGetConfluencePageTool(getAuth, getAuthHeader2, logger20) {
|
|
|
1337
1392
|
space: page.space?.name || "Unknown",
|
|
1338
1393
|
spaceKey: page.space?.key || "",
|
|
1339
1394
|
content: page.body?.storage?.value || "",
|
|
1340
|
-
url: `${
|
|
1395
|
+
url: `${response.siteUrl}/wiki${page._links.webui}`,
|
|
1341
1396
|
created: page.history?.createdDate || "",
|
|
1342
1397
|
lastModified: page.version?.when || "",
|
|
1343
1398
|
version: page.version?.number || 1
|
|
1344
1399
|
}
|
|
1345
1400
|
});
|
|
1346
1401
|
} catch (error) {
|
|
1402
|
+
const description = request2.describeError(error);
|
|
1347
1403
|
logger20.error("get-confluence-page error", {
|
|
1348
1404
|
page_id,
|
|
1349
|
-
error:
|
|
1350
|
-
status:
|
|
1405
|
+
...description.message !== void 0 ? { error: description.message } : {},
|
|
1406
|
+
...description.status !== void 0 ? { status: description.status } : {}
|
|
1351
1407
|
});
|
|
1352
1408
|
return JSON.stringify({
|
|
1353
1409
|
success: false,
|
|
1354
|
-
error:
|
|
1410
|
+
error: description.message
|
|
1355
1411
|
});
|
|
1356
1412
|
}
|
|
1357
1413
|
}).build();
|
|
1358
1414
|
}
|
|
1359
|
-
function createListConfluenceSpacesTool(
|
|
1360
|
-
return toolBuilder().name("list-confluence-spaces").description(
|
|
1361
|
-
|
|
1362
|
-
|
|
1415
|
+
function createListConfluenceSpacesTool(request2, logger20) {
|
|
1416
|
+
return toolBuilder().name("list-confluence-spaces").description(
|
|
1417
|
+
"List all available Confluence spaces. Returns space names, keys, types, and descriptions to help identify where to search for information."
|
|
1418
|
+
).category(ToolCategory.WEB).tag("confluence").tag("spaces").tag("list").usageNotes(
|
|
1419
|
+
"Use this first to discover available spaces before searching. Helps narrow down searches to specific areas (e.g., 'AI', 'BL3', 'Finance')."
|
|
1420
|
+
).follows(["search-confluence"]).schema(
|
|
1421
|
+
z.object({
|
|
1422
|
+
limit: z.number().optional().describe("Maximum number of spaces to return (default: 25)")
|
|
1423
|
+
})
|
|
1424
|
+
).implement(async ({ limit = 25 }) => {
|
|
1363
1425
|
logger20.info("list-confluence-spaces called", { limit });
|
|
1364
1426
|
try {
|
|
1365
|
-
const
|
|
1366
|
-
const response = await axios11.get(`${ATLASSIAN_SITE_URL}/wiki/rest/api/space`, {
|
|
1427
|
+
const response = await request2.get("/wiki/rest/api/space", {
|
|
1367
1428
|
headers: {
|
|
1368
|
-
Authorization: getAuthHeader2(),
|
|
1369
1429
|
Accept: "application/json"
|
|
1370
1430
|
},
|
|
1371
1431
|
params: {
|
|
@@ -1377,7 +1437,7 @@ function createListConfluenceSpacesTool(getAuth, getAuthHeader2, logger20) {
|
|
|
1377
1437
|
name: space.name,
|
|
1378
1438
|
type: space.type,
|
|
1379
1439
|
description: space.description?.plain?.value || "",
|
|
1380
|
-
url: `${
|
|
1440
|
+
url: `${response.siteUrl}/wiki${space._links.webui}`
|
|
1381
1441
|
}));
|
|
1382
1442
|
logger20.info("list-confluence-spaces result", {
|
|
1383
1443
|
spaceCount: spaces.length,
|
|
@@ -1390,28 +1450,33 @@ function createListConfluenceSpacesTool(getAuth, getAuthHeader2, logger20) {
|
|
|
1390
1450
|
spaces
|
|
1391
1451
|
});
|
|
1392
1452
|
} catch (error) {
|
|
1453
|
+
const description = request2.describeError(error);
|
|
1393
1454
|
logger20.error("list-confluence-spaces error", {
|
|
1394
|
-
error:
|
|
1395
|
-
status:
|
|
1455
|
+
...description.message !== void 0 ? { error: description.message } : {},
|
|
1456
|
+
...description.status !== void 0 ? { status: description.status } : {}
|
|
1396
1457
|
});
|
|
1397
1458
|
return JSON.stringify({
|
|
1398
1459
|
success: false,
|
|
1399
|
-
error:
|
|
1460
|
+
error: description.message
|
|
1400
1461
|
});
|
|
1401
1462
|
}
|
|
1402
1463
|
}).build();
|
|
1403
1464
|
}
|
|
1404
|
-
function createGetSpacePagesTool(
|
|
1405
|
-
return toolBuilder().name("get-space-pages").description(
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1465
|
+
function createGetSpacePagesTool(request2, logger20) {
|
|
1466
|
+
return toolBuilder().name("get-space-pages").description(
|
|
1467
|
+
"Get all pages from a specific Confluence space by space key. Useful for browsing content in a particular area."
|
|
1468
|
+
).category(ToolCategory.WEB).tag("confluence").tag("space").tag("pages").usageNotes(
|
|
1469
|
+
"Use this to explore all pages in a specific space. Get the space key from list-confluence-spaces first."
|
|
1470
|
+
).requires(["list-confluence-spaces"]).schema(
|
|
1471
|
+
z.object({
|
|
1472
|
+
space_key: z.string().describe("The space key (e.g., 'AI', 'BL3', 'FIN')"),
|
|
1473
|
+
limit: z.number().optional().describe("Maximum number of pages to return (default: 25)")
|
|
1474
|
+
})
|
|
1475
|
+
).implement(async ({ space_key, limit = 25 }) => {
|
|
1409
1476
|
logger20.info("get-space-pages called", { space_key, limit });
|
|
1410
1477
|
try {
|
|
1411
|
-
const
|
|
1412
|
-
const response = await axios11.get(`${ATLASSIAN_SITE_URL}/wiki/rest/api/content`, {
|
|
1478
|
+
const response = await request2.get("/wiki/rest/api/content", {
|
|
1413
1479
|
headers: {
|
|
1414
|
-
Authorization: getAuthHeader2(),
|
|
1415
1480
|
Accept: "application/json"
|
|
1416
1481
|
},
|
|
1417
1482
|
params: {
|
|
@@ -1424,14 +1489,17 @@ function createGetSpacePagesTool(getAuth, getAuthHeader2, logger20) {
|
|
|
1424
1489
|
const pages = response.data.results.map((page) => ({
|
|
1425
1490
|
id: page.id,
|
|
1426
1491
|
title: page.title,
|
|
1427
|
-
url: `${
|
|
1492
|
+
url: `${response.siteUrl}/wiki${page._links.webui}`,
|
|
1428
1493
|
lastModified: page.version?.when || ""
|
|
1429
1494
|
}));
|
|
1430
1495
|
if (pages.length === 0) {
|
|
1431
|
-
logger20.warn(
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1496
|
+
logger20.warn(
|
|
1497
|
+
"get-space-pages returned NO PAGES - this is a valid outcome, agent should not retry",
|
|
1498
|
+
{
|
|
1499
|
+
space_key,
|
|
1500
|
+
limit
|
|
1501
|
+
}
|
|
1502
|
+
);
|
|
1435
1503
|
} else {
|
|
1436
1504
|
logger20.info("get-space-pages result", {
|
|
1437
1505
|
space_key,
|
|
@@ -1447,28 +1515,38 @@ function createGetSpacePagesTool(getAuth, getAuthHeader2, logger20) {
|
|
|
1447
1515
|
pages
|
|
1448
1516
|
});
|
|
1449
1517
|
} catch (error) {
|
|
1518
|
+
const description = request2.describeError(error);
|
|
1450
1519
|
logger20.error("get-space-pages error", {
|
|
1451
1520
|
space_key,
|
|
1452
|
-
error:
|
|
1453
|
-
status:
|
|
1521
|
+
...description.message !== void 0 ? { error: description.message } : {},
|
|
1522
|
+
...description.status !== void 0 ? { status: description.status } : {}
|
|
1454
1523
|
});
|
|
1455
1524
|
return JSON.stringify({
|
|
1456
1525
|
success: false,
|
|
1457
|
-
error:
|
|
1526
|
+
error: description.message
|
|
1458
1527
|
});
|
|
1459
1528
|
}
|
|
1460
1529
|
}).build();
|
|
1461
1530
|
}
|
|
1462
|
-
function createCreateConfluencePageTool(
|
|
1463
|
-
return toolBuilder().name("create-confluence-page").description(
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1531
|
+
function createCreateConfluencePageTool(request2, logger20) {
|
|
1532
|
+
return toolBuilder().name("create-confluence-page").description(
|
|
1533
|
+
"Create a new page in a Confluence space. Requires space key, page title, and content (in HTML storage format)."
|
|
1534
|
+
).category(ToolCategory.WEB).tag("confluence").tag("create").tag("write").usageNotes(
|
|
1535
|
+
"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."
|
|
1536
|
+
).requires(["list-confluence-spaces"]).schema(
|
|
1537
|
+
z.object({
|
|
1538
|
+
space_key: z.string().describe("The space key where the page will be created (e.g., 'AI', 'BL3')"),
|
|
1539
|
+
title: z.string().describe("The title of the new page"),
|
|
1540
|
+
content: z.string().describe("The page content in HTML format (Confluence storage format)"),
|
|
1541
|
+
parent_page_id: z.string().optional().describe("Optional parent page ID to create this as a child page")
|
|
1542
|
+
})
|
|
1543
|
+
).implement(async ({ space_key, title, content, parent_page_id }) => {
|
|
1544
|
+
logger20.info("create-confluence-page called", {
|
|
1545
|
+
space_key,
|
|
1546
|
+
title,
|
|
1547
|
+
hasParent: !!parent_page_id
|
|
1548
|
+
});
|
|
1470
1549
|
try {
|
|
1471
|
-
const { ATLASSIAN_SITE_URL } = getAuth();
|
|
1472
1550
|
const pageData = {
|
|
1473
1551
|
type: "page",
|
|
1474
1552
|
title,
|
|
@@ -1483,16 +1561,11 @@ function createCreateConfluencePageTool(getAuth, getAuthHeader2, logger20) {
|
|
|
1483
1561
|
if (parent_page_id) {
|
|
1484
1562
|
pageData.ancestors = [{ id: parent_page_id }];
|
|
1485
1563
|
}
|
|
1486
|
-
const response = await
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
{
|
|
1490
|
-
headers: {
|
|
1491
|
-
Authorization: getAuthHeader2(),
|
|
1492
|
-
"Content-Type": "application/json"
|
|
1493
|
-
}
|
|
1564
|
+
const response = await request2.post("/wiki/rest/api/content", pageData, {
|
|
1565
|
+
headers: {
|
|
1566
|
+
"Content-Type": "application/json"
|
|
1494
1567
|
}
|
|
1495
|
-
);
|
|
1568
|
+
});
|
|
1496
1569
|
logger20.info("create-confluence-page result", {
|
|
1497
1570
|
page_id: response.data.id,
|
|
1498
1571
|
title: response.data.title,
|
|
@@ -1504,45 +1577,45 @@ function createCreateConfluencePageTool(getAuth, getAuthHeader2, logger20) {
|
|
|
1504
1577
|
id: response.data.id,
|
|
1505
1578
|
title: response.data.title,
|
|
1506
1579
|
space: space_key,
|
|
1507
|
-
url: `${
|
|
1580
|
+
url: `${response.siteUrl}/wiki${response.data._links.webui}`,
|
|
1508
1581
|
version: response.data.version?.number || 1
|
|
1509
1582
|
}
|
|
1510
1583
|
});
|
|
1511
1584
|
} catch (error) {
|
|
1585
|
+
const description = request2.describeError(error);
|
|
1512
1586
|
logger20.error("create-confluence-page error", {
|
|
1513
1587
|
space_key,
|
|
1514
1588
|
title,
|
|
1515
|
-
error:
|
|
1516
|
-
status:
|
|
1589
|
+
...description.message !== void 0 ? { error: description.message } : {},
|
|
1590
|
+
...description.status !== void 0 ? { status: description.status } : {}
|
|
1517
1591
|
});
|
|
1518
1592
|
return JSON.stringify({
|
|
1519
1593
|
success: false,
|
|
1520
|
-
error:
|
|
1594
|
+
error: description.message
|
|
1521
1595
|
});
|
|
1522
1596
|
}
|
|
1523
1597
|
}).build();
|
|
1524
1598
|
}
|
|
1525
|
-
function createUpdateConfluencePageTool(
|
|
1526
|
-
return toolBuilder().name("update-confluence-page").description(
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1599
|
+
function createUpdateConfluencePageTool(request2, logger20) {
|
|
1600
|
+
return toolBuilder().name("update-confluence-page").description(
|
|
1601
|
+
"Update an existing Confluence page's content. Requires page ID, new title, and new content."
|
|
1602
|
+
).category(ToolCategory.WEB).tag("confluence").tag("update").tag("write").usageNotes(
|
|
1603
|
+
"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."
|
|
1604
|
+
).requires(["get-confluence-page"]).schema(
|
|
1605
|
+
z.object({
|
|
1606
|
+
page_id: z.string().describe("The ID of the page to update"),
|
|
1607
|
+
title: z.string().describe("The new title for the page"),
|
|
1608
|
+
content: z.string().describe("The new content in HTML format (Confluence storage format)")
|
|
1609
|
+
})
|
|
1610
|
+
).implement(async ({ page_id, title, content }) => {
|
|
1531
1611
|
logger20.info("update-confluence-page called", { page_id, title });
|
|
1532
1612
|
try {
|
|
1533
|
-
const
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
{
|
|
1537
|
-
headers: {
|
|
1538
|
-
Authorization: getAuthHeader2()
|
|
1539
|
-
},
|
|
1540
|
-
params: { expand: "version" }
|
|
1541
|
-
}
|
|
1542
|
-
);
|
|
1613
|
+
const getResponse = await request2.get(`/wiki/rest/api/content/${page_id}`, {
|
|
1614
|
+
params: { expand: "version" }
|
|
1615
|
+
});
|
|
1543
1616
|
const currentVersion = getResponse.data.version.number;
|
|
1544
|
-
const updateResponse = await
|
|
1545
|
-
|
|
1617
|
+
const updateResponse = await request2.put(
|
|
1618
|
+
`/wiki/rest/api/content/${page_id}`,
|
|
1546
1619
|
{
|
|
1547
1620
|
type: "page",
|
|
1548
1621
|
title,
|
|
@@ -1556,7 +1629,6 @@ function createUpdateConfluencePageTool(getAuth, getAuthHeader2, logger20) {
|
|
|
1556
1629
|
},
|
|
1557
1630
|
{
|
|
1558
1631
|
headers: {
|
|
1559
|
-
Authorization: getAuthHeader2(),
|
|
1560
1632
|
"Content-Type": "application/json"
|
|
1561
1633
|
}
|
|
1562
1634
|
}
|
|
@@ -1572,49 +1644,49 @@ function createUpdateConfluencePageTool(getAuth, getAuthHeader2, logger20) {
|
|
|
1572
1644
|
page: {
|
|
1573
1645
|
id: updateResponse.data.id,
|
|
1574
1646
|
title: updateResponse.data.title,
|
|
1575
|
-
url: `${
|
|
1647
|
+
url: `${updateResponse.siteUrl}/wiki${updateResponse.data._links.webui}`,
|
|
1576
1648
|
version: updateResponse.data.version.number,
|
|
1577
1649
|
previousVersion: currentVersion
|
|
1578
1650
|
}
|
|
1579
1651
|
});
|
|
1580
1652
|
} catch (error) {
|
|
1653
|
+
const description = request2.describeError(error);
|
|
1581
1654
|
logger20.error("update-confluence-page error", {
|
|
1582
1655
|
page_id,
|
|
1583
1656
|
title,
|
|
1584
|
-
error:
|
|
1585
|
-
status:
|
|
1657
|
+
...description.message !== void 0 ? { error: description.message } : {},
|
|
1658
|
+
...description.status !== void 0 ? { status: description.status } : {}
|
|
1586
1659
|
});
|
|
1587
1660
|
return JSON.stringify({
|
|
1588
1661
|
success: false,
|
|
1589
|
-
error:
|
|
1662
|
+
error: description.message
|
|
1590
1663
|
});
|
|
1591
1664
|
}
|
|
1592
1665
|
}).build();
|
|
1593
1666
|
}
|
|
1594
|
-
function createArchiveConfluencePageTool(
|
|
1595
|
-
return toolBuilder().name("archive-confluence-page").description(
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
1667
|
+
function createArchiveConfluencePageTool(request2, logger20) {
|
|
1668
|
+
return toolBuilder().name("archive-confluence-page").description(
|
|
1669
|
+
"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."
|
|
1670
|
+
).category(ToolCategory.WEB).tag("confluence").tag("archive").tag("delete").usageNotes(
|
|
1671
|
+
"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."
|
|
1672
|
+
).conflicts(["create-confluence-page"]).schema(
|
|
1673
|
+
z.object({
|
|
1674
|
+
page_id: z.string().describe("The ID of the page to archive"),
|
|
1675
|
+
reason: z.string().optional().describe("Optional reason for archiving (for audit trail)")
|
|
1676
|
+
})
|
|
1677
|
+
).implement(async ({ page_id, reason }) => {
|
|
1599
1678
|
logger20.info("archive-confluence-page called", {
|
|
1600
1679
|
page_id,
|
|
1601
1680
|
...reason ? { reason } : {}
|
|
1602
1681
|
});
|
|
1603
1682
|
try {
|
|
1604
|
-
const
|
|
1605
|
-
|
|
1606
|
-
|
|
1607
|
-
{
|
|
1608
|
-
headers: {
|
|
1609
|
-
Authorization: getAuthHeader2()
|
|
1610
|
-
},
|
|
1611
|
-
params: { expand: "version,body.storage,space" }
|
|
1612
|
-
}
|
|
1613
|
-
);
|
|
1683
|
+
const getResponse = await request2.get(`/wiki/rest/api/content/${page_id}`, {
|
|
1684
|
+
params: { expand: "version,body.storage,space" }
|
|
1685
|
+
});
|
|
1614
1686
|
const currentVersion = getResponse.data.version.number;
|
|
1615
1687
|
const pageData = getResponse.data;
|
|
1616
|
-
await
|
|
1617
|
-
|
|
1688
|
+
await request2.put(
|
|
1689
|
+
`/wiki/rest/api/content/${page_id}`,
|
|
1618
1690
|
{
|
|
1619
1691
|
version: { number: currentVersion + 1 },
|
|
1620
1692
|
title: pageData.title,
|
|
@@ -1625,7 +1697,6 @@ function createArchiveConfluencePageTool(getAuth, getAuthHeader2, logger20) {
|
|
|
1625
1697
|
},
|
|
1626
1698
|
{
|
|
1627
1699
|
headers: {
|
|
1628
|
-
Authorization: getAuthHeader2(),
|
|
1629
1700
|
"Content-Type": "application/json"
|
|
1630
1701
|
}
|
|
1631
1702
|
}
|
|
@@ -1648,14 +1719,15 @@ function createArchiveConfluencePageTool(getAuth, getAuthHeader2, logger20) {
|
|
|
1648
1719
|
}
|
|
1649
1720
|
});
|
|
1650
1721
|
} catch (error) {
|
|
1722
|
+
const description = request2.describeError(error);
|
|
1651
1723
|
logger20.error("archive-confluence-page error", {
|
|
1652
1724
|
page_id,
|
|
1653
|
-
error:
|
|
1654
|
-
status:
|
|
1725
|
+
...description.message !== void 0 ? { error: description.message } : {},
|
|
1726
|
+
...description.status !== void 0 ? { status: description.status } : {}
|
|
1655
1727
|
});
|
|
1656
1728
|
return JSON.stringify({
|
|
1657
1729
|
success: false,
|
|
1658
|
-
error:
|
|
1730
|
+
error: description.message
|
|
1659
1731
|
});
|
|
1660
1732
|
}
|
|
1661
1733
|
}).build();
|
|
@@ -1664,13 +1736,14 @@ function createArchiveConfluencePageTool(getAuth, getAuthHeader2, logger20) {
|
|
|
1664
1736
|
// src/web/confluence/index.ts
|
|
1665
1737
|
var logLevel2 = process.env.LOG_LEVEL?.toLowerCase() || LogLevel.INFO;
|
|
1666
1738
|
var logger2 = createLogger("[tools:confluence]", { level: logLevel2 });
|
|
1667
|
-
var
|
|
1668
|
-
var
|
|
1669
|
-
var
|
|
1670
|
-
var
|
|
1671
|
-
var
|
|
1672
|
-
var
|
|
1673
|
-
var
|
|
1739
|
+
var request = createConfluenceRequest(getConfig);
|
|
1740
|
+
var searchConfluence = createSearchConfluenceTool(request, logger2);
|
|
1741
|
+
var getConfluencePage = createGetConfluencePageTool(request, logger2);
|
|
1742
|
+
var listConfluenceSpaces = createListConfluenceSpacesTool(request, logger2);
|
|
1743
|
+
var getSpacePages = createGetSpacePagesTool(request, logger2);
|
|
1744
|
+
var createConfluencePage = createCreateConfluencePageTool(request, logger2);
|
|
1745
|
+
var updateConfluencePage = createUpdateConfluencePageTool(request, logger2);
|
|
1746
|
+
var archiveConfluencePage = createArchiveConfluencePageTool(request, logger2);
|
|
1674
1747
|
var confluenceTools = [
|
|
1675
1748
|
// Read tools
|
|
1676
1749
|
searchConfluence,
|
|
@@ -1683,22 +1756,17 @@ var confluenceTools = [
|
|
|
1683
1756
|
archiveConfluencePage
|
|
1684
1757
|
];
|
|
1685
1758
|
function createConfluenceTools(config = {}) {
|
|
1686
|
-
const {
|
|
1687
|
-
apiKey,
|
|
1688
|
-
email,
|
|
1689
|
-
siteUrl,
|
|
1690
|
-
logLevel: customLogLevel
|
|
1691
|
-
} = config;
|
|
1759
|
+
const { apiKey, email, siteUrl, logLevel: customLogLevel } = config;
|
|
1692
1760
|
const getConfiguredAuth = createGetConfiguredAuth(apiKey, email, siteUrl);
|
|
1693
|
-
const
|
|
1761
|
+
const request2 = createConfluenceRequest(getConfiguredAuth);
|
|
1694
1762
|
const toolLogger = customLogLevel ? createLogger("tools:confluence", { level: customLogLevel }) : logger2;
|
|
1695
|
-
const searchConfluence2 = createSearchConfluenceTool(
|
|
1696
|
-
const getConfluencePage2 = createGetConfluencePageTool(
|
|
1697
|
-
const listConfluenceSpaces2 = createListConfluenceSpacesTool(
|
|
1698
|
-
const getSpacePages2 = createGetSpacePagesTool(
|
|
1699
|
-
const createConfluencePage2 = createCreateConfluencePageTool(
|
|
1700
|
-
const updateConfluencePage2 = createUpdateConfluencePageTool(
|
|
1701
|
-
const archiveConfluencePage2 = createArchiveConfluencePageTool(
|
|
1763
|
+
const searchConfluence2 = createSearchConfluenceTool(request2, toolLogger);
|
|
1764
|
+
const getConfluencePage2 = createGetConfluencePageTool(request2, toolLogger);
|
|
1765
|
+
const listConfluenceSpaces2 = createListConfluenceSpacesTool(request2, toolLogger);
|
|
1766
|
+
const getSpacePages2 = createGetSpacePagesTool(request2, toolLogger);
|
|
1767
|
+
const createConfluencePage2 = createCreateConfluencePageTool(request2, toolLogger);
|
|
1768
|
+
const updateConfluencePage2 = createUpdateConfluencePageTool(request2, toolLogger);
|
|
1769
|
+
const archiveConfluencePage2 = createArchiveConfluencePageTool(request2, toolLogger);
|
|
1702
1770
|
return {
|
|
1703
1771
|
searchConfluence: searchConfluence2,
|
|
1704
1772
|
getConfluencePage: getConfluencePage2,
|
|
@@ -1889,14 +1957,14 @@ function createJsonStringifyTool(defaultIndent = 2, defaultPretty = false) {
|
|
|
1889
1957
|
};
|
|
1890
1958
|
}).build();
|
|
1891
1959
|
}
|
|
1892
|
-
function
|
|
1960
|
+
function isRecord2(value) {
|
|
1893
1961
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
1894
1962
|
}
|
|
1895
1963
|
function getPathValue(current, part) {
|
|
1896
1964
|
const arrayMatch = part.match(/^(\w+)\[(\d+)\]$/);
|
|
1897
1965
|
if (arrayMatch) {
|
|
1898
1966
|
const [, key, index] = arrayMatch;
|
|
1899
|
-
if (!
|
|
1967
|
+
if (!isRecord2(current)) {
|
|
1900
1968
|
return void 0;
|
|
1901
1969
|
}
|
|
1902
1970
|
const candidate = current[key];
|
|
@@ -1905,7 +1973,7 @@ function getPathValue(current, part) {
|
|
|
1905
1973
|
}
|
|
1906
1974
|
return candidate[parseInt(index, 10)];
|
|
1907
1975
|
}
|
|
1908
|
-
if (!
|
|
1976
|
+
if (!isRecord2(current)) {
|
|
1909
1977
|
return void 0;
|
|
1910
1978
|
}
|
|
1911
1979
|
return current[part];
|
|
@@ -2561,7 +2629,7 @@ async function initializeFromEnv() {
|
|
|
2561
2629
|
// src/data/neo4j/embeddings/utils.ts
|
|
2562
2630
|
function withProviderErrorMetadata(error, message) {
|
|
2563
2631
|
const wrappedError = new Error(message);
|
|
2564
|
-
if (!
|
|
2632
|
+
if (!isRecord3(error)) return wrappedError;
|
|
2565
2633
|
if (typeof error.code === "string") wrappedError.code = error.code;
|
|
2566
2634
|
if (error.response !== void 0) wrappedError.response = error.response;
|
|
2567
2635
|
return wrappedError;
|
|
@@ -2574,12 +2642,12 @@ var DEFAULT_RETRY_CONFIG2 = {
|
|
|
2574
2642
|
// 10 seconds
|
|
2575
2643
|
backoffMultiplier: 2
|
|
2576
2644
|
};
|
|
2577
|
-
function
|
|
2645
|
+
function isRecord3(value) {
|
|
2578
2646
|
return typeof value === "object" && value !== null;
|
|
2579
2647
|
}
|
|
2580
2648
|
function isRetryableError2(error) {
|
|
2581
|
-
if (!
|
|
2582
|
-
const response =
|
|
2649
|
+
if (!isRecord3(error)) return false;
|
|
2650
|
+
const response = isRecord3(error.response) ? error.response : void 0;
|
|
2583
2651
|
const status = typeof response?.status === "number" ? response.status : void 0;
|
|
2584
2652
|
const code = typeof error.code === "string" ? error.code : void 0;
|
|
2585
2653
|
const message = typeof error.message === "string" ? error.message : void 0;
|
|
@@ -2705,7 +2773,7 @@ var OpenAIEmbeddingProvider = class {
|
|
|
2705
2773
|
const modelToUse = model || this.defaultModel;
|
|
2706
2774
|
return retryWithBackoff2(async () => {
|
|
2707
2775
|
try {
|
|
2708
|
-
const response = await
|
|
2776
|
+
const response = await axios5.post(
|
|
2709
2777
|
`${this.baseURL}/embeddings`,
|
|
2710
2778
|
{
|
|
2711
2779
|
input: text,
|
|
@@ -2735,7 +2803,7 @@ var OpenAIEmbeddingProvider = class {
|
|
|
2735
2803
|
};
|
|
2736
2804
|
} catch (error) {
|
|
2737
2805
|
let message;
|
|
2738
|
-
const response =
|
|
2806
|
+
const response = axios5.isAxiosError(error) ? error.response : void 0;
|
|
2739
2807
|
if (response?.status === 401) {
|
|
2740
2808
|
message = "Invalid OpenAI API key. Get your key at https://platform.openai.com/api-keys";
|
|
2741
2809
|
} else if (response?.status === 429) {
|
|
@@ -2762,7 +2830,7 @@ var OpenAIEmbeddingProvider = class {
|
|
|
2762
2830
|
const modelToUse = model || this.defaultModel;
|
|
2763
2831
|
return retryWithBackoff2(async () => {
|
|
2764
2832
|
try {
|
|
2765
|
-
const response = await
|
|
2833
|
+
const response = await axios5.post(
|
|
2766
2834
|
`${this.baseURL}/embeddings`,
|
|
2767
2835
|
{
|
|
2768
2836
|
input: texts,
|
|
@@ -2793,7 +2861,7 @@ var OpenAIEmbeddingProvider = class {
|
|
|
2793
2861
|
};
|
|
2794
2862
|
} catch (error) {
|
|
2795
2863
|
let message;
|
|
2796
|
-
const response =
|
|
2864
|
+
const response = axios5.isAxiosError(error) ? error.response : void 0;
|
|
2797
2865
|
if (response?.status === 401) {
|
|
2798
2866
|
message = "Invalid OpenAI API key. Get your key at https://platform.openai.com/api-keys";
|
|
2799
2867
|
} else if (response?.status === 429) {
|
|
@@ -2835,7 +2903,7 @@ var CohereEmbeddingProvider = class {
|
|
|
2835
2903
|
const modelToUse = model || this.defaultModel;
|
|
2836
2904
|
return retryWithBackoff2(async () => {
|
|
2837
2905
|
try {
|
|
2838
|
-
const response = await
|
|
2906
|
+
const response = await axios5.post(
|
|
2839
2907
|
`${this.baseUrl}/embed`,
|
|
2840
2908
|
{
|
|
2841
2909
|
texts,
|
|
@@ -2863,7 +2931,7 @@ var CohereEmbeddingProvider = class {
|
|
|
2863
2931
|
} : void 0
|
|
2864
2932
|
};
|
|
2865
2933
|
} catch (error) {
|
|
2866
|
-
if (
|
|
2934
|
+
if (axios5.isAxiosError(error)) {
|
|
2867
2935
|
const status = error.response?.status;
|
|
2868
2936
|
const providerMessage = error.response?.data?.message || error.message;
|
|
2869
2937
|
let message;
|
|
@@ -2901,7 +2969,7 @@ var HuggingFaceEmbeddingProvider = class {
|
|
|
2901
2969
|
const modelToUse = model || this.defaultModel;
|
|
2902
2970
|
return retryWithBackoff2(async () => {
|
|
2903
2971
|
try {
|
|
2904
|
-
const response = await
|
|
2972
|
+
const response = await axios5.post(
|
|
2905
2973
|
`${this.baseUrl}/${modelToUse}`,
|
|
2906
2974
|
{
|
|
2907
2975
|
inputs: text,
|
|
@@ -2923,7 +2991,7 @@ var HuggingFaceEmbeddingProvider = class {
|
|
|
2923
2991
|
dimensions: embedding.length
|
|
2924
2992
|
};
|
|
2925
2993
|
} catch (error) {
|
|
2926
|
-
if (
|
|
2994
|
+
if (axios5.isAxiosError(error)) {
|
|
2927
2995
|
const status = error.response?.status;
|
|
2928
2996
|
const message = error.response?.data?.error || error.message;
|
|
2929
2997
|
let messageText;
|
|
@@ -2948,7 +3016,7 @@ var HuggingFaceEmbeddingProvider = class {
|
|
|
2948
3016
|
const modelToUse = model || this.defaultModel;
|
|
2949
3017
|
return retryWithBackoff2(async () => {
|
|
2950
3018
|
try {
|
|
2951
|
-
const response = await
|
|
3019
|
+
const response = await axios5.post(
|
|
2952
3020
|
`${this.baseUrl}/${modelToUse}`,
|
|
2953
3021
|
{
|
|
2954
3022
|
inputs: texts,
|
|
@@ -2971,7 +3039,7 @@ var HuggingFaceEmbeddingProvider = class {
|
|
|
2971
3039
|
dimensions
|
|
2972
3040
|
};
|
|
2973
3041
|
} catch (error) {
|
|
2974
|
-
if (
|
|
3042
|
+
if (axios5.isAxiosError(error)) {
|
|
2975
3043
|
const status = error.response?.status;
|
|
2976
3044
|
const message = error.response?.data?.error || error.message;
|
|
2977
3045
|
let messageText;
|
|
@@ -3020,7 +3088,7 @@ var VoyageEmbeddingProvider = class {
|
|
|
3020
3088
|
const modelToUse = model || this.defaultModel;
|
|
3021
3089
|
return retryWithBackoff2(async () => {
|
|
3022
3090
|
try {
|
|
3023
|
-
const response = await
|
|
3091
|
+
const response = await axios5.post(
|
|
3024
3092
|
`${this.baseUrl}/embeddings`,
|
|
3025
3093
|
{
|
|
3026
3094
|
input: texts,
|
|
@@ -3047,7 +3115,7 @@ var VoyageEmbeddingProvider = class {
|
|
|
3047
3115
|
} : void 0
|
|
3048
3116
|
};
|
|
3049
3117
|
} catch (error) {
|
|
3050
|
-
if (
|
|
3118
|
+
if (axios5.isAxiosError(error)) {
|
|
3051
3119
|
const status = error.response?.status;
|
|
3052
3120
|
const message = error.response?.data?.message || error.message;
|
|
3053
3121
|
let messageText;
|
|
@@ -3084,7 +3152,7 @@ var OllamaEmbeddingProvider = class {
|
|
|
3084
3152
|
const modelToUse = model || this.defaultModel;
|
|
3085
3153
|
return retryWithBackoff2(async () => {
|
|
3086
3154
|
try {
|
|
3087
|
-
const response = await
|
|
3155
|
+
const response = await axios5.post(
|
|
3088
3156
|
`${this.baseUrl}/api/embeddings`,
|
|
3089
3157
|
{
|
|
3090
3158
|
model: modelToUse,
|
|
@@ -3103,7 +3171,7 @@ var OllamaEmbeddingProvider = class {
|
|
|
3103
3171
|
dimensions: embedding.length
|
|
3104
3172
|
};
|
|
3105
3173
|
} catch (error) {
|
|
3106
|
-
if (
|
|
3174
|
+
if (axios5.isAxiosError(error)) {
|
|
3107
3175
|
const status = error.response?.status;
|
|
3108
3176
|
const message = error.response?.data?.error || error.message;
|
|
3109
3177
|
let messageText;
|
|
@@ -3496,6 +3564,16 @@ function formatResults(records) {
|
|
|
3496
3564
|
});
|
|
3497
3565
|
}
|
|
3498
3566
|
|
|
3567
|
+
// src/data/neo4j/tools/session-owner.ts
|
|
3568
|
+
async function withNeo4jSession(database, operation) {
|
|
3569
|
+
const session = neo4jPool.getSession(database);
|
|
3570
|
+
try {
|
|
3571
|
+
return await operation(session);
|
|
3572
|
+
} finally {
|
|
3573
|
+
await session.close();
|
|
3574
|
+
}
|
|
3575
|
+
}
|
|
3576
|
+
|
|
3499
3577
|
// src/data/neo4j/tools/neo4j-query.ts
|
|
3500
3578
|
var logger4 = createLogger("agentforge:tools:neo4j:query");
|
|
3501
3579
|
function createNeo4jQueryTool() {
|
|
@@ -3516,8 +3594,7 @@ function createNeo4jQueryTool() {
|
|
|
3516
3594
|
...input.database ? { database: input.database } : {}
|
|
3517
3595
|
});
|
|
3518
3596
|
try {
|
|
3519
|
-
|
|
3520
|
-
try {
|
|
3597
|
+
return await withNeo4jSession(input.database, async (session) => {
|
|
3521
3598
|
const result = await session.run(input.cypher, input.parameters || {});
|
|
3522
3599
|
const formattedResults = formatResults(result.records);
|
|
3523
3600
|
const duration = Date.now() - startTime;
|
|
@@ -3546,9 +3623,7 @@ function createNeo4jQueryTool() {
|
|
|
3546
3623
|
}
|
|
3547
3624
|
}
|
|
3548
3625
|
};
|
|
3549
|
-
}
|
|
3550
|
-
await session.close();
|
|
3551
|
-
}
|
|
3626
|
+
});
|
|
3552
3627
|
} catch (error) {
|
|
3553
3628
|
const duration = Date.now() - startTime;
|
|
3554
3629
|
const errorMessage = error instanceof Error ? error.message : "Failed to execute query";
|
|
@@ -3576,8 +3651,7 @@ function createNeo4jGetSchemaTool() {
|
|
|
3576
3651
|
};
|
|
3577
3652
|
}
|
|
3578
3653
|
try {
|
|
3579
|
-
|
|
3580
|
-
try {
|
|
3654
|
+
return await withNeo4jSession(input.database, async (session) => {
|
|
3581
3655
|
const labelsResult = await session.run("CALL db.labels()");
|
|
3582
3656
|
const nodeLabels = labelsResult.records.map((r) => r.get("label"));
|
|
3583
3657
|
const relTypesResult = await session.run("CALL db.relationshipTypes()");
|
|
@@ -3617,9 +3691,7 @@ function createNeo4jGetSchemaTool() {
|
|
|
3617
3691
|
totalIndexes: indexes.length
|
|
3618
3692
|
}
|
|
3619
3693
|
};
|
|
3620
|
-
}
|
|
3621
|
-
await session.close();
|
|
3622
|
-
}
|
|
3694
|
+
});
|
|
3623
3695
|
} catch (error) {
|
|
3624
3696
|
return {
|
|
3625
3697
|
success: false,
|
|
@@ -3698,8 +3770,7 @@ function createNeo4jFindNodesTool() {
|
|
|
3698
3770
|
}
|
|
3699
3771
|
try {
|
|
3700
3772
|
const safeLabel = validateLabel(input.label);
|
|
3701
|
-
|
|
3702
|
-
try {
|
|
3773
|
+
return await withNeo4jSession(input.database, async (session) => {
|
|
3703
3774
|
let cypher = `MATCH (n:${safeLabel})`;
|
|
3704
3775
|
let parameters = {};
|
|
3705
3776
|
if (input.properties && Object.keys(input.properties).length > 0) {
|
|
@@ -3721,9 +3792,7 @@ function createNeo4jFindNodesTool() {
|
|
|
3721
3792
|
limit: input.limit
|
|
3722
3793
|
}
|
|
3723
3794
|
};
|
|
3724
|
-
}
|
|
3725
|
-
await session.close();
|
|
3726
|
-
}
|
|
3795
|
+
});
|
|
3727
3796
|
} catch (error) {
|
|
3728
3797
|
return {
|
|
3729
3798
|
success: false,
|
|
@@ -3749,8 +3818,7 @@ function createNeo4jTraverseTool() {
|
|
|
3749
3818
|
try {
|
|
3750
3819
|
const safeDirection = validateDirection(input.direction || "outgoing");
|
|
3751
3820
|
const safeRelType = input.relationshipType ? validateRelationshipType(input.relationshipType) : null;
|
|
3752
|
-
|
|
3753
|
-
try {
|
|
3821
|
+
return await withNeo4jSession(input.database, async (session) => {
|
|
3754
3822
|
let relPattern = "";
|
|
3755
3823
|
if (safeDirection === "OUTGOING") {
|
|
3756
3824
|
relPattern = safeRelType ? `-[r:${safeRelType}*1..${input.maxDepth}]->` : `-[r*1..${input.maxDepth}]->`;
|
|
@@ -3787,9 +3855,7 @@ function createNeo4jTraverseTool() {
|
|
|
3787
3855
|
maxDepth: input.maxDepth
|
|
3788
3856
|
}
|
|
3789
3857
|
};
|
|
3790
|
-
}
|
|
3791
|
-
await session.close();
|
|
3792
|
-
}
|
|
3858
|
+
});
|
|
3793
3859
|
} catch (error) {
|
|
3794
3860
|
return {
|
|
3795
3861
|
success: false,
|
|
@@ -3814,8 +3880,7 @@ function createNeo4jVectorSearchTool() {
|
|
|
3814
3880
|
};
|
|
3815
3881
|
}
|
|
3816
3882
|
try {
|
|
3817
|
-
|
|
3818
|
-
try {
|
|
3883
|
+
return await withNeo4jSession(input.database, async (session) => {
|
|
3819
3884
|
const cypher = `
|
|
3820
3885
|
CALL db.index.vector.queryNodes($indexName, $limit, $queryVector)
|
|
3821
3886
|
YIELD node, score
|
|
@@ -3842,9 +3907,7 @@ function createNeo4jVectorSearchTool() {
|
|
|
3842
3907
|
limit: input.limit
|
|
3843
3908
|
}
|
|
3844
3909
|
};
|
|
3845
|
-
}
|
|
3846
|
-
await session.close();
|
|
3847
|
-
}
|
|
3910
|
+
});
|
|
3848
3911
|
} catch (error) {
|
|
3849
3912
|
const errorMessage = error instanceof Error ? error.message : "Failed to perform vector search";
|
|
3850
3913
|
let helpText = "";
|
|
@@ -3890,8 +3953,7 @@ function createNeo4jVectorSearchWithEmbeddingTool() {
|
|
|
3890
3953
|
});
|
|
3891
3954
|
try {
|
|
3892
3955
|
const embeddingResult = await embeddingManager.generateEmbedding(input.queryText, input.model);
|
|
3893
|
-
|
|
3894
|
-
try {
|
|
3956
|
+
return await withNeo4jSession(input.database, async (session) => {
|
|
3895
3957
|
const cypher = `
|
|
3896
3958
|
CALL db.index.vector.queryNodes($indexName, $limit, $queryVector)
|
|
3897
3959
|
YIELD node, score
|
|
@@ -3933,9 +3995,7 @@ function createNeo4jVectorSearchWithEmbeddingTool() {
|
|
|
3933
3995
|
usage: embeddingResult.usage
|
|
3934
3996
|
}
|
|
3935
3997
|
};
|
|
3936
|
-
}
|
|
3937
|
-
await session.close();
|
|
3938
|
-
}
|
|
3998
|
+
});
|
|
3939
3999
|
} catch (error) {
|
|
3940
4000
|
const duration = Date.now() - startTime;
|
|
3941
4001
|
const errorMessage = error instanceof Error ? error.message : "Failed to perform vector search with embedding";
|
|
@@ -3991,8 +4051,7 @@ function createNeo4jCreateNodeWithEmbeddingTool() {
|
|
|
3991
4051
|
const embeddingResult = await embeddingManager.generateEmbedding(textToEmbed, input.model);
|
|
3992
4052
|
const safeLabel = validateLabel(input.label);
|
|
3993
4053
|
const safeEmbeddingProp = validatePropertyKey(input.embeddingProperty || "embedding");
|
|
3994
|
-
|
|
3995
|
-
try {
|
|
4054
|
+
return await withNeo4jSession(input.database, async (session) => {
|
|
3996
4055
|
const allProperties = {
|
|
3997
4056
|
...input.properties
|
|
3998
4057
|
};
|
|
@@ -4026,9 +4085,7 @@ function createNeo4jCreateNodeWithEmbeddingTool() {
|
|
|
4026
4085
|
},
|
|
4027
4086
|
message: `Created node with label '${input.label}' and ${embeddingResult.dimensions}-dimensional embedding`
|
|
4028
4087
|
};
|
|
4029
|
-
}
|
|
4030
|
-
await session.close();
|
|
4031
|
-
}
|
|
4088
|
+
});
|
|
4032
4089
|
} catch (error) {
|
|
4033
4090
|
const errorMessage = error instanceof Error ? error.message : "Failed to create node with embedding";
|
|
4034
4091
|
let helpText = "";
|
|
@@ -10315,49 +10372,57 @@ var fileSearchSchema = z.object({
|
|
|
10315
10372
|
recursive: z.boolean().default(true).describe("Search in subdirectories"),
|
|
10316
10373
|
caseSensitive: z.boolean().default(false).describe("Case-sensitive pattern matching")
|
|
10317
10374
|
});
|
|
10375
|
+
async function* traverseDirectory(directory, recursive) {
|
|
10376
|
+
const entries = await promises.readdir(directory, { withFileTypes: true });
|
|
10377
|
+
for (const entry of entries) {
|
|
10378
|
+
const fullPath = path.join(directory, entry.name);
|
|
10379
|
+
const isDirectory = entry.isDirectory();
|
|
10380
|
+
yield {
|
|
10381
|
+
name: entry.name,
|
|
10382
|
+
fullPath,
|
|
10383
|
+
isFile: entry.isFile(),
|
|
10384
|
+
isDirectory
|
|
10385
|
+
};
|
|
10386
|
+
if (recursive && isDirectory) {
|
|
10387
|
+
yield* traverseDirectory(fullPath, true);
|
|
10388
|
+
}
|
|
10389
|
+
}
|
|
10390
|
+
}
|
|
10391
|
+
|
|
10392
|
+
// src/file/directory/tools/directory-list.ts
|
|
10318
10393
|
function createDirectoryListTool(defaultRecursive = false, defaultIncludeDetails = false, policy = DEFAULT_FILE_SYSTEM_POLICY) {
|
|
10319
10394
|
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) => {
|
|
10320
10395
|
const safePath = await policy.resolvePath(input.path, "directory listing");
|
|
10321
10396
|
const includeDetails = input.includeDetails ?? defaultIncludeDetails;
|
|
10322
|
-
const
|
|
10323
|
-
|
|
10324
|
-
|
|
10325
|
-
|
|
10326
|
-
|
|
10327
|
-
|
|
10328
|
-
|
|
10329
|
-
if (!entry.isDirectory() || !recursive2) {
|
|
10330
|
-
continue;
|
|
10331
|
-
}
|
|
10332
|
-
}
|
|
10333
|
-
if (includeDetails) {
|
|
10334
|
-
const stats = await promises.lstat(fullPath);
|
|
10335
|
-
files2.push({
|
|
10336
|
-
name: entry.name,
|
|
10337
|
-
path: relativePath,
|
|
10338
|
-
fullPath,
|
|
10339
|
-
isFile: entry.isFile(),
|
|
10340
|
-
isDirectory: entry.isDirectory(),
|
|
10341
|
-
size: stats.size,
|
|
10342
|
-
modified: stats.mtime.toISOString()
|
|
10343
|
-
});
|
|
10344
|
-
} else {
|
|
10345
|
-
files2.push({
|
|
10346
|
-
name: entry.name,
|
|
10347
|
-
path: relativePath,
|
|
10348
|
-
isFile: entry.isFile(),
|
|
10349
|
-
isDirectory: entry.isDirectory()
|
|
10350
|
-
});
|
|
10351
|
-
}
|
|
10352
|
-
if (recursive2 && entry.isDirectory()) {
|
|
10353
|
-
const subFiles = await listFiles(fullPath, true);
|
|
10354
|
-
files2.push(...subFiles);
|
|
10397
|
+
const recursive = input.recursive ?? defaultRecursive;
|
|
10398
|
+
const files = [];
|
|
10399
|
+
for await (const entry of traverseDirectory(safePath, recursive)) {
|
|
10400
|
+
const relativePath = path.relative(safePath, entry.fullPath);
|
|
10401
|
+
if (input.extension && !entry.name.endsWith(input.extension)) {
|
|
10402
|
+
if (!entry.isDirectory || !recursive) {
|
|
10403
|
+
continue;
|
|
10355
10404
|
}
|
|
10356
10405
|
}
|
|
10357
|
-
|
|
10358
|
-
|
|
10359
|
-
|
|
10360
|
-
|
|
10406
|
+
if (includeDetails) {
|
|
10407
|
+
const stats = await promises.lstat(entry.fullPath);
|
|
10408
|
+
files.push({
|
|
10409
|
+
name: entry.name,
|
|
10410
|
+
path: relativePath,
|
|
10411
|
+
fullPath: entry.fullPath,
|
|
10412
|
+
isFile: entry.isFile,
|
|
10413
|
+
isDirectory: entry.isDirectory,
|
|
10414
|
+
size: stats.size,
|
|
10415
|
+
modified: stats.mtime.toISOString()
|
|
10416
|
+
});
|
|
10417
|
+
} else {
|
|
10418
|
+
files.push({
|
|
10419
|
+
name: entry.name,
|
|
10420
|
+
path: relativePath,
|
|
10421
|
+
isFile: entry.isFile,
|
|
10422
|
+
isDirectory: entry.isDirectory
|
|
10423
|
+
});
|
|
10424
|
+
}
|
|
10425
|
+
}
|
|
10361
10426
|
const result = {
|
|
10362
10427
|
path: input.path,
|
|
10363
10428
|
files,
|
|
@@ -10393,24 +10458,14 @@ function createFileSearchTool(defaultRecursive = true, defaultCaseSensitive = fa
|
|
|
10393
10458
|
const recursive = input.recursive ?? defaultRecursive;
|
|
10394
10459
|
const caseSensitive = input.caseSensitive ?? defaultCaseSensitive;
|
|
10395
10460
|
const safeDirectory = await policy.resolvePath(input.directory, "file search");
|
|
10396
|
-
const
|
|
10397
|
-
|
|
10398
|
-
|
|
10399
|
-
|
|
10400
|
-
|
|
10401
|
-
|
|
10402
|
-
const fullPath = path.join(dir, entry.name);
|
|
10403
|
-
if (entry.isFile() && regex.test(entry.name)) {
|
|
10404
|
-
matches2.push(fullPath);
|
|
10405
|
-
}
|
|
10406
|
-
if (recursive && entry.isDirectory()) {
|
|
10407
|
-
const subMatches = await searchFiles(fullPath);
|
|
10408
|
-
matches2.push(...subMatches);
|
|
10409
|
-
}
|
|
10461
|
+
const regexPattern = input.pattern.replace(/\./g, "\\.").replace(/\*/g, ".*");
|
|
10462
|
+
const regex = new RegExp(`^${regexPattern}$`, caseSensitive ? "" : "i");
|
|
10463
|
+
const matches = [];
|
|
10464
|
+
for await (const entry of traverseDirectory(safeDirectory, recursive)) {
|
|
10465
|
+
if (entry.isFile && regex.test(entry.name)) {
|
|
10466
|
+
matches.push(entry.fullPath);
|
|
10410
10467
|
}
|
|
10411
|
-
|
|
10412
|
-
};
|
|
10413
|
-
const matches = await searchFiles(safeDirectory);
|
|
10468
|
+
}
|
|
10414
10469
|
return {
|
|
10415
10470
|
directory: input.directory,
|
|
10416
10471
|
pattern: input.pattern,
|
|
@@ -11372,11 +11427,11 @@ var AskHumanInputSchema = z.object({
|
|
|
11372
11427
|
});
|
|
11373
11428
|
var logLevel3 = process.env.LOG_LEVEL?.toLowerCase() || LogLevel.INFO;
|
|
11374
11429
|
var logger19 = createLogger("agentforge:tools:agent:ask-human", { level: logLevel3 });
|
|
11375
|
-
function
|
|
11430
|
+
function isRecord4(value) {
|
|
11376
11431
|
return typeof value === "object" && value !== null;
|
|
11377
11432
|
}
|
|
11378
11433
|
function resolveInterrupt(module) {
|
|
11379
|
-
if (!
|
|
11434
|
+
if (!isRecord4(module)) {
|
|
11380
11435
|
return void 0;
|
|
11381
11436
|
}
|
|
11382
11437
|
const candidate = module.interrupt;
|