@agentforge/tools 0.12.4 → 0.12.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.cjs CHANGED
@@ -1,13 +1,14 @@
1
1
  'use strict';
2
2
 
3
3
  var core = require('@agentforge/core');
4
- var axios12 = require('axios');
4
+ var axios16 = require('axios');
5
5
  var zod = require('zod');
6
6
  var cheerio = require('cheerio');
7
7
  var webApi = require('@slack/web-api');
8
8
  var sync = require('csv-parse/sync');
9
9
  var sync$1 = require('csv-stringify/sync');
10
10
  var fastXmlParser = require('fast-xml-parser');
11
+ var neo4j = require('neo4j-driver');
11
12
  var fs = require('fs');
12
13
  var path7 = require('path');
13
14
  var dateFns = require('date-fns');
@@ -33,8 +34,9 @@ function _interopNamespace(e) {
33
34
  return Object.freeze(n);
34
35
  }
35
36
 
36
- var axios12__default = /*#__PURE__*/_interopDefault(axios12);
37
+ var axios16__default = /*#__PURE__*/_interopDefault(axios16);
37
38
  var cheerio__namespace = /*#__PURE__*/_interopNamespace(cheerio);
39
+ var neo4j__default = /*#__PURE__*/_interopDefault(neo4j);
38
40
  var path7__namespace = /*#__PURE__*/_interopNamespace(path7);
39
41
 
40
42
  // src/web/http/tools/http-client.ts
@@ -71,7 +73,7 @@ function createHttpClientTool(defaultTimeout = 3e4, defaultHeaders = {}) {
71
73
  validateStatus: () => true
72
74
  // Don't throw on any status code
73
75
  };
74
- const response = await axios12__default.default(config);
76
+ const response = await axios16__default.default(config);
75
77
  return {
76
78
  status: response.status,
77
79
  statusText: response.statusText,
@@ -84,7 +86,7 @@ function createHttpClientTool(defaultTimeout = 3e4, defaultHeaders = {}) {
84
86
  }
85
87
  function createHttpGetTool(defaultTimeout = 3e4, defaultHeaders = {}) {
86
88
  return core.toolBuilder().name("http-get").description("Make a simple HTTP GET request to a URL and return the response data.").category(core.ToolCategory.WEB).tags(["http", "get", "fetch", "web"]).schema(httpGetSchema).implement(async (input) => {
87
- const response = await axios12__default.default.get(input.url, {
89
+ const response = await axios16__default.default.get(input.url, {
88
90
  headers: { ...defaultHeaders, ...input.headers },
89
91
  params: input.params,
90
92
  timeout: defaultTimeout
@@ -94,7 +96,7 @@ function createHttpGetTool(defaultTimeout = 3e4, defaultHeaders = {}) {
94
96
  }
95
97
  function createHttpPostTool(defaultTimeout = 3e4, defaultHeaders = {}) {
96
98
  return core.toolBuilder().name("http-post").description("Make a simple HTTP POST request with JSON body and return the response data.").category(core.ToolCategory.WEB).tags(["http", "post", "api", "web"]).schema(httpPostSchema).implement(async (input) => {
97
- const response = await axios12__default.default.post(input.url, input.body, {
99
+ const response = await axios16__default.default.post(input.url, input.body, {
98
100
  headers: {
99
101
  "Content-Type": "application/json",
100
102
  ...defaultHeaders,
@@ -131,7 +133,7 @@ var webScraperSchema = zod.z.object({
131
133
  });
132
134
  function createWebScraperTool(defaultTimeout = 3e4, userAgent = "Mozilla/5.0 (compatible; AgentForge/1.0; +https://agentforge.dev)") {
133
135
  return core.toolBuilder().name("web-scraper").description("Scrape and extract data from web pages. Can extract text, HTML, links, images, and use CSS selectors to target specific elements.").category(core.ToolCategory.WEB).tags(["scraper", "web", "html", "extract", "parse"]).schema(webScraperSchema).implement(async (input) => {
134
- const response = await axios12__default.default.get(input.url, {
136
+ const response = await axios16__default.default.get(input.url, {
135
137
  timeout: input.timeout || defaultTimeout,
136
138
  headers: {
137
139
  "User-Agent": userAgent
@@ -521,7 +523,7 @@ var DuckDuckGoProvider = class {
521
523
  async search(query, maxResults, timeout = DEFAULT_TIMEOUT) {
522
524
  return retryWithBackoff(async () => {
523
525
  try {
524
- const response = await axios12__default.default.get(
526
+ const response = await axios16__default.default.get(
525
527
  "https://api.duckduckgo.com/",
526
528
  {
527
529
  params: {
@@ -625,7 +627,7 @@ var SerperProvider = class {
625
627
  }
626
628
  return retryWithBackoff(async () => {
627
629
  try {
628
- const response = await axios12__default.default.post(
630
+ const response = await axios16__default.default.post(
629
631
  "https://google.serper.dev/search",
630
632
  {
631
633
  q: query,
@@ -805,7 +807,7 @@ function getDefaultSlackClient() {
805
807
  }
806
808
  };
807
809
  }
808
- function createSendSlackMessageTool(getSlackClient, logger4) {
810
+ function createSendSlackMessageTool(getSlackClient, logger8) {
809
811
  return core.toolBuilder().name("send-slack-message").description("Send a message to a Slack channel for team communication and notifications").category(core.ToolCategory.WEB).tags(["slack", "messaging", "communication"]).usageNotes(
810
812
  "Use this for general team communication. For notifications with @mentions, consider using notify-slack instead. Use get-slack-channels first if you need to find the right channel."
811
813
  ).suggests(["get-slack-channels"]).schema(
@@ -814,7 +816,7 @@ function createSendSlackMessageTool(getSlackClient, logger4) {
814
816
  message: zod.z.string().describe("Message content to send")
815
817
  })
816
818
  ).implementSafe(async ({ channel, message }) => {
817
- logger4.info("send-slack-message called", { channel, messageLength: message.length });
819
+ logger8.info("send-slack-message called", { channel, messageLength: message.length });
818
820
  try {
819
821
  const { client: slack, config } = getSlackClient();
820
822
  const result = await slack.chat.postMessage({
@@ -823,7 +825,7 @@ function createSendSlackMessageTool(getSlackClient, logger4) {
823
825
  username: config.botName,
824
826
  icon_emoji: config.botIcon
825
827
  });
826
- logger4.info("send-slack-message result", {
828
+ logger8.info("send-slack-message result", {
827
829
  channel: result.channel,
828
830
  timestamp: result.ts,
829
831
  messageLength: message.length,
@@ -836,7 +838,7 @@ function createSendSlackMessageTool(getSlackClient, logger4) {
836
838
  message_id: result.ts
837
839
  };
838
840
  } catch (error) {
839
- logger4.error("send-slack-message failed", {
841
+ logger8.error("send-slack-message failed", {
840
842
  channel,
841
843
  error: error.message,
842
844
  data: error.data
@@ -845,7 +847,7 @@ function createSendSlackMessageTool(getSlackClient, logger4) {
845
847
  }
846
848
  }).build();
847
849
  }
848
- function createNotifySlackTool(getSlackClient, logger4) {
850
+ function createNotifySlackTool(getSlackClient, logger8) {
849
851
  return core.toolBuilder().name("notify-slack").description("Send a notification to a Slack channel with optional @mentions for urgent alerts").category(core.ToolCategory.WEB).tags(["slack", "notification", "alert"]).usageNotes(
850
852
  "Use this for urgent notifications that require @mentions. For general messages without mentions, use send-slack-message instead."
851
853
  ).suggests(["get-slack-channels"]).schema(
@@ -855,7 +857,7 @@ function createNotifySlackTool(getSlackClient, logger4) {
855
857
  mentions: zod.z.array(zod.z.string()).optional().describe("List of usernames to mention (without @)")
856
858
  })
857
859
  ).implementSafe(async ({ channel, message, mentions = [] }) => {
858
- logger4.info("notify-slack called", {
860
+ logger8.info("notify-slack called", {
859
861
  channel,
860
862
  messageLength: message.length,
861
863
  mentionCount: mentions.length
@@ -869,7 +871,7 @@ function createNotifySlackTool(getSlackClient, logger4) {
869
871
  username: config.botName,
870
872
  icon_emoji: config.botIcon
871
873
  });
872
- logger4.info("notify-slack result", {
874
+ logger8.info("notify-slack result", {
873
875
  channel: result.channel,
874
876
  timestamp: result.ts,
875
877
  mentions: mentions.length
@@ -883,7 +885,7 @@ function createNotifySlackTool(getSlackClient, logger4) {
883
885
  };
884
886
  }).build();
885
887
  }
886
- function createGetSlackChannelsTool(getSlackClient, logger4) {
888
+ function createGetSlackChannelsTool(getSlackClient, logger8) {
887
889
  return core.toolBuilder().name("get-slack-channels").description("Get a list of available Slack channels to find the right channel for messaging").category(core.ToolCategory.WEB).tags(["slack", "channels", "list"]).usageNotes(
888
890
  "Use this first to discover available channels before sending messages. Helps ensure you are sending to the correct channel."
889
891
  ).follows(["send-slack-message", "notify-slack"]).schema(
@@ -891,7 +893,7 @@ function createGetSlackChannelsTool(getSlackClient, logger4) {
891
893
  include_private: zod.z.boolean().optional().describe("Include private channels (default: false)")
892
894
  })
893
895
  ).implementSafe(async ({ include_private = false }) => {
894
- logger4.info("get-slack-channels called", { include_private });
896
+ logger8.info("get-slack-channels called", { include_private });
895
897
  const { client: slack } = getSlackClient();
896
898
  const publicChannels = await slack.conversations.list({
897
899
  types: "public_channel",
@@ -905,7 +907,7 @@ function createGetSlackChannelsTool(getSlackClient, logger4) {
905
907
  });
906
908
  allChannels = [...allChannels, ...privateChannels.channels || []];
907
909
  }
908
- logger4.info("get-slack-channels result", {
910
+ logger8.info("get-slack-channels result", {
909
911
  channelCount: allChannels.length,
910
912
  includePrivate: include_private
911
913
  });
@@ -920,7 +922,7 @@ function createGetSlackChannelsTool(getSlackClient, logger4) {
920
922
  };
921
923
  }).build();
922
924
  }
923
- function createGetSlackMessagesTool(getSlackClient, logger4) {
925
+ function createGetSlackMessagesTool(getSlackClient, logger8) {
924
926
  return core.toolBuilder().name("get-slack-messages").description("Retrieve message history from a Slack channel to read recent conversations").category(core.ToolCategory.WEB).tags(["slack", "messages", "history", "read"]).usageNotes(
925
927
  "Use this to read recent messages from a channel. Use get-slack-channels first if you need to find the channel ID. Returns messages in reverse chronological order (newest first)."
926
928
  ).suggests(["get-slack-channels"]).schema(
@@ -929,7 +931,7 @@ function createGetSlackMessagesTool(getSlackClient, logger4) {
929
931
  limit: zod.z.number().int().min(1).max(100).optional().describe("Number of messages to retrieve (default: 20, max: 100)")
930
932
  })
931
933
  ).implementSafe(async ({ channel, limit = 20 }) => {
932
- logger4.info("get-slack-messages called", { channel, limit });
934
+ logger8.info("get-slack-messages called", { channel, limit });
933
935
  try {
934
936
  const { client: slack } = getSlackClient();
935
937
  let channelId = channel;
@@ -940,7 +942,7 @@ function createGetSlackMessagesTool(getSlackClient, logger4) {
940
942
  });
941
943
  const found = channels.channels?.find((c) => c.name === channel);
942
944
  if (!found) {
943
- logger4.error("get-slack-messages: channel not found", { channel });
945
+ logger8.error("get-slack-messages: channel not found", { channel });
944
946
  throw new Error(
945
947
  `Channel '${channel}' not found. Use get-slack-channels to see available channels.`
946
948
  );
@@ -952,7 +954,7 @@ function createGetSlackMessagesTool(getSlackClient, logger4) {
952
954
  limit: Math.min(limit, 100)
953
955
  // Cap at 100 for performance
954
956
  });
955
- logger4.info("get-slack-messages result", {
957
+ logger8.info("get-slack-messages result", {
956
958
  channel: channelId,
957
959
  messageCount: result.messages?.length || 0,
958
960
  limit
@@ -970,7 +972,7 @@ function createGetSlackMessagesTool(getSlackClient, logger4) {
970
972
  })) || []
971
973
  };
972
974
  } catch (error) {
973
- logger4.error("get-slack-messages failed", {
975
+ logger8.error("get-slack-messages failed", {
974
976
  channel,
975
977
  error: error.message,
976
978
  data: error.data
@@ -1033,8 +1035,8 @@ function createGetConfiguredAuth(apiKey, email, siteUrl) {
1033
1035
  function createGetConfiguredAuthHeader(getConfiguredAuth) {
1034
1036
  return function getConfiguredAuthHeader() {
1035
1037
  const { ATLASSIAN_API_KEY, ATLASSIAN_EMAIL } = getConfiguredAuth();
1036
- const auth = Buffer.from(`${ATLASSIAN_EMAIL}:${ATLASSIAN_API_KEY}`).toString("base64");
1037
- return `Basic ${auth}`;
1038
+ const auth2 = Buffer.from(`${ATLASSIAN_EMAIL}:${ATLASSIAN_API_KEY}`).toString("base64");
1039
+ return `Basic ${auth2}`;
1038
1040
  };
1039
1041
  }
1040
1042
  function getConfig() {
@@ -1048,18 +1050,18 @@ function getConfig() {
1048
1050
  }
1049
1051
  function getAuthHeader() {
1050
1052
  const { ATLASSIAN_API_KEY, ATLASSIAN_EMAIL } = getConfig();
1051
- const auth = Buffer.from(`${ATLASSIAN_EMAIL}:${ATLASSIAN_API_KEY}`).toString("base64");
1052
- return `Basic ${auth}`;
1053
+ const auth2 = Buffer.from(`${ATLASSIAN_EMAIL}:${ATLASSIAN_API_KEY}`).toString("base64");
1054
+ return `Basic ${auth2}`;
1053
1055
  }
1054
- function createSearchConfluenceTool(getAuth, getAuthHeader2, logger4) {
1056
+ function createSearchConfluenceTool(getAuth, getAuthHeader2, logger8) {
1055
1057
  return core.toolBuilder().name("search-confluence").description("Search for pages in Confluence using keywords or CQL (Confluence Query Language). Returns matching pages with titles, IDs, and excerpts.").category(core.ToolCategory.WEB).tag("confluence").tag("search").tag("knowledge-base").usageNotes("Use this to find relevant documentation, policies, or information in Confluence. You can search by keywords or use CQL for advanced queries (e.g., 'space=AI AND type=page'). Use get-confluence-page to retrieve full content of specific pages.").suggests(["get-confluence-page"]).schema(zod.z.object({
1056
1058
  query: zod.z.string().describe("Search query or CQL expression (e.g., 'payment processing' or 'space=BL3 AND title~payment')"),
1057
1059
  limit: zod.z.number().optional().describe("Maximum number of results to return (default: 10, max: 25)")
1058
1060
  })).implement(async ({ query, limit = 10 }) => {
1059
- logger4.info("search-confluence called", { query, limit });
1061
+ logger8.info("search-confluence called", { query, limit });
1060
1062
  try {
1061
1063
  const { ATLASSIAN_SITE_URL } = getAuth();
1062
- const response = await axios12__default.default.get(`${ATLASSIAN_SITE_URL}/wiki/rest/api/content/search`, {
1064
+ const response = await axios16__default.default.get(`${ATLASSIAN_SITE_URL}/wiki/rest/api/content/search`, {
1063
1065
  headers: {
1064
1066
  Authorization: getAuthHeader2(),
1065
1067
  Accept: "application/json"
@@ -1081,13 +1083,13 @@ function createSearchConfluenceTool(getAuth, getAuthHeader2, logger4) {
1081
1083
  lastModified: page.version?.when || ""
1082
1084
  }));
1083
1085
  if (results.length === 0) {
1084
- logger4.warn("search-confluence returned NO RESULTS - this is a valid outcome, agent should not retry", {
1086
+ logger8.warn("search-confluence returned NO RESULTS - this is a valid outcome, agent should not retry", {
1085
1087
  query,
1086
1088
  limit,
1087
1089
  totalSize: response.data.totalSize
1088
1090
  });
1089
1091
  } else {
1090
- logger4.info("search-confluence result", {
1092
+ logger8.info("search-confluence result", {
1091
1093
  query,
1092
1094
  resultCount: results.length,
1093
1095
  totalSize: response.data.totalSize,
@@ -1102,7 +1104,7 @@ function createSearchConfluenceTool(getAuth, getAuthHeader2, logger4) {
1102
1104
  results
1103
1105
  });
1104
1106
  } catch (error) {
1105
- logger4.error("search-confluence error", {
1107
+ logger8.error("search-confluence error", {
1106
1108
  query,
1107
1109
  error: error.response?.data?.message || error.message,
1108
1110
  status: error.response?.status
@@ -1114,14 +1116,14 @@ function createSearchConfluenceTool(getAuth, getAuthHeader2, logger4) {
1114
1116
  }
1115
1117
  }).build();
1116
1118
  }
1117
- function createGetConfluencePageTool(getAuth, getAuthHeader2, logger4) {
1119
+ function createGetConfluencePageTool(getAuth, getAuthHeader2, logger8) {
1118
1120
  return core.toolBuilder().name("get-confluence-page").description("Get the full content of a specific Confluence page by its ID. Returns the page title, content (in storage format), space, and metadata.").category(core.ToolCategory.WEB).tag("confluence").tag("page").tag("content").usageNotes("Use this after search-confluence to retrieve the full content of a specific page. The page ID can be found in search results.").requires(["search-confluence"]).schema(zod.z.object({
1119
1121
  page_id: zod.z.string().describe("The Confluence page ID (from search results)")
1120
1122
  })).implement(async ({ page_id }) => {
1121
- logger4.info("get-confluence-page called", { page_id });
1123
+ logger8.info("get-confluence-page called", { page_id });
1122
1124
  try {
1123
1125
  const { ATLASSIAN_SITE_URL } = getAuth();
1124
- const response = await axios12__default.default.get(`${ATLASSIAN_SITE_URL}/wiki/rest/api/content/${page_id}`, {
1126
+ const response = await axios16__default.default.get(`${ATLASSIAN_SITE_URL}/wiki/rest/api/content/${page_id}`, {
1125
1127
  headers: {
1126
1128
  Authorization: getAuthHeader2(),
1127
1129
  Accept: "application/json"
@@ -1131,7 +1133,7 @@ function createGetConfluencePageTool(getAuth, getAuthHeader2, logger4) {
1131
1133
  }
1132
1134
  });
1133
1135
  const page = response.data;
1134
- logger4.info("get-confluence-page result", {
1136
+ logger8.info("get-confluence-page result", {
1135
1137
  page_id,
1136
1138
  title: page.title,
1137
1139
  space: page.space?.name,
@@ -1153,7 +1155,7 @@ function createGetConfluencePageTool(getAuth, getAuthHeader2, logger4) {
1153
1155
  }
1154
1156
  });
1155
1157
  } catch (error) {
1156
- logger4.error("get-confluence-page error", {
1158
+ logger8.error("get-confluence-page error", {
1157
1159
  page_id,
1158
1160
  error: error.response?.data?.message || error.message,
1159
1161
  status: error.response?.status
@@ -1165,14 +1167,14 @@ function createGetConfluencePageTool(getAuth, getAuthHeader2, logger4) {
1165
1167
  }
1166
1168
  }).build();
1167
1169
  }
1168
- function createListConfluenceSpacesTool(getAuth, getAuthHeader2, logger4) {
1170
+ function createListConfluenceSpacesTool(getAuth, getAuthHeader2, logger8) {
1169
1171
  return core.toolBuilder().name("list-confluence-spaces").description("List all available Confluence spaces. Returns space names, keys, types, and descriptions to help identify where to search for information.").category(core.ToolCategory.WEB).tag("confluence").tag("spaces").tag("list").usageNotes("Use this first to discover available spaces before searching. Helps narrow down searches to specific areas (e.g., 'AI', 'BL3', 'Finance').").follows(["search-confluence"]).schema(zod.z.object({
1170
1172
  limit: zod.z.number().optional().describe("Maximum number of spaces to return (default: 25)")
1171
1173
  })).implement(async ({ limit = 25 }) => {
1172
- logger4.info("list-confluence-spaces called", { limit });
1174
+ logger8.info("list-confluence-spaces called", { limit });
1173
1175
  try {
1174
1176
  const { ATLASSIAN_SITE_URL } = getAuth();
1175
- const response = await axios12__default.default.get(`${ATLASSIAN_SITE_URL}/wiki/rest/api/space`, {
1177
+ const response = await axios16__default.default.get(`${ATLASSIAN_SITE_URL}/wiki/rest/api/space`, {
1176
1178
  headers: {
1177
1179
  Authorization: getAuthHeader2(),
1178
1180
  Accept: "application/json"
@@ -1188,7 +1190,7 @@ function createListConfluenceSpacesTool(getAuth, getAuthHeader2, logger4) {
1188
1190
  description: space.description?.plain?.value || "",
1189
1191
  url: `${ATLASSIAN_SITE_URL}/wiki${space._links.webui}`
1190
1192
  }));
1191
- logger4.info("list-confluence-spaces result", {
1193
+ logger8.info("list-confluence-spaces result", {
1192
1194
  spaceCount: spaces.length,
1193
1195
  spaceKeys: spaces.map((s) => s.key).slice(0, 5)
1194
1196
  // Log first 5 space keys
@@ -1199,7 +1201,7 @@ function createListConfluenceSpacesTool(getAuth, getAuthHeader2, logger4) {
1199
1201
  spaces
1200
1202
  });
1201
1203
  } catch (error) {
1202
- logger4.error("list-confluence-spaces error", {
1204
+ logger8.error("list-confluence-spaces error", {
1203
1205
  error: error.response?.data?.message || error.message,
1204
1206
  status: error.response?.status
1205
1207
  });
@@ -1210,15 +1212,15 @@ function createListConfluenceSpacesTool(getAuth, getAuthHeader2, logger4) {
1210
1212
  }
1211
1213
  }).build();
1212
1214
  }
1213
- function createGetSpacePagesTool(getAuth, getAuthHeader2, logger4) {
1215
+ function createGetSpacePagesTool(getAuth, getAuthHeader2, logger8) {
1214
1216
  return core.toolBuilder().name("get-space-pages").description("Get all pages from a specific Confluence space by space key. Useful for browsing content in a particular area.").category(core.ToolCategory.WEB).tag("confluence").tag("space").tag("pages").usageNotes("Use this to explore all pages in a specific space. Get the space key from list-confluence-spaces first.").requires(["list-confluence-spaces"]).schema(zod.z.object({
1215
1217
  space_key: zod.z.string().describe("The space key (e.g., 'AI', 'BL3', 'FIN')"),
1216
1218
  limit: zod.z.number().optional().describe("Maximum number of pages to return (default: 25)")
1217
1219
  })).implement(async ({ space_key, limit = 25 }) => {
1218
- logger4.info("get-space-pages called", { space_key, limit });
1220
+ logger8.info("get-space-pages called", { space_key, limit });
1219
1221
  try {
1220
1222
  const { ATLASSIAN_SITE_URL } = getAuth();
1221
- const response = await axios12__default.default.get(`${ATLASSIAN_SITE_URL}/wiki/rest/api/content`, {
1223
+ const response = await axios16__default.default.get(`${ATLASSIAN_SITE_URL}/wiki/rest/api/content`, {
1222
1224
  headers: {
1223
1225
  Authorization: getAuthHeader2(),
1224
1226
  Accept: "application/json"
@@ -1237,12 +1239,12 @@ function createGetSpacePagesTool(getAuth, getAuthHeader2, logger4) {
1237
1239
  lastModified: page.version?.when || ""
1238
1240
  }));
1239
1241
  if (pages.length === 0) {
1240
- logger4.warn("get-space-pages returned NO PAGES - this is a valid outcome, agent should not retry", {
1242
+ logger8.warn("get-space-pages returned NO PAGES - this is a valid outcome, agent should not retry", {
1241
1243
  space_key,
1242
1244
  limit
1243
1245
  });
1244
1246
  } else {
1245
- logger4.info("get-space-pages result", {
1247
+ logger8.info("get-space-pages result", {
1246
1248
  space_key,
1247
1249
  pageCount: pages.length,
1248
1250
  titles: pages.map((p) => p.title).slice(0, 3)
@@ -1256,7 +1258,7 @@ function createGetSpacePagesTool(getAuth, getAuthHeader2, logger4) {
1256
1258
  pages
1257
1259
  });
1258
1260
  } catch (error) {
1259
- logger4.error("get-space-pages error", {
1261
+ logger8.error("get-space-pages error", {
1260
1262
  space_key,
1261
1263
  error: error.response?.data?.message || error.message,
1262
1264
  status: error.response?.status
@@ -1268,14 +1270,14 @@ function createGetSpacePagesTool(getAuth, getAuthHeader2, logger4) {
1268
1270
  }
1269
1271
  }).build();
1270
1272
  }
1271
- function createCreateConfluencePageTool(getAuth, getAuthHeader2, logger4) {
1273
+ function createCreateConfluencePageTool(getAuth, getAuthHeader2, logger8) {
1272
1274
  return core.toolBuilder().name("create-confluence-page").description("Create a new page in a Confluence space. Requires space key, page title, and content (in HTML storage format).").category(core.ToolCategory.WEB).tag("confluence").tag("create").tag("write").usageNotes("Use this to create new documentation pages. Content should be in Confluence storage format (HTML). Get the space key from list-confluence-spaces first. Be mindful of creating duplicate pages.").requires(["list-confluence-spaces"]).schema(zod.z.object({
1273
1275
  space_key: zod.z.string().describe("The space key where the page will be created (e.g., 'AI', 'BL3')"),
1274
1276
  title: zod.z.string().describe("The title of the new page"),
1275
1277
  content: zod.z.string().describe("The page content in HTML format (Confluence storage format)"),
1276
1278
  parent_page_id: zod.z.string().optional().describe("Optional parent page ID to create this as a child page")
1277
1279
  })).implement(async ({ space_key, title, content, parent_page_id }) => {
1278
- logger4.info("create-confluence-page called", { space_key, title, hasParent: !!parent_page_id });
1280
+ logger8.info("create-confluence-page called", { space_key, title, hasParent: !!parent_page_id });
1279
1281
  try {
1280
1282
  const { ATLASSIAN_SITE_URL } = getAuth();
1281
1283
  const pageData = {
@@ -1292,7 +1294,7 @@ function createCreateConfluencePageTool(getAuth, getAuthHeader2, logger4) {
1292
1294
  if (parent_page_id) {
1293
1295
  pageData.ancestors = [{ id: parent_page_id }];
1294
1296
  }
1295
- const response = await axios12__default.default.post(
1297
+ const response = await axios16__default.default.post(
1296
1298
  `${ATLASSIAN_SITE_URL}/wiki/rest/api/content`,
1297
1299
  pageData,
1298
1300
  {
@@ -1302,7 +1304,7 @@ function createCreateConfluencePageTool(getAuth, getAuthHeader2, logger4) {
1302
1304
  }
1303
1305
  }
1304
1306
  );
1305
- logger4.info("create-confluence-page result", {
1307
+ logger8.info("create-confluence-page result", {
1306
1308
  page_id: response.data.id,
1307
1309
  title: response.data.title,
1308
1310
  space: space_key
@@ -1318,7 +1320,7 @@ function createCreateConfluencePageTool(getAuth, getAuthHeader2, logger4) {
1318
1320
  }
1319
1321
  });
1320
1322
  } catch (error) {
1321
- logger4.error("create-confluence-page error", {
1323
+ logger8.error("create-confluence-page error", {
1322
1324
  space_key,
1323
1325
  title,
1324
1326
  error: error.response?.data?.message || error.message,
@@ -1331,16 +1333,16 @@ function createCreateConfluencePageTool(getAuth, getAuthHeader2, logger4) {
1331
1333
  }
1332
1334
  }).build();
1333
1335
  }
1334
- function createUpdateConfluencePageTool(getAuth, getAuthHeader2, logger4) {
1336
+ function createUpdateConfluencePageTool(getAuth, getAuthHeader2, logger8) {
1335
1337
  return core.toolBuilder().name("update-confluence-page").description("Update an existing Confluence page's content. Requires page ID, new title, and new content.").category(core.ToolCategory.WEB).tag("confluence").tag("update").tag("write").usageNotes("Use this to update existing documentation. You must provide the page ID (from search results). The tool will automatically handle version incrementing. Always get the current page content first to avoid overwriting important information.").requires(["get-confluence-page"]).schema(zod.z.object({
1336
1338
  page_id: zod.z.string().describe("The ID of the page to update"),
1337
1339
  title: zod.z.string().describe("The new title for the page"),
1338
1340
  content: zod.z.string().describe("The new content in HTML format (Confluence storage format)")
1339
1341
  })).implement(async ({ page_id, title, content }) => {
1340
- logger4.info("update-confluence-page called", { page_id, title });
1342
+ logger8.info("update-confluence-page called", { page_id, title });
1341
1343
  try {
1342
1344
  const { ATLASSIAN_SITE_URL } = getAuth();
1343
- const getResponse = await axios12__default.default.get(
1345
+ const getResponse = await axios16__default.default.get(
1344
1346
  `${ATLASSIAN_SITE_URL}/wiki/rest/api/content/${page_id}`,
1345
1347
  {
1346
1348
  headers: {
@@ -1350,7 +1352,7 @@ function createUpdateConfluencePageTool(getAuth, getAuthHeader2, logger4) {
1350
1352
  }
1351
1353
  );
1352
1354
  const currentVersion = getResponse.data.version.number;
1353
- const updateResponse = await axios12__default.default.put(
1355
+ const updateResponse = await axios16__default.default.put(
1354
1356
  `${ATLASSIAN_SITE_URL}/wiki/rest/api/content/${page_id}`,
1355
1357
  {
1356
1358
  type: "page",
@@ -1370,7 +1372,7 @@ function createUpdateConfluencePageTool(getAuth, getAuthHeader2, logger4) {
1370
1372
  }
1371
1373
  }
1372
1374
  );
1373
- logger4.info("update-confluence-page result", {
1375
+ logger8.info("update-confluence-page result", {
1374
1376
  page_id,
1375
1377
  title: updateResponse.data.title,
1376
1378
  previousVersion: currentVersion,
@@ -1387,7 +1389,7 @@ function createUpdateConfluencePageTool(getAuth, getAuthHeader2, logger4) {
1387
1389
  }
1388
1390
  });
1389
1391
  } catch (error) {
1390
- logger4.error("update-confluence-page error", {
1392
+ logger8.error("update-confluence-page error", {
1391
1393
  page_id,
1392
1394
  title,
1393
1395
  error: error.response?.data?.message || error.message,
@@ -1400,15 +1402,15 @@ function createUpdateConfluencePageTool(getAuth, getAuthHeader2, logger4) {
1400
1402
  }
1401
1403
  }).build();
1402
1404
  }
1403
- function createArchiveConfluencePageTool(getAuth, getAuthHeader2, logger4) {
1405
+ function createArchiveConfluencePageTool(getAuth, getAuthHeader2, logger8) {
1404
1406
  return core.toolBuilder().name("archive-confluence-page").description("Archive a Confluence page by moving it to trash. The page can be restored by space admins. Note: UI may require a note explaining why the page was archived.").category(core.ToolCategory.WEB).tag("confluence").tag("archive").tag("delete").usageNotes("Use this to archive outdated or obsolete documentation. The page is moved to trash, not permanently deleted. Space admins can restore it if needed. Be very careful - only archive pages that are truly obsolete.").conflicts(["create-confluence-page"]).schema(zod.z.object({
1405
1407
  page_id: zod.z.string().describe("The ID of the page to archive"),
1406
1408
  reason: zod.z.string().optional().describe("Optional reason for archiving (for audit trail)")
1407
1409
  })).implement(async ({ page_id, reason }) => {
1408
- logger4.info("archive-confluence-page called", { page_id, reason });
1410
+ logger8.info("archive-confluence-page called", { page_id, reason });
1409
1411
  try {
1410
1412
  const { ATLASSIAN_SITE_URL } = getAuth();
1411
- const getResponse = await axios12__default.default.get(
1413
+ const getResponse = await axios16__default.default.get(
1412
1414
  `${ATLASSIAN_SITE_URL}/wiki/rest/api/content/${page_id}`,
1413
1415
  {
1414
1416
  headers: {
@@ -1419,7 +1421,7 @@ function createArchiveConfluencePageTool(getAuth, getAuthHeader2, logger4) {
1419
1421
  );
1420
1422
  const currentVersion = getResponse.data.version.number;
1421
1423
  const pageData = getResponse.data;
1422
- await axios12__default.default.put(
1424
+ await axios16__default.default.put(
1423
1425
  `${ATLASSIAN_SITE_URL}/wiki/rest/api/content/${page_id}`,
1424
1426
  {
1425
1427
  version: { number: currentVersion + 1 },
@@ -1436,7 +1438,7 @@ function createArchiveConfluencePageTool(getAuth, getAuthHeader2, logger4) {
1436
1438
  }
1437
1439
  }
1438
1440
  );
1439
- logger4.info("archive-confluence-page result", {
1441
+ logger8.info("archive-confluence-page result", {
1440
1442
  page_id,
1441
1443
  title: pageData.title,
1442
1444
  previousVersion: currentVersion,
@@ -1454,7 +1456,7 @@ function createArchiveConfluencePageTool(getAuth, getAuthHeader2, logger4) {
1454
1456
  }
1455
1457
  });
1456
1458
  } catch (error) {
1457
- logger4.error("archive-confluence-page error", {
1459
+ logger8.error("archive-confluence-page error", {
1458
1460
  page_id,
1459
1461
  error: error.response?.data?.message || error.message,
1460
1462
  status: error.response?.status
@@ -2051,6 +2053,1706 @@ function createTransformerTools(config = {}) {
2051
2053
  createObjectOmitTool()
2052
2054
  ];
2053
2055
  }
2056
+ var neo4jQuerySchema = zod.z.object({
2057
+ cypher: zod.z.string().describe("Cypher query to execute"),
2058
+ parameters: zod.z.record(zod.z.any()).optional().describe("Query parameters for parameterized queries"),
2059
+ database: zod.z.string().optional().describe("Database name (defaults to configured database)")
2060
+ });
2061
+ var neo4jGetSchemaSchema = zod.z.object({
2062
+ database: zod.z.string().optional().describe("Database name (defaults to configured database)")
2063
+ });
2064
+ var neo4jFindNodesSchema = zod.z.object({
2065
+ label: zod.z.string().describe("Node label to search for"),
2066
+ properties: zod.z.record(zod.z.any()).optional().describe("Properties to match (key-value pairs)"),
2067
+ limit: zod.z.number().default(100).describe("Maximum number of nodes to return"),
2068
+ database: zod.z.string().optional().describe("Database name (defaults to configured database)")
2069
+ });
2070
+ var neo4jTraverseSchema = zod.z.object({
2071
+ startNodeId: zod.z.union([
2072
+ zod.z.string().describe("Node ID as string"),
2073
+ zod.z.number().describe("Node ID as number")
2074
+ ]).describe("ID of the starting node"),
2075
+ relationshipType: zod.z.string().optional().describe("Type of relationship to follow (optional, follows all if not specified)"),
2076
+ direction: zod.z.enum(["outgoing", "incoming", "both"]).default("outgoing").describe("Direction to traverse"),
2077
+ maxDepth: zod.z.number().default(1).describe("Maximum depth to traverse"),
2078
+ limit: zod.z.number().default(100).describe("Maximum number of nodes to return"),
2079
+ database: zod.z.string().optional().describe("Database name (defaults to configured database)")
2080
+ });
2081
+ var neo4jVectorSearchSchema = zod.z.object({
2082
+ indexName: zod.z.string().describe("Name of the vector index to search"),
2083
+ queryVector: zod.z.array(zod.z.number().describe("Vector dimension value")).describe("Query vector for similarity search"),
2084
+ limit: zod.z.number().default(10).describe("Maximum number of results to return"),
2085
+ database: zod.z.string().optional().describe("Database name (defaults to configured database)")
2086
+ });
2087
+ var neo4jVectorSearchWithEmbeddingSchema = zod.z.object({
2088
+ indexName: zod.z.string().describe("Name of the vector index to search"),
2089
+ queryText: zod.z.string().describe("Text to generate embedding from for similarity search"),
2090
+ limit: zod.z.number().default(10).describe("Maximum number of results to return"),
2091
+ model: zod.z.string().optional().describe("Embedding model to use (defaults to configured model)"),
2092
+ database: zod.z.string().optional().describe("Database name (defaults to configured database)")
2093
+ });
2094
+ var neo4jCreateNodeWithEmbeddingSchema = zod.z.object({
2095
+ label: zod.z.string().describe("Node label"),
2096
+ properties: zod.z.record(zod.z.string(), zod.z.any().describe("Property value")).describe("Node properties (key-value pairs)"),
2097
+ textProperty: zod.z.string().describe("Name of the property containing text to embed"),
2098
+ embeddingProperty: zod.z.string().default("embedding").describe("Name of the property to store the embedding vector"),
2099
+ model: zod.z.string().optional().describe("Embedding model to use (defaults to configured model)"),
2100
+ database: zod.z.string().optional().describe("Database name (defaults to configured database)")
2101
+ });
2102
+ var logger3 = core.createLogger("agentforge:tools:neo4j:pool");
2103
+ var Neo4jConnectionPool = class {
2104
+ driver = null;
2105
+ config = null;
2106
+ /**
2107
+ * Initialize the connection pool
2108
+ */
2109
+ async initialize(config) {
2110
+ const startTime = Date.now();
2111
+ logger3.debug("Initializing Neo4j connection pool", {
2112
+ uri: config.uri,
2113
+ username: config.username,
2114
+ database: config.database || "neo4j",
2115
+ maxConnectionPoolSize: config.maxConnectionPoolSize || 50,
2116
+ connectionTimeout: config.connectionTimeout || 3e4
2117
+ });
2118
+ if (this.driver) {
2119
+ logger3.debug("Closing existing connection before reinitializing");
2120
+ await this.close();
2121
+ }
2122
+ this.config = config;
2123
+ this.driver = neo4j__default.default.driver(
2124
+ config.uri,
2125
+ neo4j.auth.basic(config.username, config.password),
2126
+ {
2127
+ maxConnectionPoolSize: config.maxConnectionPoolSize || 50,
2128
+ connectionTimeout: config.connectionTimeout || 3e4
2129
+ }
2130
+ );
2131
+ await this.verifyConnectivity();
2132
+ logger3.info("Neo4j connection pool initialized", {
2133
+ uri: config.uri,
2134
+ database: config.database || "neo4j",
2135
+ duration: Date.now() - startTime
2136
+ });
2137
+ }
2138
+ /**
2139
+ * Verify connectivity to Neo4j
2140
+ */
2141
+ async verifyConnectivity() {
2142
+ if (!this.driver) {
2143
+ const error = "Neo4j driver not initialized";
2144
+ logger3.error(error);
2145
+ throw new Error(error);
2146
+ }
2147
+ logger3.debug("Verifying Neo4j connectivity");
2148
+ try {
2149
+ await this.driver.verifyConnectivity();
2150
+ logger3.debug("Neo4j connectivity verified");
2151
+ } catch (error) {
2152
+ const errorMessage = `Failed to connect to Neo4j: ${error instanceof Error ? error.message : "Unknown error"}`;
2153
+ logger3.error("Neo4j connectivity verification failed", {
2154
+ error: error instanceof Error ? error.message : "Unknown error",
2155
+ uri: this.config?.uri
2156
+ });
2157
+ throw new Error(errorMessage);
2158
+ }
2159
+ }
2160
+ /**
2161
+ * Get a session for executing queries
2162
+ */
2163
+ getSession(database) {
2164
+ if (!this.driver) {
2165
+ throw new Error("Neo4j driver not initialized. Call initialize() first.");
2166
+ }
2167
+ return this.driver.session({
2168
+ database: database || this.config?.database || "neo4j"
2169
+ });
2170
+ }
2171
+ /**
2172
+ * Execute a query with automatic session management
2173
+ */
2174
+ async executeQuery(cypher, parameters, database) {
2175
+ const session = this.getSession(database);
2176
+ try {
2177
+ const result = await session.run(cypher, parameters);
2178
+ return result.records.map((record) => record.toObject());
2179
+ } finally {
2180
+ await session.close();
2181
+ }
2182
+ }
2183
+ /**
2184
+ * Execute a read query
2185
+ */
2186
+ async executeReadQuery(cypher, parameters, database) {
2187
+ const session = this.getSession(database);
2188
+ try {
2189
+ const result = await session.executeRead((tx) => tx.run(cypher, parameters));
2190
+ return result.records.map((record) => record.toObject());
2191
+ } finally {
2192
+ await session.close();
2193
+ }
2194
+ }
2195
+ /**
2196
+ * Execute a write query
2197
+ */
2198
+ async executeWriteQuery(cypher, parameters, database) {
2199
+ const session = this.getSession(database);
2200
+ try {
2201
+ const result = await session.executeWrite((tx) => tx.run(cypher, parameters));
2202
+ return result.records.map((record) => record.toObject());
2203
+ } finally {
2204
+ await session.close();
2205
+ }
2206
+ }
2207
+ /**
2208
+ * Check if driver is initialized
2209
+ */
2210
+ isInitialized() {
2211
+ return this.driver !== null;
2212
+ }
2213
+ /**
2214
+ * Get current configuration
2215
+ */
2216
+ getConfig() {
2217
+ return this.config;
2218
+ }
2219
+ /**
2220
+ * Close the connection pool
2221
+ */
2222
+ async close() {
2223
+ if (this.driver) {
2224
+ logger3.debug("Closing Neo4j connection pool");
2225
+ await this.driver.close();
2226
+ this.driver = null;
2227
+ this.config = null;
2228
+ logger3.info("Neo4j connection pool closed");
2229
+ }
2230
+ }
2231
+ };
2232
+ var neo4jPool = new Neo4jConnectionPool();
2233
+ async function initializeFromEnv() {
2234
+ if (!process.env.NEO4J_URI) {
2235
+ logger3.warn("NEO4J_URI environment variable not set, using default", {
2236
+ default: "bolt://localhost:7687"
2237
+ });
2238
+ }
2239
+ if (!process.env.NEO4J_USER) {
2240
+ logger3.warn("NEO4J_USER environment variable not set, using default", {
2241
+ default: "neo4j"
2242
+ });
2243
+ }
2244
+ if (!process.env.NEO4J_PASSWORD) {
2245
+ logger3.warn("NEO4J_PASSWORD environment variable not set, using default", {
2246
+ default: "password"
2247
+ });
2248
+ }
2249
+ if (!process.env.NEO4J_DATABASE) {
2250
+ logger3.debug("NEO4J_DATABASE environment variable not set, using default", {
2251
+ default: "neo4j"
2252
+ });
2253
+ }
2254
+ const config = {
2255
+ uri: process.env.NEO4J_URI || "bolt://localhost:7687",
2256
+ username: process.env.NEO4J_USER || "neo4j",
2257
+ password: process.env.NEO4J_PASSWORD || "password",
2258
+ database: process.env.NEO4J_DATABASE
2259
+ };
2260
+ await neo4jPool.initialize(config);
2261
+ }
2262
+
2263
+ // src/data/neo4j/embeddings/utils.ts
2264
+ var DEFAULT_RETRY_CONFIG2 = {
2265
+ maxRetries: 3,
2266
+ initialDelay: 1e3,
2267
+ // 1 second
2268
+ maxDelay: 1e4,
2269
+ // 10 seconds
2270
+ backoffMultiplier: 2
2271
+ };
2272
+ function isRetryableError2(error) {
2273
+ if (error.code === "ECONNRESET" || error.code === "ETIMEDOUT" || error.code === "ENOTFOUND") {
2274
+ return true;
2275
+ }
2276
+ if (error.code === "ECONNABORTED" || error.message?.includes("timeout")) {
2277
+ return true;
2278
+ }
2279
+ if (error.response?.status >= 500 && error.response?.status < 600) {
2280
+ return true;
2281
+ }
2282
+ if (error.response?.status === 429) {
2283
+ return true;
2284
+ }
2285
+ return false;
2286
+ }
2287
+ function sleep2(ms) {
2288
+ return new Promise((resolve2) => setTimeout(resolve2, ms));
2289
+ }
2290
+ async function retryWithBackoff2(fn, config = DEFAULT_RETRY_CONFIG2) {
2291
+ let lastError;
2292
+ let delay = config.initialDelay;
2293
+ for (let attempt = 0; attempt <= config.maxRetries; attempt++) {
2294
+ try {
2295
+ return await fn();
2296
+ } catch (error) {
2297
+ lastError = error;
2298
+ if (!isRetryableError2(error)) {
2299
+ throw error;
2300
+ }
2301
+ if (attempt === config.maxRetries) {
2302
+ break;
2303
+ }
2304
+ await sleep2(delay);
2305
+ delay = Math.min(delay * config.backoffMultiplier, config.maxDelay);
2306
+ }
2307
+ }
2308
+ throw lastError;
2309
+ }
2310
+ function getOpenAIApiKey() {
2311
+ return process.env.OPENAI_API_KEY;
2312
+ }
2313
+ function getCohereApiKey() {
2314
+ return process.env.COHERE_API_KEY;
2315
+ }
2316
+ function getHuggingFaceApiKey() {
2317
+ return process.env.HUGGINGFACE_API_KEY;
2318
+ }
2319
+ function getVoyageApiKey() {
2320
+ return process.env.VOYAGE_API_KEY;
2321
+ }
2322
+ function getOllamaBaseUrl() {
2323
+ return process.env.OLLAMA_BASE_URL || "http://localhost:11434";
2324
+ }
2325
+ function getEmbeddingProvider() {
2326
+ return process.env.EMBEDDING_PROVIDER || "openai";
2327
+ }
2328
+ function getEmbeddingModel() {
2329
+ return process.env.EMBEDDING_MODEL;
2330
+ }
2331
+ function validateText(text) {
2332
+ if (!text || text.trim().length === 0) {
2333
+ throw new Error("Text cannot be empty");
2334
+ }
2335
+ if (text.length > 5e4) {
2336
+ throw new Error("Text is too long for embedding generation (max ~50,000 characters)");
2337
+ }
2338
+ }
2339
+ function validateBatch(texts) {
2340
+ if (!texts || texts.length === 0) {
2341
+ throw new Error("Batch cannot be empty");
2342
+ }
2343
+ if (texts.length > 2048) {
2344
+ throw new Error("Batch size too large (max 2048 texts)");
2345
+ }
2346
+ texts.forEach((text, index) => {
2347
+ try {
2348
+ validateText(text);
2349
+ } catch (error) {
2350
+ throw new Error(`Invalid text at index ${index}: ${error instanceof Error ? error.message : "Unknown error"}`);
2351
+ }
2352
+ });
2353
+ }
2354
+ var OpenAIEmbeddingProvider = class {
2355
+ name = "openai";
2356
+ defaultModel = "text-embedding-3-small";
2357
+ apiKey;
2358
+ baseURL = "https://api.openai.com/v1";
2359
+ constructor(apiKey) {
2360
+ this.apiKey = apiKey || getOpenAIApiKey();
2361
+ }
2362
+ /**
2363
+ * Check if OpenAI is available (API key is set)
2364
+ */
2365
+ isAvailable() {
2366
+ return !!this.apiKey;
2367
+ }
2368
+ /**
2369
+ * Generate embedding for a single text
2370
+ */
2371
+ async generateEmbedding(text, model) {
2372
+ if (!this.apiKey) {
2373
+ throw new Error(
2374
+ "OpenAI API key not found. Set OPENAI_API_KEY environment variable. Get your key at https://platform.openai.com/api-keys"
2375
+ );
2376
+ }
2377
+ validateText(text);
2378
+ const modelToUse = model || this.defaultModel;
2379
+ return retryWithBackoff2(async () => {
2380
+ try {
2381
+ const response = await axios16__default.default.post(
2382
+ `${this.baseURL}/embeddings`,
2383
+ {
2384
+ input: text,
2385
+ model: modelToUse
2386
+ },
2387
+ {
2388
+ headers: {
2389
+ "Authorization": `Bearer ${this.apiKey}`,
2390
+ "Content-Type": "application/json"
2391
+ },
2392
+ timeout: 3e4
2393
+ // 30 seconds
2394
+ }
2395
+ );
2396
+ const data = response.data;
2397
+ if (!data.data || data.data.length === 0) {
2398
+ throw new Error("No embedding returned from OpenAI");
2399
+ }
2400
+ return {
2401
+ embedding: data.data[0].embedding,
2402
+ model: data.model,
2403
+ dimensions: data.data[0].embedding.length,
2404
+ usage: {
2405
+ promptTokens: data.usage.prompt_tokens,
2406
+ totalTokens: data.usage.total_tokens
2407
+ }
2408
+ };
2409
+ } catch (error) {
2410
+ let wrappedError;
2411
+ if (error.response?.status === 401) {
2412
+ wrappedError = new Error(
2413
+ "Invalid OpenAI API key. Get your key at https://platform.openai.com/api-keys"
2414
+ );
2415
+ } else if (error.response?.status === 429) {
2416
+ wrappedError = new Error(
2417
+ "OpenAI API rate limit exceeded. Please try again later or upgrade your plan."
2418
+ );
2419
+ } else if (error.response?.status === 400) {
2420
+ wrappedError = new Error(
2421
+ `OpenAI API error: ${error.response.data?.error?.message || "Bad request"}`
2422
+ );
2423
+ } else {
2424
+ wrappedError = new Error(`OpenAI embedding generation failed: ${error.message}`);
2425
+ }
2426
+ if (error.code) wrappedError.code = error.code;
2427
+ if (error.response) wrappedError.response = error.response;
2428
+ throw wrappedError;
2429
+ }
2430
+ });
2431
+ }
2432
+ /**
2433
+ * Generate embeddings for multiple texts (batch)
2434
+ */
2435
+ async generateBatchEmbeddings(texts, model) {
2436
+ if (!this.apiKey) {
2437
+ throw new Error(
2438
+ "OpenAI API key not found. Set OPENAI_API_KEY environment variable. Get your key at https://platform.openai.com/api-keys"
2439
+ );
2440
+ }
2441
+ validateBatch(texts);
2442
+ const modelToUse = model || this.defaultModel;
2443
+ return retryWithBackoff2(async () => {
2444
+ try {
2445
+ const response = await axios16__default.default.post(
2446
+ `${this.baseURL}/embeddings`,
2447
+ {
2448
+ input: texts,
2449
+ model: modelToUse
2450
+ },
2451
+ {
2452
+ headers: {
2453
+ "Authorization": `Bearer ${this.apiKey}`,
2454
+ "Content-Type": "application/json"
2455
+ },
2456
+ timeout: 6e4
2457
+ // 60 seconds for batch
2458
+ }
2459
+ );
2460
+ const data = response.data;
2461
+ if (!data.data || data.data.length === 0) {
2462
+ throw new Error("No embeddings returned from OpenAI");
2463
+ }
2464
+ const sortedData = data.data.sort((a, b) => a.index - b.index);
2465
+ return {
2466
+ embeddings: sortedData.map((item) => item.embedding),
2467
+ model: data.model,
2468
+ dimensions: sortedData[0].embedding.length,
2469
+ usage: {
2470
+ promptTokens: data.usage.prompt_tokens,
2471
+ totalTokens: data.usage.total_tokens
2472
+ }
2473
+ };
2474
+ } catch (error) {
2475
+ let wrappedError;
2476
+ if (error.response?.status === 401) {
2477
+ wrappedError = new Error(
2478
+ "Invalid OpenAI API key. Get your key at https://platform.openai.com/api-keys"
2479
+ );
2480
+ } else if (error.response?.status === 429) {
2481
+ wrappedError = new Error(
2482
+ "OpenAI API rate limit exceeded. Please try again later or upgrade your plan."
2483
+ );
2484
+ } else if (error.response?.status === 400) {
2485
+ wrappedError = new Error(
2486
+ `OpenAI API error: ${error.response.data?.error?.message || "Bad request"}`
2487
+ );
2488
+ } else {
2489
+ wrappedError = new Error(`OpenAI batch embedding generation failed: ${error.message}`);
2490
+ }
2491
+ if (error.code) wrappedError.code = error.code;
2492
+ if (error.response) wrappedError.response = error.response;
2493
+ throw wrappedError;
2494
+ }
2495
+ });
2496
+ }
2497
+ };
2498
+ var CohereEmbeddingProvider = class {
2499
+ name = "cohere";
2500
+ defaultModel = "embed-english-v3.0";
2501
+ apiKey;
2502
+ baseUrl = "https://api.cohere.ai/v1";
2503
+ constructor(apiKey, model) {
2504
+ this.apiKey = apiKey;
2505
+ }
2506
+ /**
2507
+ * Check if Cohere is available (API key is set)
2508
+ */
2509
+ isAvailable() {
2510
+ return !!this.apiKey;
2511
+ }
2512
+ async generateEmbedding(text, model) {
2513
+ const result = await this.generateBatchEmbeddings([text], model);
2514
+ return {
2515
+ embedding: result.embeddings[0],
2516
+ model: result.model,
2517
+ dimensions: result.dimensions,
2518
+ usage: result.usage
2519
+ };
2520
+ }
2521
+ async generateBatchEmbeddings(texts, model) {
2522
+ const modelToUse = model || this.defaultModel;
2523
+ return retryWithBackoff2(async () => {
2524
+ try {
2525
+ const response = await axios16__default.default.post(
2526
+ `${this.baseUrl}/embed`,
2527
+ {
2528
+ texts,
2529
+ model: modelToUse,
2530
+ input_type: "search_document",
2531
+ // For storing in vector DB
2532
+ embedding_types: ["float"]
2533
+ },
2534
+ {
2535
+ headers: {
2536
+ "Authorization": `Bearer ${this.apiKey}`,
2537
+ "Content-Type": "application/json"
2538
+ }
2539
+ }
2540
+ );
2541
+ const embeddings = response.data.embeddings.float;
2542
+ const dimensions = embeddings[0]?.length || 0;
2543
+ return {
2544
+ embeddings,
2545
+ model: modelToUse,
2546
+ dimensions,
2547
+ usage: response.data.meta?.billed_units ? {
2548
+ promptTokens: response.data.meta.billed_units.input_tokens || 0,
2549
+ totalTokens: response.data.meta.billed_units.input_tokens || 0
2550
+ } : void 0
2551
+ };
2552
+ } catch (error) {
2553
+ if (axios16__default.default.isAxiosError(error)) {
2554
+ const status = error.response?.status;
2555
+ const message = error.response?.data?.message || error.message;
2556
+ let wrappedError;
2557
+ if (status === 401) {
2558
+ wrappedError = new Error(`Cohere API authentication failed. Please check your COHERE_API_KEY. ${message}`);
2559
+ } else if (status === 429) {
2560
+ wrappedError = new Error(`Cohere API rate limit exceeded. ${message}`);
2561
+ } else if (status === 400) {
2562
+ wrappedError = new Error(`Cohere API request invalid: ${message}`);
2563
+ } else {
2564
+ wrappedError = new Error(`Cohere API error (${status}): ${message}`);
2565
+ }
2566
+ if (error.code) wrappedError.code = error.code;
2567
+ if (error.response) wrappedError.response = error.response;
2568
+ throw wrappedError;
2569
+ }
2570
+ throw error;
2571
+ }
2572
+ });
2573
+ }
2574
+ };
2575
+ var HuggingFaceEmbeddingProvider = class {
2576
+ name = "huggingface";
2577
+ defaultModel = "sentence-transformers/all-MiniLM-L6-v2";
2578
+ apiKey;
2579
+ baseUrl = "https://api-inference.huggingface.co/pipeline/feature-extraction";
2580
+ constructor(apiKey, model) {
2581
+ this.apiKey = apiKey;
2582
+ }
2583
+ /**
2584
+ * Check if HuggingFace is available (API key is set)
2585
+ */
2586
+ isAvailable() {
2587
+ return !!this.apiKey;
2588
+ }
2589
+ async generateEmbedding(text, model) {
2590
+ const modelToUse = model || this.defaultModel;
2591
+ return retryWithBackoff2(async () => {
2592
+ try {
2593
+ const response = await axios16__default.default.post(
2594
+ `${this.baseUrl}/${modelToUse}`,
2595
+ {
2596
+ inputs: text,
2597
+ options: {
2598
+ wait_for_model: true
2599
+ }
2600
+ },
2601
+ {
2602
+ headers: {
2603
+ "Authorization": `Bearer ${this.apiKey}`,
2604
+ "Content-Type": "application/json"
2605
+ }
2606
+ }
2607
+ );
2608
+ const embedding = Array.isArray(response.data) ? response.data : response.data[0];
2609
+ return {
2610
+ embedding,
2611
+ model: modelToUse,
2612
+ dimensions: embedding.length
2613
+ };
2614
+ } catch (error) {
2615
+ if (axios16__default.default.isAxiosError(error)) {
2616
+ const status = error.response?.status;
2617
+ const message = error.response?.data?.error || error.message;
2618
+ let wrappedError;
2619
+ if (status === 401) {
2620
+ wrappedError = new Error(`HuggingFace API authentication failed. Please check your HUGGINGFACE_API_KEY. ${message}`);
2621
+ } else if (status === 429) {
2622
+ wrappedError = new Error(`HuggingFace API rate limit exceeded. ${message}`);
2623
+ } else if (status === 400) {
2624
+ wrappedError = new Error(`HuggingFace API request invalid: ${message}`);
2625
+ } else if (status === 503) {
2626
+ wrappedError = new Error(`HuggingFace model is loading. Please retry in a moment. ${message}`);
2627
+ } else {
2628
+ wrappedError = new Error(`HuggingFace API error (${status}): ${message}`);
2629
+ }
2630
+ if (error.code) wrappedError.code = error.code;
2631
+ if (error.response) wrappedError.response = error.response;
2632
+ throw wrappedError;
2633
+ }
2634
+ throw error;
2635
+ }
2636
+ });
2637
+ }
2638
+ async generateBatchEmbeddings(texts, model) {
2639
+ const modelToUse = model || this.defaultModel;
2640
+ return retryWithBackoff2(async () => {
2641
+ try {
2642
+ const response = await axios16__default.default.post(
2643
+ `${this.baseUrl}/${modelToUse}`,
2644
+ {
2645
+ inputs: texts,
2646
+ options: {
2647
+ wait_for_model: true
2648
+ }
2649
+ },
2650
+ {
2651
+ headers: {
2652
+ "Authorization": `Bearer ${this.apiKey}`,
2653
+ "Content-Type": "application/json"
2654
+ }
2655
+ }
2656
+ );
2657
+ const embeddings = response.data;
2658
+ const dimensions = embeddings[0]?.length || 0;
2659
+ return {
2660
+ embeddings,
2661
+ model: modelToUse,
2662
+ dimensions
2663
+ };
2664
+ } catch (error) {
2665
+ if (axios16__default.default.isAxiosError(error)) {
2666
+ const status = error.response?.status;
2667
+ const message = error.response?.data?.error || error.message;
2668
+ let wrappedError;
2669
+ if (status === 401) {
2670
+ wrappedError = new Error(`HuggingFace API authentication failed. Please check your HUGGINGFACE_API_KEY. ${message}`);
2671
+ } else if (status === 429) {
2672
+ wrappedError = new Error(`HuggingFace API rate limit exceeded. ${message}`);
2673
+ } else if (status === 400) {
2674
+ wrappedError = new Error(`HuggingFace API request invalid: ${message}`);
2675
+ } else if (status === 503) {
2676
+ wrappedError = new Error(`HuggingFace model is loading. Please retry in a moment. ${message}`);
2677
+ } else {
2678
+ wrappedError = new Error(`HuggingFace API error (${status}): ${message}`);
2679
+ }
2680
+ if (error.code) wrappedError.code = error.code;
2681
+ if (error.response) wrappedError.response = error.response;
2682
+ throw wrappedError;
2683
+ }
2684
+ throw error;
2685
+ }
2686
+ });
2687
+ }
2688
+ };
2689
+ var VoyageEmbeddingProvider = class {
2690
+ name = "voyage";
2691
+ defaultModel = "voyage-2";
2692
+ apiKey;
2693
+ baseUrl = "https://api.voyageai.com/v1";
2694
+ constructor(apiKey, model) {
2695
+ this.apiKey = apiKey;
2696
+ }
2697
+ /**
2698
+ * Check if Voyage AI is available (API key is set)
2699
+ */
2700
+ isAvailable() {
2701
+ return !!this.apiKey;
2702
+ }
2703
+ async generateEmbedding(text, model) {
2704
+ const result = await this.generateBatchEmbeddings([text], model);
2705
+ return {
2706
+ embedding: result.embeddings[0],
2707
+ model: result.model,
2708
+ dimensions: result.dimensions,
2709
+ usage: result.usage
2710
+ };
2711
+ }
2712
+ async generateBatchEmbeddings(texts, model) {
2713
+ const modelToUse = model || this.defaultModel;
2714
+ return retryWithBackoff2(async () => {
2715
+ try {
2716
+ const response = await axios16__default.default.post(
2717
+ `${this.baseUrl}/embeddings`,
2718
+ {
2719
+ input: texts,
2720
+ model: modelToUse,
2721
+ input_type: "document"
2722
+ // For storing in vector DB
2723
+ },
2724
+ {
2725
+ headers: {
2726
+ "Authorization": `Bearer ${this.apiKey}`,
2727
+ "Content-Type": "application/json"
2728
+ }
2729
+ }
2730
+ );
2731
+ const embeddings = response.data.data.map((item) => item.embedding);
2732
+ const dimensions = embeddings[0]?.length || 0;
2733
+ return {
2734
+ embeddings,
2735
+ model: modelToUse,
2736
+ dimensions,
2737
+ usage: response.data.usage ? {
2738
+ promptTokens: response.data.usage.total_tokens || 0,
2739
+ totalTokens: response.data.usage.total_tokens || 0
2740
+ } : void 0
2741
+ };
2742
+ } catch (error) {
2743
+ if (axios16__default.default.isAxiosError(error)) {
2744
+ const status = error.response?.status;
2745
+ const message = error.response?.data?.message || error.message;
2746
+ let wrappedError;
2747
+ if (status === 401) {
2748
+ wrappedError = new Error(`Voyage AI API authentication failed. Please check your VOYAGE_API_KEY. ${message}`);
2749
+ } else if (status === 429) {
2750
+ wrappedError = new Error(`Voyage AI API rate limit exceeded. ${message}`);
2751
+ } else if (status === 400) {
2752
+ wrappedError = new Error(`Voyage AI API request invalid: ${message}`);
2753
+ } else {
2754
+ wrappedError = new Error(`Voyage AI API error (${status}): ${message}`);
2755
+ }
2756
+ if (error.code) wrappedError.code = error.code;
2757
+ if (error.response) wrappedError.response = error.response;
2758
+ throw wrappedError;
2759
+ }
2760
+ throw error;
2761
+ }
2762
+ });
2763
+ }
2764
+ };
2765
+ var OllamaEmbeddingProvider = class {
2766
+ name = "ollama";
2767
+ defaultModel = "nomic-embed-text";
2768
+ baseUrl;
2769
+ constructor(model, baseUrl = "http://localhost:11434") {
2770
+ this.baseUrl = baseUrl;
2771
+ }
2772
+ /**
2773
+ * Check if Ollama is available (always true for local service)
2774
+ */
2775
+ isAvailable() {
2776
+ return true;
2777
+ }
2778
+ async generateEmbedding(text, model) {
2779
+ const modelToUse = model || this.defaultModel;
2780
+ return retryWithBackoff2(async () => {
2781
+ try {
2782
+ const response = await axios16__default.default.post(
2783
+ `${this.baseUrl}/api/embeddings`,
2784
+ {
2785
+ model: modelToUse,
2786
+ prompt: text
2787
+ },
2788
+ {
2789
+ headers: {
2790
+ "Content-Type": "application/json"
2791
+ }
2792
+ }
2793
+ );
2794
+ const embedding = response.data.embedding;
2795
+ return {
2796
+ embedding,
2797
+ model: modelToUse,
2798
+ dimensions: embedding.length
2799
+ };
2800
+ } catch (error) {
2801
+ if (axios16__default.default.isAxiosError(error)) {
2802
+ const status = error.response?.status;
2803
+ const message = error.response?.data?.error || error.message;
2804
+ let wrappedError;
2805
+ if (error.code === "ECONNREFUSED") {
2806
+ wrappedError = new Error(`Cannot connect to Ollama at ${this.baseUrl}. Make sure Ollama is running locally.`);
2807
+ } else if (status === 404) {
2808
+ wrappedError = new Error(`Ollama model not found. Pull it with: ollama pull <model-name>`);
2809
+ } else if (status === 400) {
2810
+ wrappedError = new Error(`Ollama API request invalid: ${message}`);
2811
+ } else {
2812
+ wrappedError = new Error(`Ollama API error (${status}): ${message}`);
2813
+ }
2814
+ if (error.code) wrappedError.code = error.code;
2815
+ if (error.response) wrappedError.response = error.response;
2816
+ throw wrappedError;
2817
+ }
2818
+ throw error;
2819
+ }
2820
+ });
2821
+ }
2822
+ async generateBatchEmbeddings(texts, model) {
2823
+ const modelToUse = model || this.defaultModel;
2824
+ const embeddings = [];
2825
+ for (const text of texts) {
2826
+ const result = await this.generateEmbedding(text, modelToUse);
2827
+ embeddings.push(result.embedding);
2828
+ }
2829
+ const dimensions = embeddings[0]?.length || 0;
2830
+ return {
2831
+ embeddings,
2832
+ model: modelToUse,
2833
+ dimensions
2834
+ };
2835
+ }
2836
+ };
2837
+
2838
+ // src/data/neo4j/embeddings/embedding-manager.ts
2839
+ var logger4 = core.createLogger("agentforge:tools:neo4j:embeddings");
2840
+ var EmbeddingManager = class {
2841
+ provider = null;
2842
+ config = null;
2843
+ /**
2844
+ * Initialize with configuration
2845
+ */
2846
+ initialize(config) {
2847
+ logger4.debug("Initializing embedding manager", {
2848
+ provider: config.provider,
2849
+ model: config.model
2850
+ });
2851
+ this.config = config;
2852
+ this.provider = this.createProvider(config.provider, config.apiKey);
2853
+ logger4.info("Embedding manager initialized", {
2854
+ provider: config.provider,
2855
+ model: config.model || this.getDefaultModel(config.provider)
2856
+ });
2857
+ }
2858
+ /**
2859
+ * Initialize from environment variables
2860
+ */
2861
+ initializeFromEnv() {
2862
+ const providerName = getEmbeddingProvider();
2863
+ const model = getEmbeddingModel();
2864
+ logger4.debug("Initializing embedding manager from environment", {
2865
+ provider: providerName,
2866
+ model: model || this.getDefaultModel(providerName)
2867
+ });
2868
+ let apiKey = "";
2869
+ let baseUrl = "";
2870
+ switch (providerName) {
2871
+ case "openai": {
2872
+ const key = getOpenAIApiKey();
2873
+ if (!key) {
2874
+ logger4.error("OPENAI_API_KEY environment variable not set", {
2875
+ provider: "openai",
2876
+ required: true
2877
+ });
2878
+ throw new Error("OPENAI_API_KEY environment variable is required for OpenAI embeddings");
2879
+ }
2880
+ apiKey = key;
2881
+ logger4.debug("OpenAI API key found");
2882
+ break;
2883
+ }
2884
+ case "cohere": {
2885
+ const key = getCohereApiKey();
2886
+ if (!key) {
2887
+ logger4.error("COHERE_API_KEY environment variable not set", {
2888
+ provider: "cohere",
2889
+ required: true
2890
+ });
2891
+ throw new Error("COHERE_API_KEY environment variable is required for Cohere embeddings");
2892
+ }
2893
+ apiKey = key;
2894
+ logger4.debug("Cohere API key found");
2895
+ break;
2896
+ }
2897
+ case "huggingface": {
2898
+ const key = getHuggingFaceApiKey();
2899
+ if (!key) {
2900
+ logger4.error("HUGGINGFACE_API_KEY environment variable not set", {
2901
+ provider: "huggingface",
2902
+ required: true
2903
+ });
2904
+ throw new Error("HUGGINGFACE_API_KEY environment variable is required for HuggingFace embeddings");
2905
+ }
2906
+ apiKey = key;
2907
+ logger4.debug("HuggingFace API key found");
2908
+ break;
2909
+ }
2910
+ case "voyage": {
2911
+ const key = getVoyageApiKey();
2912
+ if (!key) {
2913
+ logger4.error("VOYAGE_API_KEY environment variable not set", {
2914
+ provider: "voyage",
2915
+ required: true
2916
+ });
2917
+ throw new Error("VOYAGE_API_KEY environment variable is required for Voyage AI embeddings");
2918
+ }
2919
+ apiKey = key;
2920
+ logger4.debug("Voyage API key found");
2921
+ break;
2922
+ }
2923
+ case "ollama":
2924
+ baseUrl = getOllamaBaseUrl();
2925
+ logger4.debug("Using Ollama (local, no API key required)", {
2926
+ baseUrl: baseUrl || "http://localhost:11434"
2927
+ });
2928
+ break;
2929
+ default:
2930
+ logger4.error("Unknown embedding provider", {
2931
+ provider: providerName
2932
+ });
2933
+ throw new Error(`Unknown embedding provider: ${providerName}`);
2934
+ }
2935
+ this.provider = this.createProvider(providerName, apiKey, model, baseUrl);
2936
+ this.config = {
2937
+ provider: providerName,
2938
+ model: model || this.getDefaultModel(providerName),
2939
+ apiKey: ""
2940
+ // Not stored when using env
2941
+ };
2942
+ logger4.info("Embedding manager initialized from environment", {
2943
+ provider: providerName,
2944
+ model: this.config.model
2945
+ });
2946
+ }
2947
+ /**
2948
+ * Check if manager is initialized
2949
+ */
2950
+ isInitialized() {
2951
+ return this.provider !== null && this.config !== null;
2952
+ }
2953
+ /**
2954
+ * Get current provider
2955
+ */
2956
+ getProvider() {
2957
+ if (!this.provider) {
2958
+ throw new Error("Embedding manager not initialized. Call initialize() or initializeFromEnv() first.");
2959
+ }
2960
+ return this.provider;
2961
+ }
2962
+ /**
2963
+ * Get current configuration
2964
+ */
2965
+ getConfig() {
2966
+ if (!this.config) {
2967
+ throw new Error("Embedding manager not initialized. Call initialize() or initializeFromEnv() first.");
2968
+ }
2969
+ return this.config;
2970
+ }
2971
+ /**
2972
+ * Generate embedding for a single text
2973
+ */
2974
+ async generateEmbedding(text, model) {
2975
+ const startTime = Date.now();
2976
+ const provider = this.getProvider();
2977
+ const config = this.getConfig();
2978
+ const modelToUse = model || config.model;
2979
+ logger4.debug("Generating embedding", {
2980
+ provider: provider.name,
2981
+ model: modelToUse,
2982
+ textLength: text.length
2983
+ });
2984
+ const result = await provider.generateEmbedding(text, modelToUse);
2985
+ logger4.info("Embedding generated", {
2986
+ provider: provider.name,
2987
+ model: result.model,
2988
+ dimensions: result.dimensions,
2989
+ duration: Date.now() - startTime
2990
+ });
2991
+ return result;
2992
+ }
2993
+ /**
2994
+ * Generate embeddings for multiple texts (batch)
2995
+ */
2996
+ async generateBatchEmbeddings(texts, model) {
2997
+ const startTime = Date.now();
2998
+ const provider = this.getProvider();
2999
+ const config = this.getConfig();
3000
+ const modelToUse = model || config.model;
3001
+ logger4.debug("Generating batch embeddings", {
3002
+ provider: provider.name,
3003
+ model: modelToUse,
3004
+ count: texts.length
3005
+ });
3006
+ const result = await provider.generateBatchEmbeddings(texts, modelToUse);
3007
+ logger4.info("Batch embeddings generated", {
3008
+ provider: provider.name,
3009
+ model: result.model,
3010
+ dimensions: result.dimensions,
3011
+ count: texts.length,
3012
+ duration: Date.now() - startTime
3013
+ });
3014
+ return result;
3015
+ }
3016
+ /**
3017
+ * Get default model for a provider
3018
+ */
3019
+ getDefaultModel(provider) {
3020
+ switch (provider) {
3021
+ case "openai":
3022
+ return "text-embedding-3-small";
3023
+ case "cohere":
3024
+ return "embed-english-v3.0";
3025
+ case "huggingface":
3026
+ return "sentence-transformers/all-MiniLM-L6-v2";
3027
+ case "voyage":
3028
+ return "voyage-2";
3029
+ case "ollama":
3030
+ return "nomic-embed-text";
3031
+ default:
3032
+ return "text-embedding-3-small";
3033
+ }
3034
+ }
3035
+ /**
3036
+ * Create a provider instance
3037
+ */
3038
+ createProvider(providerName, apiKey, model, baseUrl) {
3039
+ switch (providerName) {
3040
+ case "openai":
3041
+ return new OpenAIEmbeddingProvider(apiKey);
3042
+ case "cohere":
3043
+ return new CohereEmbeddingProvider(apiKey);
3044
+ case "huggingface":
3045
+ return new HuggingFaceEmbeddingProvider(apiKey);
3046
+ case "voyage":
3047
+ return new VoyageEmbeddingProvider(apiKey);
3048
+ case "ollama":
3049
+ return new OllamaEmbeddingProvider(model, baseUrl || "http://localhost:11434");
3050
+ default:
3051
+ throw new Error(`Unknown embedding provider: ${providerName}`);
3052
+ }
3053
+ }
3054
+ /**
3055
+ * Reset the manager (for testing)
3056
+ */
3057
+ reset() {
3058
+ this.provider = null;
3059
+ this.config = null;
3060
+ }
3061
+ };
3062
+ var embeddingManager = new EmbeddingManager();
3063
+ function initializeEmbeddings() {
3064
+ embeddingManager.initializeFromEnv();
3065
+ }
3066
+ function initializeEmbeddingsWithConfig(config) {
3067
+ embeddingManager.initialize(config);
3068
+ }
3069
+ async function generateEmbedding(text, model) {
3070
+ return embeddingManager.generateEmbedding(text, model);
3071
+ }
3072
+ async function generateBatchEmbeddings(texts, model) {
3073
+ return embeddingManager.generateBatchEmbeddings(texts, model);
3074
+ }
3075
+ function formatInteger(value) {
3076
+ if (value.inSafeRange()) {
3077
+ return value.toNumber();
3078
+ }
3079
+ return value.toString();
3080
+ }
3081
+ function formatValue(value) {
3082
+ if (value === null || value === void 0) {
3083
+ return value;
3084
+ }
3085
+ if (value instanceof neo4j.Integer) {
3086
+ return formatInteger(value);
3087
+ }
3088
+ if (value instanceof neo4j.Node) {
3089
+ return formatNode(value);
3090
+ }
3091
+ if (value instanceof neo4j.Relationship) {
3092
+ return formatRelationship(value);
3093
+ }
3094
+ if (value instanceof neo4j.Path) {
3095
+ return formatPath(value);
3096
+ }
3097
+ if (Array.isArray(value)) {
3098
+ return value.map(formatValue);
3099
+ }
3100
+ if (typeof value === "object") {
3101
+ const formatted = {};
3102
+ for (const [key, val] of Object.entries(value)) {
3103
+ formatted[key] = formatValue(val);
3104
+ }
3105
+ return formatted;
3106
+ }
3107
+ return value;
3108
+ }
3109
+ function formatNode(node) {
3110
+ return {
3111
+ identity: formatInteger(node.identity),
3112
+ labels: node.labels,
3113
+ properties: formatValue(node.properties)
3114
+ };
3115
+ }
3116
+ function formatRelationship(rel) {
3117
+ return {
3118
+ identity: formatInteger(rel.identity),
3119
+ type: rel.type,
3120
+ start: formatInteger(rel.start),
3121
+ end: formatInteger(rel.end),
3122
+ properties: formatValue(rel.properties)
3123
+ };
3124
+ }
3125
+ function formatPath(path12) {
3126
+ return {
3127
+ start: formatNode(path12.start),
3128
+ end: formatNode(path12.end),
3129
+ segments: path12.segments.map((segment) => ({
3130
+ start: formatNode(segment.start),
3131
+ relationship: formatRelationship(segment.relationship),
3132
+ end: formatNode(segment.end)
3133
+ })),
3134
+ length: path12.length
3135
+ };
3136
+ }
3137
+ function formatResults(records) {
3138
+ return records.map((record) => {
3139
+ const formatted = {};
3140
+ for (const key of record.keys) {
3141
+ formatted[key] = formatValue(record.get(key));
3142
+ }
3143
+ return formatted;
3144
+ });
3145
+ }
3146
+
3147
+ // src/data/neo4j/tools/neo4j-query.ts
3148
+ var logger5 = core.createLogger("agentforge:tools:neo4j:query");
3149
+ function createNeo4jQueryTool() {
3150
+ return core.toolBuilder().name("neo4j-query").description(
3151
+ "Execute a Cypher query against the Neo4j graph database. Supports parameterized queries for safety. Use this for complex queries, graph traversals, and custom operations."
3152
+ ).category(core.ToolCategory.DATABASE).tags(["neo4j", "graph", "database", "cypher", "query"]).schema(neo4jQuerySchema).implement(async (input) => {
3153
+ if (!neo4jPool.isInitialized()) {
3154
+ logger5.warn("Neo4j query attempted but connection not initialized");
3155
+ return {
3156
+ success: false,
3157
+ error: "Neo4j connection not initialized. Please configure Neo4j connection first."
3158
+ };
3159
+ }
3160
+ const startTime = Date.now();
3161
+ logger5.debug("Executing Neo4j query", {
3162
+ cypherPreview: input.cypher.substring(0, 100),
3163
+ parameterCount: Object.keys(input.parameters || {}).length,
3164
+ database: input.database
3165
+ });
3166
+ try {
3167
+ const session = neo4jPool.getSession(input.database);
3168
+ try {
3169
+ const result = await session.run(input.cypher, input.parameters || {});
3170
+ const formattedResults = formatResults(result.records);
3171
+ const duration = Date.now() - startTime;
3172
+ logger5.info("Neo4j query executed successfully", {
3173
+ recordCount: result.records.length,
3174
+ nodesCreated: result.summary.counters.updates().nodesCreated,
3175
+ nodesDeleted: result.summary.counters.updates().nodesDeleted,
3176
+ relationshipsCreated: result.summary.counters.updates().relationshipsCreated,
3177
+ relationshipsDeleted: result.summary.counters.updates().relationshipsDeleted,
3178
+ propertiesSet: result.summary.counters.updates().propertiesSet,
3179
+ duration
3180
+ });
3181
+ return {
3182
+ success: true,
3183
+ data: formattedResults,
3184
+ recordCount: result.records.length,
3185
+ summary: {
3186
+ query: input.cypher,
3187
+ parameters: input.parameters,
3188
+ counters: {
3189
+ nodesCreated: result.summary.counters.updates().nodesCreated,
3190
+ nodesDeleted: result.summary.counters.updates().nodesDeleted,
3191
+ relationshipsCreated: result.summary.counters.updates().relationshipsCreated,
3192
+ relationshipsDeleted: result.summary.counters.updates().relationshipsDeleted,
3193
+ propertiesSet: result.summary.counters.updates().propertiesSet
3194
+ }
3195
+ }
3196
+ };
3197
+ } finally {
3198
+ await session.close();
3199
+ }
3200
+ } catch (error) {
3201
+ const duration = Date.now() - startTime;
3202
+ const errorMessage = error instanceof Error ? error.message : "Failed to execute query";
3203
+ logger5.error("Neo4j query execution failed", {
3204
+ error: errorMessage,
3205
+ cypherPreview: input.cypher.substring(0, 100),
3206
+ duration
3207
+ });
3208
+ return {
3209
+ success: false,
3210
+ error: errorMessage,
3211
+ query: input.cypher
3212
+ };
3213
+ }
3214
+ }).build();
3215
+ }
3216
+ function createNeo4jGetSchemaTool() {
3217
+ return core.toolBuilder().name("neo4j-get-schema").description(
3218
+ "Get the schema of the Neo4j graph database including node labels, relationship types, property keys, constraints, and indexes. This helps understand the structure of the graph before querying."
3219
+ ).category(core.ToolCategory.DATABASE).tags(["neo4j", "graph", "database", "schema", "introspection"]).schema(neo4jGetSchemaSchema).implement(async (input) => {
3220
+ if (!neo4jPool.isInitialized()) {
3221
+ return {
3222
+ success: false,
3223
+ error: "Neo4j connection not initialized. Please configure Neo4j connection first."
3224
+ };
3225
+ }
3226
+ try {
3227
+ const session = neo4jPool.getSession(input.database);
3228
+ try {
3229
+ const labelsResult = await session.run("CALL db.labels()");
3230
+ const nodeLabels = labelsResult.records.map((r) => r.get("label"));
3231
+ const relTypesResult = await session.run("CALL db.relationshipTypes()");
3232
+ const relationshipTypes = relTypesResult.records.map((r) => r.get("relationshipType"));
3233
+ const propsResult = await session.run("CALL db.propertyKeys()");
3234
+ const propertyKeys = propsResult.records.map((r) => r.get("propertyKey"));
3235
+ const constraintsResult = await session.run("SHOW CONSTRAINTS");
3236
+ const constraints = constraintsResult.records.map((r) => ({
3237
+ name: r.get("name"),
3238
+ type: r.get("type"),
3239
+ entityType: r.get("entityType"),
3240
+ labelsOrTypes: r.get("labelsOrTypes"),
3241
+ properties: r.get("properties")
3242
+ }));
3243
+ const indexesResult = await session.run("SHOW INDEXES");
3244
+ const indexes = indexesResult.records.map((r) => ({
3245
+ name: r.get("name"),
3246
+ type: r.get("type"),
3247
+ entityType: r.get("entityType"),
3248
+ labelsOrTypes: r.get("labelsOrTypes"),
3249
+ properties: r.get("properties")
3250
+ }));
3251
+ return {
3252
+ success: true,
3253
+ schema: {
3254
+ nodeLabels,
3255
+ relationshipTypes,
3256
+ propertyKeys,
3257
+ constraints,
3258
+ indexes
3259
+ },
3260
+ summary: {
3261
+ totalLabels: nodeLabels.length,
3262
+ totalRelationshipTypes: relationshipTypes.length,
3263
+ totalPropertyKeys: propertyKeys.length,
3264
+ totalConstraints: constraints.length,
3265
+ totalIndexes: indexes.length
3266
+ }
3267
+ };
3268
+ } finally {
3269
+ await session.close();
3270
+ }
3271
+ } catch (error) {
3272
+ return {
3273
+ success: false,
3274
+ error: error instanceof Error ? error.message : "Failed to get schema"
3275
+ };
3276
+ }
3277
+ }).build();
3278
+ }
3279
+
3280
+ // src/data/neo4j/utils/cypher-sanitizer.ts
3281
+ function escapeCypherIdentifier(identifier, type = "identifier") {
3282
+ if (!identifier || typeof identifier !== "string") {
3283
+ throw new Error(`Invalid ${type}: must be a non-empty string`);
3284
+ }
3285
+ const trimmed = identifier.trim();
3286
+ if (trimmed.length === 0) {
3287
+ throw new Error(`Invalid ${type}: cannot be empty or whitespace`);
3288
+ }
3289
+ if (trimmed.includes("\0")) {
3290
+ throw new Error(`Invalid ${type}: cannot contain null bytes`);
3291
+ }
3292
+ const simplePattern = /^[a-zA-Z_][a-zA-Z0-9_]*$/;
3293
+ if (simplePattern.test(trimmed)) {
3294
+ return trimmed;
3295
+ }
3296
+ const escaped = trimmed.replace(/`/g, "``");
3297
+ return `\`${escaped}\``;
3298
+ }
3299
+ function validateLabel(label) {
3300
+ return escapeCypherIdentifier(label, "label");
3301
+ }
3302
+ function validatePropertyKey(key) {
3303
+ return escapeCypherIdentifier(key, "property key");
3304
+ }
3305
+ function validateRelationshipType(type) {
3306
+ return escapeCypherIdentifier(type, "relationship type");
3307
+ }
3308
+ function validateDirection(direction) {
3309
+ const normalized = direction.toUpperCase();
3310
+ if (normalized !== "OUTGOING" && normalized !== "INCOMING" && normalized !== "BOTH") {
3311
+ throw new Error(`Invalid direction: must be 'OUTGOING', 'INCOMING', or 'BOTH'`);
3312
+ }
3313
+ return normalized;
3314
+ }
3315
+ function buildPropertyFilter(properties, nodeVar = "n") {
3316
+ const keys = Object.keys(properties);
3317
+ if (keys.length === 0) {
3318
+ return { whereClause: "", parameters: {} };
3319
+ }
3320
+ const safeNodeVar = escapeCypherIdentifier(nodeVar, "node variable");
3321
+ const conditions = keys.map((key, index) => {
3322
+ const safeKey = validatePropertyKey(key);
3323
+ const paramName = `prop_${index}`;
3324
+ return `${safeNodeVar}.${safeKey} = $${paramName}`;
3325
+ });
3326
+ const parameters = {};
3327
+ keys.forEach((key, index) => {
3328
+ parameters[`prop_${index}`] = properties[key];
3329
+ });
3330
+ return {
3331
+ whereClause: `WHERE ${conditions.join(" AND ")}`,
3332
+ parameters
3333
+ };
3334
+ }
3335
+
3336
+ // src/data/neo4j/tools/neo4j-find-nodes.ts
3337
+ function createNeo4jFindNodesTool() {
3338
+ return core.toolBuilder().name("neo4j-find-nodes").description(
3339
+ "Find nodes in the Neo4j graph by label and optional property filters. This is a simplified interface for common node lookup operations. Returns nodes matching the specified criteria."
3340
+ ).category(core.ToolCategory.DATABASE).tags(["neo4j", "graph", "database", "nodes", "search"]).schema(neo4jFindNodesSchema).implement(async (input) => {
3341
+ if (!neo4jPool.isInitialized()) {
3342
+ return {
3343
+ success: false,
3344
+ error: "Neo4j connection not initialized. Please configure Neo4j connection first."
3345
+ };
3346
+ }
3347
+ try {
3348
+ const safeLabel = validateLabel(input.label);
3349
+ const session = neo4jPool.getSession(input.database);
3350
+ try {
3351
+ let cypher = `MATCH (n:${safeLabel})`;
3352
+ let parameters = {};
3353
+ if (input.properties && Object.keys(input.properties).length > 0) {
3354
+ const { whereClause, parameters: filterParams } = buildPropertyFilter(input.properties, "n");
3355
+ cypher += ` ${whereClause}`;
3356
+ parameters = { ...parameters, ...filterParams };
3357
+ }
3358
+ cypher += ` RETURN n LIMIT $limit`;
3359
+ parameters.limit = input.limit;
3360
+ const result = await session.run(cypher, parameters);
3361
+ const formattedResults = formatResults(result.records);
3362
+ return {
3363
+ success: true,
3364
+ nodes: formattedResults.map((r) => r.n),
3365
+ count: result.records.length,
3366
+ query: {
3367
+ label: input.label,
3368
+ properties: input.properties,
3369
+ limit: input.limit
3370
+ }
3371
+ };
3372
+ } finally {
3373
+ await session.close();
3374
+ }
3375
+ } catch (error) {
3376
+ return {
3377
+ success: false,
3378
+ error: error instanceof Error ? error.message : "Failed to find nodes",
3379
+ query: {
3380
+ label: input.label,
3381
+ properties: input.properties
3382
+ }
3383
+ };
3384
+ }
3385
+ }).build();
3386
+ }
3387
+ function createNeo4jTraverseTool() {
3388
+ return core.toolBuilder().name("neo4j-traverse").description(
3389
+ "Traverse the Neo4j graph from a starting node by following relationships. Supports filtering by relationship type, direction, and maximum depth. Returns connected nodes and their relationships."
3390
+ ).category(core.ToolCategory.DATABASE).tags(["neo4j", "graph", "database", "traverse", "relationships"]).schema(neo4jTraverseSchema).implement(async (input) => {
3391
+ if (!neo4jPool.isInitialized()) {
3392
+ return {
3393
+ success: false,
3394
+ error: "Neo4j connection not initialized. Please configure Neo4j connection first."
3395
+ };
3396
+ }
3397
+ try {
3398
+ const safeDirection = validateDirection(input.direction || "outgoing");
3399
+ const safeRelType = input.relationshipType ? validateRelationshipType(input.relationshipType) : null;
3400
+ const session = neo4jPool.getSession(input.database);
3401
+ try {
3402
+ let relPattern = "";
3403
+ if (safeDirection === "OUTGOING") {
3404
+ relPattern = safeRelType ? `-[r:${safeRelType}*1..${input.maxDepth}]->` : `-[r*1..${input.maxDepth}]->`;
3405
+ } else if (safeDirection === "INCOMING") {
3406
+ relPattern = safeRelType ? `<-[r:${safeRelType}*1..${input.maxDepth}]-` : `<-[r*1..${input.maxDepth}]-`;
3407
+ } else {
3408
+ relPattern = safeRelType ? `-[r:${safeRelType}*1..${input.maxDepth}]-` : `-[r*1..${input.maxDepth}]-`;
3409
+ }
3410
+ const cypher = `
3411
+ MATCH path = (start)${relPattern}(end)
3412
+ WHERE id(start) = $startNodeId
3413
+ RETURN start, end, relationships(path) as rels, length(path) as depth
3414
+ LIMIT $limit
3415
+ `;
3416
+ const parameters = {
3417
+ startNodeId: typeof input.startNodeId === "string" ? parseInt(input.startNodeId, 10) : input.startNodeId,
3418
+ limit: input.limit
3419
+ };
3420
+ const result = await session.run(cypher, parameters);
3421
+ const formattedResults = formatResults(result.records);
3422
+ return {
3423
+ success: true,
3424
+ paths: formattedResults.map((r) => ({
3425
+ start: r.start,
3426
+ end: r.end,
3427
+ relationships: r.rels,
3428
+ depth: r.depth
3429
+ })),
3430
+ count: result.records.length,
3431
+ query: {
3432
+ startNodeId: input.startNodeId,
3433
+ relationshipType: input.relationshipType,
3434
+ direction: input.direction,
3435
+ maxDepth: input.maxDepth
3436
+ }
3437
+ };
3438
+ } finally {
3439
+ await session.close();
3440
+ }
3441
+ } catch (error) {
3442
+ return {
3443
+ success: false,
3444
+ error: error instanceof Error ? error.message : "Failed to traverse graph",
3445
+ query: {
3446
+ startNodeId: input.startNodeId,
3447
+ relationshipType: input.relationshipType,
3448
+ direction: input.direction
3449
+ }
3450
+ };
3451
+ }
3452
+ }).build();
3453
+ }
3454
+ function createNeo4jVectorSearchTool() {
3455
+ return core.toolBuilder().name("neo4j-vector-search").description(
3456
+ "Perform semantic similarity search using vector indexes in Neo4j. Essential for GraphRAG applications - finds nodes with similar embeddings. Requires a vector index to be created in advance."
3457
+ ).category(core.ToolCategory.DATABASE).tags(["neo4j", "graph", "database", "vector", "search", "semantic", "graphrag"]).schema(neo4jVectorSearchSchema).implement(async (input) => {
3458
+ if (!neo4jPool.isInitialized()) {
3459
+ return {
3460
+ success: false,
3461
+ error: "Neo4j connection not initialized. Please configure Neo4j connection first."
3462
+ };
3463
+ }
3464
+ try {
3465
+ const session = neo4jPool.getSession(input.database);
3466
+ try {
3467
+ const cypher = `
3468
+ CALL db.index.vector.queryNodes($indexName, $limit, $queryVector)
3469
+ YIELD node, score
3470
+ RETURN node, score
3471
+ ORDER BY score DESC
3472
+ `;
3473
+ const parameters = {
3474
+ indexName: input.indexName,
3475
+ limit: input.limit,
3476
+ queryVector: input.queryVector
3477
+ };
3478
+ const result = await session.run(cypher, parameters);
3479
+ const formattedResults = formatResults(result.records);
3480
+ return {
3481
+ success: true,
3482
+ results: formattedResults.map((r) => ({
3483
+ node: r.node,
3484
+ score: r.score
3485
+ })),
3486
+ count: result.records.length,
3487
+ query: {
3488
+ indexName: input.indexName,
3489
+ vectorDimension: input.queryVector.length,
3490
+ limit: input.limit
3491
+ }
3492
+ };
3493
+ } finally {
3494
+ await session.close();
3495
+ }
3496
+ } catch (error) {
3497
+ const errorMessage = error instanceof Error ? error.message : "Failed to perform vector search";
3498
+ let helpText = "";
3499
+ if (errorMessage.includes("index") || errorMessage.includes("not found")) {
3500
+ helpText = " Make sure the vector index exists. Create one with: CREATE VECTOR INDEX <name> FOR (n:Label) ON (n.embedding)";
3501
+ }
3502
+ return {
3503
+ success: false,
3504
+ error: errorMessage + helpText,
3505
+ query: {
3506
+ indexName: input.indexName,
3507
+ vectorDimension: input.queryVector.length
3508
+ }
3509
+ };
3510
+ }
3511
+ }).build();
3512
+ }
3513
+ var logger6 = core.createLogger("agentforge:tools:neo4j:vector-search");
3514
+ function createNeo4jVectorSearchWithEmbeddingTool() {
3515
+ return core.toolBuilder().name("neo4j-vector-search-with-embedding").description(
3516
+ "Perform semantic similarity search in Neo4j by automatically generating embeddings from text. This tool takes text input, generates an embedding vector, and searches for similar nodes. Essential for GraphRAG applications - no need to manually generate embeddings. Requires a vector index and embedding provider (OpenAI) to be configured."
3517
+ ).category(core.ToolCategory.DATABASE).tags(["neo4j", "graph", "database", "vector", "search", "semantic", "graphrag", "embedding", "ai"]).schema(neo4jVectorSearchWithEmbeddingSchema).implement(async (input) => {
3518
+ if (!neo4jPool.isInitialized()) {
3519
+ logger6.warn("Vector search attempted but Neo4j connection not initialized");
3520
+ return {
3521
+ success: false,
3522
+ error: "Neo4j connection not initialized. Please configure Neo4j connection first."
3523
+ };
3524
+ }
3525
+ if (!embeddingManager.isInitialized()) {
3526
+ logger6.warn("Vector search attempted but embedding manager not initialized");
3527
+ return {
3528
+ success: false,
3529
+ error: "Embedding manager not initialized. Please configure embedding provider (set OPENAI_API_KEY and optionally EMBEDDING_MODEL)."
3530
+ };
3531
+ }
3532
+ const startTime = Date.now();
3533
+ logger6.debug("Performing vector search with embedding", {
3534
+ queryTextLength: input.queryText.length,
3535
+ indexName: input.indexName,
3536
+ limit: input.limit,
3537
+ model: input.model
3538
+ });
3539
+ try {
3540
+ const embeddingResult = await embeddingManager.generateEmbedding(input.queryText, input.model);
3541
+ const session = neo4jPool.getSession(input.database);
3542
+ try {
3543
+ const cypher = `
3544
+ CALL db.index.vector.queryNodes($indexName, $limit, $queryVector)
3545
+ YIELD node, score
3546
+ RETURN node, score
3547
+ ORDER BY score DESC
3548
+ `;
3549
+ const parameters = {
3550
+ indexName: input.indexName,
3551
+ limit: input.limit,
3552
+ queryVector: embeddingResult.embedding
3553
+ };
3554
+ const result = await session.run(cypher, parameters);
3555
+ const formattedResults = formatResults(result.records);
3556
+ const duration = Date.now() - startTime;
3557
+ logger6.info("Vector search completed successfully", {
3558
+ resultCount: result.records.length,
3559
+ indexName: input.indexName,
3560
+ embeddingModel: embeddingResult.model,
3561
+ embeddingDimensions: embeddingResult.dimensions,
3562
+ duration
3563
+ });
3564
+ return {
3565
+ success: true,
3566
+ results: formattedResults.map((r) => ({
3567
+ node: r.node,
3568
+ score: r.score
3569
+ })),
3570
+ count: result.records.length,
3571
+ query: {
3572
+ text: input.queryText,
3573
+ indexName: input.indexName,
3574
+ embeddingModel: embeddingResult.model,
3575
+ vectorDimension: embeddingResult.dimensions,
3576
+ limit: input.limit
3577
+ },
3578
+ embedding: {
3579
+ model: embeddingResult.model,
3580
+ dimensions: embeddingResult.dimensions,
3581
+ usage: embeddingResult.usage
3582
+ }
3583
+ };
3584
+ } finally {
3585
+ await session.close();
3586
+ }
3587
+ } catch (error) {
3588
+ const duration = Date.now() - startTime;
3589
+ const errorMessage = error instanceof Error ? error.message : "Failed to perform vector search with embedding";
3590
+ let helpText = "";
3591
+ if (errorMessage.includes("index") || errorMessage.includes("not found")) {
3592
+ helpText = " Make sure the vector index exists. Create one with: CREATE VECTOR INDEX <name> FOR (n:Label) ON (n.embedding)";
3593
+ } else if (errorMessage.includes("API key") || errorMessage.includes("not initialized")) {
3594
+ helpText = " Make sure OPENAI_API_KEY is set in your environment variables.";
3595
+ } else if (errorMessage.includes("dimension")) {
3596
+ helpText = " Make sure the vector index dimensions match your embedding model dimensions.";
3597
+ }
3598
+ logger6.error("Vector search failed", {
3599
+ error: errorMessage,
3600
+ indexName: input.indexName,
3601
+ queryTextLength: input.queryText.length,
3602
+ duration
3603
+ });
3604
+ return {
3605
+ success: false,
3606
+ error: errorMessage + helpText,
3607
+ query: {
3608
+ text: input.queryText,
3609
+ indexName: input.indexName
3610
+ }
3611
+ };
3612
+ }
3613
+ }).build();
3614
+ }
3615
+ function createNeo4jCreateNodeWithEmbeddingTool() {
3616
+ return core.toolBuilder().name("neo4j-create-node-with-embedding").description(
3617
+ "Create a Neo4j node with automatic embedding generation from text content. This tool extracts text from a specified property, generates an embedding vector, and stores both the original properties and the embedding in the node. Perfect for building GraphRAG knowledge bases. Requires embedding provider (OpenAI) to be configured."
3618
+ ).category(core.ToolCategory.DATABASE).tags(["neo4j", "graph", "database", "create", "node", "embedding", "graphrag", "ai"]).schema(neo4jCreateNodeWithEmbeddingSchema).implement(async (input) => {
3619
+ if (!neo4jPool.isInitialized()) {
3620
+ return {
3621
+ success: false,
3622
+ error: "Neo4j connection not initialized. Please configure Neo4j connection first."
3623
+ };
3624
+ }
3625
+ if (!embeddingManager.isInitialized()) {
3626
+ return {
3627
+ success: false,
3628
+ error: "Embedding manager not initialized. Please configure embedding provider (set OPENAI_API_KEY and optionally EMBEDDING_MODEL)."
3629
+ };
3630
+ }
3631
+ try {
3632
+ const textToEmbed = input.properties[input.textProperty];
3633
+ if (!textToEmbed || typeof textToEmbed !== "string") {
3634
+ return {
3635
+ success: false,
3636
+ error: `Property '${input.textProperty}' not found or is not a string in the provided properties.`
3637
+ };
3638
+ }
3639
+ const embeddingResult = await embeddingManager.generateEmbedding(textToEmbed, input.model);
3640
+ const safeLabel = validateLabel(input.label);
3641
+ const safeEmbeddingProp = validatePropertyKey(input.embeddingProperty || "embedding");
3642
+ const session = neo4jPool.getSession(input.database);
3643
+ try {
3644
+ const allProperties = {
3645
+ ...input.properties
3646
+ };
3647
+ allProperties[input.embeddingProperty || "embedding"] = embeddingResult.embedding;
3648
+ const cypher = `
3649
+ CREATE (n:${safeLabel})
3650
+ SET n = $properties
3651
+ RETURN n, id(n) as nodeId
3652
+ `;
3653
+ const parameters = {
3654
+ properties: allProperties
3655
+ };
3656
+ const result = await session.run(cypher, parameters);
3657
+ const formattedResults = formatResults(result.records);
3658
+ if (formattedResults.length === 0) {
3659
+ return {
3660
+ success: false,
3661
+ error: "Failed to create node"
3662
+ };
3663
+ }
3664
+ const createdNode = formattedResults[0];
3665
+ return {
3666
+ success: true,
3667
+ node: createdNode.n,
3668
+ nodeId: createdNode.nodeId,
3669
+ embedding: {
3670
+ model: embeddingResult.model,
3671
+ dimensions: embeddingResult.dimensions,
3672
+ property: safeEmbeddingProp,
3673
+ usage: embeddingResult.usage
3674
+ },
3675
+ message: `Created node with label '${input.label}' and ${embeddingResult.dimensions}-dimensional embedding`
3676
+ };
3677
+ } finally {
3678
+ await session.close();
3679
+ }
3680
+ } catch (error) {
3681
+ const errorMessage = error instanceof Error ? error.message : "Failed to create node with embedding";
3682
+ let helpText = "";
3683
+ if (errorMessage.includes("API key") || errorMessage.includes("not initialized")) {
3684
+ helpText = " Make sure OPENAI_API_KEY is set in your environment variables.";
3685
+ } else if (errorMessage.includes("Syntax error") || errorMessage.includes("Invalid")) {
3686
+ helpText = " Check that the label and property names are valid.";
3687
+ }
3688
+ return {
3689
+ success: false,
3690
+ error: errorMessage + helpText
3691
+ };
3692
+ }
3693
+ }).build();
3694
+ }
3695
+
3696
+ // src/data/neo4j/index.ts
3697
+ var neo4jQuery = createNeo4jQueryTool();
3698
+ var neo4jGetSchema = createNeo4jGetSchemaTool();
3699
+ var neo4jFindNodes = createNeo4jFindNodesTool();
3700
+ var neo4jTraverse = createNeo4jTraverseTool();
3701
+ var neo4jVectorSearch = createNeo4jVectorSearchTool();
3702
+ var neo4jVectorSearchWithEmbedding = createNeo4jVectorSearchWithEmbeddingTool();
3703
+ var neo4jCreateNodeWithEmbedding = createNeo4jCreateNodeWithEmbeddingTool();
3704
+ var neo4jTools = [
3705
+ neo4jQuery,
3706
+ neo4jGetSchema,
3707
+ neo4jFindNodes,
3708
+ neo4jTraverse,
3709
+ neo4jVectorSearch,
3710
+ neo4jVectorSearchWithEmbedding,
3711
+ neo4jCreateNodeWithEmbedding
3712
+ ];
3713
+ var neo4jCoreTools = [
3714
+ neo4jQuery,
3715
+ neo4jGetSchema,
3716
+ neo4jFindNodes,
3717
+ neo4jTraverse,
3718
+ neo4jVectorSearch
3719
+ ];
3720
+ function createNeo4jTools(config = {}, includeEmbeddingTools = true) {
3721
+ if (config.uri && config.username && config.password) {
3722
+ neo4jPool.initialize({
3723
+ uri: config.uri,
3724
+ username: config.username,
3725
+ password: config.password,
3726
+ database: config.database,
3727
+ maxConnectionPoolSize: config.maxConnectionPoolSize,
3728
+ connectionTimeout: config.connectionTimeout
3729
+ }).catch((error) => {
3730
+ console.error("Failed to initialize Neo4j connection:", error);
3731
+ });
3732
+ }
3733
+ const coreTools = [
3734
+ createNeo4jQueryTool(),
3735
+ createNeo4jGetSchemaTool(),
3736
+ createNeo4jFindNodesTool(),
3737
+ createNeo4jTraverseTool(),
3738
+ createNeo4jVectorSearchTool()
3739
+ ];
3740
+ if (includeEmbeddingTools) {
3741
+ return [
3742
+ ...coreTools,
3743
+ createNeo4jVectorSearchWithEmbeddingTool(),
3744
+ createNeo4jCreateNodeWithEmbeddingTool()
3745
+ ];
3746
+ }
3747
+ return coreTools;
3748
+ }
3749
+ async function initializeNeo4jTools() {
3750
+ await initializeFromEnv();
3751
+ try {
3752
+ embeddingManager.initializeFromEnv();
3753
+ } catch (error) {
3754
+ }
3755
+ }
2054
3756
  var fileReaderSchema = zod.z.object({
2055
3757
  path: zod.z.string().describe("Path to the file to read"),
2056
3758
  encoding: zod.z.enum(["utf8", "utf-8", "ascii", "base64", "hex", "binary"]).default("utf8").describe("File encoding")
@@ -3200,7 +4902,7 @@ var AskHumanInputSchema = zod.z.object({
3200
4902
  suggestions: zod.z.array(zod.z.string()).optional().describe("Suggested responses for the human")
3201
4903
  });
3202
4904
  var logLevel3 = process.env.LOG_LEVEL?.toLowerCase() || core.LogLevel.INFO;
3203
- var logger3 = core.createLogger("askHuman", { level: logLevel3 });
4905
+ var logger7 = core.createLogger("askHuman", { level: logLevel3 });
3204
4906
  function createAskHumanTool() {
3205
4907
  return core.toolBuilder().name("ask-human").description(
3206
4908
  "Ask a human for input or approval. Use this when you need human guidance, approval for a critical action, or clarification on ambiguous requirements. The agent execution will pause until the human responds."
@@ -3233,13 +4935,13 @@ function createAskHumanTool() {
3233
4935
  suggestions: validatedInput.suggestions,
3234
4936
  status: "pending"
3235
4937
  };
3236
- logger3.debug("About to call interrupt()", { humanRequest });
4938
+ logger7.debug("About to call interrupt()", { humanRequest });
3237
4939
  let response;
3238
4940
  try {
3239
4941
  response = interrupt(humanRequest);
3240
- logger3.debug("interrupt() returned successfully", { response, responseType: typeof response });
4942
+ logger7.debug("interrupt() returned successfully", { response, responseType: typeof response });
3241
4943
  } catch (error) {
3242
- logger3.debug("interrupt() threw error (expected for GraphInterrupt)", {
4944
+ logger7.debug("interrupt() threw error (expected for GraphInterrupt)", {
3243
4945
  errorType: error?.constructor?.name,
3244
4946
  error: error instanceof Error ? error.message : String(error)
3245
4947
  });
@@ -3268,15 +4970,18 @@ exports.AskHumanInputSchema = AskHumanInputSchema;
3268
4970
  exports.CalculatorSchema = CalculatorSchema;
3269
4971
  exports.CreditCardValidatorSchema = CreditCardValidatorSchema;
3270
4972
  exports.CurrentDateTimeSchema = CurrentDateTimeSchema;
4973
+ exports.DEFAULT_RETRY_CONFIG = DEFAULT_RETRY_CONFIG2;
3271
4974
  exports.DateArithmeticSchema = DateArithmeticSchema;
3272
4975
  exports.DateComparisonSchema = DateComparisonSchema;
3273
4976
  exports.DateDifferenceSchema = DateDifferenceSchema;
3274
4977
  exports.DateFormatterSchema = DateFormatterSchema;
3275
4978
  exports.DuckDuckGoProvider = DuckDuckGoProvider;
3276
4979
  exports.EmailValidatorSchema = EmailValidatorSchema;
4980
+ exports.EmbeddingManager = EmbeddingManager;
3277
4981
  exports.HttpMethod = HttpMethod;
3278
4982
  exports.IpValidatorSchema = IpValidatorSchema;
3279
4983
  exports.MathFunctionsSchema = MathFunctionsSchema;
4984
+ exports.OpenAIEmbeddingProvider = OpenAIEmbeddingProvider;
3280
4985
  exports.PhoneValidatorSchema = PhoneValidatorSchema;
3281
4986
  exports.RandomNumberSchema = RandomNumberSchema;
3282
4987
  exports.SerperProvider = SerperProvider;
@@ -3350,6 +5055,14 @@ exports.createJsonTools = createJsonTools;
3350
5055
  exports.createJsonValidatorTool = createJsonValidatorTool;
3351
5056
  exports.createMathFunctionsTool = createMathFunctionsTool;
3352
5057
  exports.createMathOperationTools = createMathOperationTools;
5058
+ exports.createNeo4jCreateNodeWithEmbeddingTool = createNeo4jCreateNodeWithEmbeddingTool;
5059
+ exports.createNeo4jFindNodesTool = createNeo4jFindNodesTool;
5060
+ exports.createNeo4jGetSchemaTool = createNeo4jGetSchemaTool;
5061
+ exports.createNeo4jQueryTool = createNeo4jQueryTool;
5062
+ exports.createNeo4jTools = createNeo4jTools;
5063
+ exports.createNeo4jTraverseTool = createNeo4jTraverseTool;
5064
+ exports.createNeo4jVectorSearchTool = createNeo4jVectorSearchTool;
5065
+ exports.createNeo4jVectorSearchWithEmbeddingTool = createNeo4jVectorSearchWithEmbeddingTool;
3353
5066
  exports.createObjectOmitTool = createObjectOmitTool;
3354
5067
  exports.createObjectPickTool = createObjectPickTool;
3355
5068
  exports.createPathBasenameTool = createPathBasenameTool;
@@ -3410,6 +5123,7 @@ exports.directoryList = directoryList;
3410
5123
  exports.directoryListSchema = directoryListSchema;
3411
5124
  exports.directoryOperationTools = directoryOperationTools;
3412
5125
  exports.emailValidator = emailValidator;
5126
+ exports.embeddingManager = embeddingManager;
3413
5127
  exports.extractImages = extractImages;
3414
5128
  exports.extractImagesSchema = extractImagesSchema;
3415
5129
  exports.extractLinks = extractLinks;
@@ -3427,10 +5141,19 @@ exports.fileSearch = fileSearch;
3427
5141
  exports.fileSearchSchema = fileSearchSchema;
3428
5142
  exports.fileWriter = fileWriter;
3429
5143
  exports.fileWriterSchema = fileWriterSchema;
5144
+ exports.generateBatchEmbeddings = generateBatchEmbeddings;
5145
+ exports.generateEmbedding = generateEmbedding;
5146
+ exports.getCohereApiKey = getCohereApiKey;
3430
5147
  exports.getConfluencePage = getConfluencePage;
5148
+ exports.getEmbeddingModel = getEmbeddingModel;
5149
+ exports.getEmbeddingProvider = getEmbeddingProvider;
5150
+ exports.getHuggingFaceApiKey = getHuggingFaceApiKey;
5151
+ exports.getOllamaBaseUrl = getOllamaBaseUrl;
5152
+ exports.getOpenAIApiKey = getOpenAIApiKey;
3431
5153
  exports.getSlackChannels = getSlackChannels;
3432
5154
  exports.getSlackMessages = getSlackMessages;
3433
5155
  exports.getSpacePages = getSpacePages;
5156
+ exports.getVoyageApiKey = getVoyageApiKey;
3434
5157
  exports.htmlParser = htmlParser;
3435
5158
  exports.htmlParserSchema = htmlParserSchema;
3436
5159
  exports.htmlParserTools = htmlParserTools;
@@ -3441,7 +5164,12 @@ exports.httpPost = httpPost;
3441
5164
  exports.httpPostSchema = httpPostSchema;
3442
5165
  exports.httpRequestSchema = httpRequestSchema;
3443
5166
  exports.httpTools = httpTools;
5167
+ exports.initializeEmbeddings = initializeEmbeddings;
5168
+ exports.initializeEmbeddingsWithConfig = initializeEmbeddingsWithConfig;
5169
+ exports.initializeFromEnv = initializeFromEnv;
5170
+ exports.initializeNeo4jTools = initializeNeo4jTools;
3444
5171
  exports.ipValidator = ipValidator;
5172
+ exports.isRetryableError = isRetryableError2;
3445
5173
  exports.jsonMerge = jsonMerge;
3446
5174
  exports.jsonMergeSchema = jsonMergeSchema;
3447
5175
  exports.jsonParser = jsonParser;
@@ -3460,6 +5188,23 @@ exports.jsonValidatorSchema = jsonValidatorSchema;
3460
5188
  exports.listConfluenceSpaces = listConfluenceSpaces;
3461
5189
  exports.mathFunctions = mathFunctions;
3462
5190
  exports.mathOperationTools = mathOperationTools;
5191
+ exports.neo4jCoreTools = neo4jCoreTools;
5192
+ exports.neo4jCreateNodeWithEmbedding = neo4jCreateNodeWithEmbedding;
5193
+ exports.neo4jCreateNodeWithEmbeddingSchema = neo4jCreateNodeWithEmbeddingSchema;
5194
+ exports.neo4jFindNodes = neo4jFindNodes;
5195
+ exports.neo4jFindNodesSchema = neo4jFindNodesSchema;
5196
+ exports.neo4jGetSchema = neo4jGetSchema;
5197
+ exports.neo4jGetSchemaSchema = neo4jGetSchemaSchema;
5198
+ exports.neo4jPool = neo4jPool;
5199
+ exports.neo4jQuery = neo4jQuery;
5200
+ exports.neo4jQuerySchema = neo4jQuerySchema;
5201
+ exports.neo4jTools = neo4jTools;
5202
+ exports.neo4jTraverse = neo4jTraverse;
5203
+ exports.neo4jTraverseSchema = neo4jTraverseSchema;
5204
+ exports.neo4jVectorSearch = neo4jVectorSearch;
5205
+ exports.neo4jVectorSearchSchema = neo4jVectorSearchSchema;
5206
+ exports.neo4jVectorSearchWithEmbedding = neo4jVectorSearchWithEmbedding;
5207
+ exports.neo4jVectorSearchWithEmbeddingSchema = neo4jVectorSearchWithEmbeddingSchema;
3463
5208
  exports.notifySlack = notifySlack;
3464
5209
  exports.objectOmit = objectOmit;
3465
5210
  exports.objectOmitSchema = objectOmitSchema;
@@ -3484,6 +5229,7 @@ exports.pathResolveSchema = pathResolveSchema;
3484
5229
  exports.pathUtilityTools = pathUtilityTools;
3485
5230
  exports.phoneValidator = phoneValidator;
3486
5231
  exports.randomNumber = randomNumber;
5232
+ exports.retryWithBackoff = retryWithBackoff2;
3487
5233
  exports.scraperTools = scraperTools;
3488
5234
  exports.searchConfluence = searchConfluence;
3489
5235
  exports.searchResultSchema = searchResultSchema;
@@ -3509,6 +5255,8 @@ exports.urlValidatorSchema = urlValidatorSchema;
3509
5255
  exports.urlValidatorSimple = urlValidatorSimple;
3510
5256
  exports.urlValidatorTools = urlValidatorTools;
3511
5257
  exports.uuidValidator = uuidValidator;
5258
+ exports.validateBatch = validateBatch;
5259
+ exports.validateText = validateText;
3512
5260
  exports.validationTools = validationTools;
3513
5261
  exports.webScraper = webScraper;
3514
5262
  exports.webScraperSchema = webScraperSchema;