@agentforge/tools 0.12.5 → 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.js CHANGED
@@ -1,11 +1,12 @@
1
1
  import { toolBuilder, ToolCategory, LogLevel, createLogger } from '@agentforge/core';
2
- import axios12 from 'axios';
2
+ import axios16 from 'axios';
3
3
  import { z } from 'zod';
4
4
  import * as cheerio from 'cheerio';
5
5
  import { WebClient } from '@slack/web-api';
6
6
  import { parse } from 'csv-parse/sync';
7
7
  import { stringify } from 'csv-stringify/sync';
8
8
  import { XMLParser, XMLBuilder } from 'fast-xml-parser';
9
+ import neo4j, { auth, Integer, Node, Relationship, Path } from 'neo4j-driver';
9
10
  import { promises } from 'fs';
10
11
  import * as path7 from 'path';
11
12
  import { format, parse as parse$1, isValid, add, sub, differenceInDays, differenceInHours, differenceInMinutes, isAfter, isBefore } from 'date-fns';
@@ -45,7 +46,7 @@ function createHttpClientTool(defaultTimeout = 3e4, defaultHeaders = {}) {
45
46
  validateStatus: () => true
46
47
  // Don't throw on any status code
47
48
  };
48
- const response = await axios12(config);
49
+ const response = await axios16(config);
49
50
  return {
50
51
  status: response.status,
51
52
  statusText: response.statusText,
@@ -58,7 +59,7 @@ function createHttpClientTool(defaultTimeout = 3e4, defaultHeaders = {}) {
58
59
  }
59
60
  function createHttpGetTool(defaultTimeout = 3e4, defaultHeaders = {}) {
60
61
  return toolBuilder().name("http-get").description("Make a simple HTTP GET request to a URL and return the response data.").category(ToolCategory.WEB).tags(["http", "get", "fetch", "web"]).schema(httpGetSchema).implement(async (input) => {
61
- const response = await axios12.get(input.url, {
62
+ const response = await axios16.get(input.url, {
62
63
  headers: { ...defaultHeaders, ...input.headers },
63
64
  params: input.params,
64
65
  timeout: defaultTimeout
@@ -68,7 +69,7 @@ function createHttpGetTool(defaultTimeout = 3e4, defaultHeaders = {}) {
68
69
  }
69
70
  function createHttpPostTool(defaultTimeout = 3e4, defaultHeaders = {}) {
70
71
  return toolBuilder().name("http-post").description("Make a simple HTTP POST request with JSON body and return the response data.").category(ToolCategory.WEB).tags(["http", "post", "api", "web"]).schema(httpPostSchema).implement(async (input) => {
71
- const response = await axios12.post(input.url, input.body, {
72
+ const response = await axios16.post(input.url, input.body, {
72
73
  headers: {
73
74
  "Content-Type": "application/json",
74
75
  ...defaultHeaders,
@@ -105,7 +106,7 @@ var webScraperSchema = z.object({
105
106
  });
106
107
  function createWebScraperTool(defaultTimeout = 3e4, userAgent = "Mozilla/5.0 (compatible; AgentForge/1.0; +https://agentforge.dev)") {
107
108
  return 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(ToolCategory.WEB).tags(["scraper", "web", "html", "extract", "parse"]).schema(webScraperSchema).implement(async (input) => {
108
- const response = await axios12.get(input.url, {
109
+ const response = await axios16.get(input.url, {
109
110
  timeout: input.timeout || defaultTimeout,
110
111
  headers: {
111
112
  "User-Agent": userAgent
@@ -495,7 +496,7 @@ var DuckDuckGoProvider = class {
495
496
  async search(query, maxResults, timeout = DEFAULT_TIMEOUT) {
496
497
  return retryWithBackoff(async () => {
497
498
  try {
498
- const response = await axios12.get(
499
+ const response = await axios16.get(
499
500
  "https://api.duckduckgo.com/",
500
501
  {
501
502
  params: {
@@ -599,7 +600,7 @@ var SerperProvider = class {
599
600
  }
600
601
  return retryWithBackoff(async () => {
601
602
  try {
602
- const response = await axios12.post(
603
+ const response = await axios16.post(
603
604
  "https://google.serper.dev/search",
604
605
  {
605
606
  q: query,
@@ -779,7 +780,7 @@ function getDefaultSlackClient() {
779
780
  }
780
781
  };
781
782
  }
782
- function createSendSlackMessageTool(getSlackClient, logger4) {
783
+ function createSendSlackMessageTool(getSlackClient, logger8) {
783
784
  return toolBuilder().name("send-slack-message").description("Send a message to a Slack channel for team communication and notifications").category(ToolCategory.WEB).tags(["slack", "messaging", "communication"]).usageNotes(
784
785
  "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."
785
786
  ).suggests(["get-slack-channels"]).schema(
@@ -788,7 +789,7 @@ function createSendSlackMessageTool(getSlackClient, logger4) {
788
789
  message: z.string().describe("Message content to send")
789
790
  })
790
791
  ).implementSafe(async ({ channel, message }) => {
791
- logger4.info("send-slack-message called", { channel, messageLength: message.length });
792
+ logger8.info("send-slack-message called", { channel, messageLength: message.length });
792
793
  try {
793
794
  const { client: slack, config } = getSlackClient();
794
795
  const result = await slack.chat.postMessage({
@@ -797,7 +798,7 @@ function createSendSlackMessageTool(getSlackClient, logger4) {
797
798
  username: config.botName,
798
799
  icon_emoji: config.botIcon
799
800
  });
800
- logger4.info("send-slack-message result", {
801
+ logger8.info("send-slack-message result", {
801
802
  channel: result.channel,
802
803
  timestamp: result.ts,
803
804
  messageLength: message.length,
@@ -810,7 +811,7 @@ function createSendSlackMessageTool(getSlackClient, logger4) {
810
811
  message_id: result.ts
811
812
  };
812
813
  } catch (error) {
813
- logger4.error("send-slack-message failed", {
814
+ logger8.error("send-slack-message failed", {
814
815
  channel,
815
816
  error: error.message,
816
817
  data: error.data
@@ -819,7 +820,7 @@ function createSendSlackMessageTool(getSlackClient, logger4) {
819
820
  }
820
821
  }).build();
821
822
  }
822
- function createNotifySlackTool(getSlackClient, logger4) {
823
+ function createNotifySlackTool(getSlackClient, logger8) {
823
824
  return toolBuilder().name("notify-slack").description("Send a notification to a Slack channel with optional @mentions for urgent alerts").category(ToolCategory.WEB).tags(["slack", "notification", "alert"]).usageNotes(
824
825
  "Use this for urgent notifications that require @mentions. For general messages without mentions, use send-slack-message instead."
825
826
  ).suggests(["get-slack-channels"]).schema(
@@ -829,7 +830,7 @@ function createNotifySlackTool(getSlackClient, logger4) {
829
830
  mentions: z.array(z.string()).optional().describe("List of usernames to mention (without @)")
830
831
  })
831
832
  ).implementSafe(async ({ channel, message, mentions = [] }) => {
832
- logger4.info("notify-slack called", {
833
+ logger8.info("notify-slack called", {
833
834
  channel,
834
835
  messageLength: message.length,
835
836
  mentionCount: mentions.length
@@ -843,7 +844,7 @@ function createNotifySlackTool(getSlackClient, logger4) {
843
844
  username: config.botName,
844
845
  icon_emoji: config.botIcon
845
846
  });
846
- logger4.info("notify-slack result", {
847
+ logger8.info("notify-slack result", {
847
848
  channel: result.channel,
848
849
  timestamp: result.ts,
849
850
  mentions: mentions.length
@@ -857,7 +858,7 @@ function createNotifySlackTool(getSlackClient, logger4) {
857
858
  };
858
859
  }).build();
859
860
  }
860
- function createGetSlackChannelsTool(getSlackClient, logger4) {
861
+ function createGetSlackChannelsTool(getSlackClient, logger8) {
861
862
  return toolBuilder().name("get-slack-channels").description("Get a list of available Slack channels to find the right channel for messaging").category(ToolCategory.WEB).tags(["slack", "channels", "list"]).usageNotes(
862
863
  "Use this first to discover available channels before sending messages. Helps ensure you are sending to the correct channel."
863
864
  ).follows(["send-slack-message", "notify-slack"]).schema(
@@ -865,7 +866,7 @@ function createGetSlackChannelsTool(getSlackClient, logger4) {
865
866
  include_private: z.boolean().optional().describe("Include private channels (default: false)")
866
867
  })
867
868
  ).implementSafe(async ({ include_private = false }) => {
868
- logger4.info("get-slack-channels called", { include_private });
869
+ logger8.info("get-slack-channels called", { include_private });
869
870
  const { client: slack } = getSlackClient();
870
871
  const publicChannels = await slack.conversations.list({
871
872
  types: "public_channel",
@@ -879,7 +880,7 @@ function createGetSlackChannelsTool(getSlackClient, logger4) {
879
880
  });
880
881
  allChannels = [...allChannels, ...privateChannels.channels || []];
881
882
  }
882
- logger4.info("get-slack-channels result", {
883
+ logger8.info("get-slack-channels result", {
883
884
  channelCount: allChannels.length,
884
885
  includePrivate: include_private
885
886
  });
@@ -894,7 +895,7 @@ function createGetSlackChannelsTool(getSlackClient, logger4) {
894
895
  };
895
896
  }).build();
896
897
  }
897
- function createGetSlackMessagesTool(getSlackClient, logger4) {
898
+ function createGetSlackMessagesTool(getSlackClient, logger8) {
898
899
  return toolBuilder().name("get-slack-messages").description("Retrieve message history from a Slack channel to read recent conversations").category(ToolCategory.WEB).tags(["slack", "messages", "history", "read"]).usageNotes(
899
900
  "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)."
900
901
  ).suggests(["get-slack-channels"]).schema(
@@ -903,7 +904,7 @@ function createGetSlackMessagesTool(getSlackClient, logger4) {
903
904
  limit: z.number().int().min(1).max(100).optional().describe("Number of messages to retrieve (default: 20, max: 100)")
904
905
  })
905
906
  ).implementSafe(async ({ channel, limit = 20 }) => {
906
- logger4.info("get-slack-messages called", { channel, limit });
907
+ logger8.info("get-slack-messages called", { channel, limit });
907
908
  try {
908
909
  const { client: slack } = getSlackClient();
909
910
  let channelId = channel;
@@ -914,7 +915,7 @@ function createGetSlackMessagesTool(getSlackClient, logger4) {
914
915
  });
915
916
  const found = channels.channels?.find((c) => c.name === channel);
916
917
  if (!found) {
917
- logger4.error("get-slack-messages: channel not found", { channel });
918
+ logger8.error("get-slack-messages: channel not found", { channel });
918
919
  throw new Error(
919
920
  `Channel '${channel}' not found. Use get-slack-channels to see available channels.`
920
921
  );
@@ -926,7 +927,7 @@ function createGetSlackMessagesTool(getSlackClient, logger4) {
926
927
  limit: Math.min(limit, 100)
927
928
  // Cap at 100 for performance
928
929
  });
929
- logger4.info("get-slack-messages result", {
930
+ logger8.info("get-slack-messages result", {
930
931
  channel: channelId,
931
932
  messageCount: result.messages?.length || 0,
932
933
  limit
@@ -944,7 +945,7 @@ function createGetSlackMessagesTool(getSlackClient, logger4) {
944
945
  })) || []
945
946
  };
946
947
  } catch (error) {
947
- logger4.error("get-slack-messages failed", {
948
+ logger8.error("get-slack-messages failed", {
948
949
  channel,
949
950
  error: error.message,
950
951
  data: error.data
@@ -1007,8 +1008,8 @@ function createGetConfiguredAuth(apiKey, email, siteUrl) {
1007
1008
  function createGetConfiguredAuthHeader(getConfiguredAuth) {
1008
1009
  return function getConfiguredAuthHeader() {
1009
1010
  const { ATLASSIAN_API_KEY, ATLASSIAN_EMAIL } = getConfiguredAuth();
1010
- const auth = Buffer.from(`${ATLASSIAN_EMAIL}:${ATLASSIAN_API_KEY}`).toString("base64");
1011
- return `Basic ${auth}`;
1011
+ const auth2 = Buffer.from(`${ATLASSIAN_EMAIL}:${ATLASSIAN_API_KEY}`).toString("base64");
1012
+ return `Basic ${auth2}`;
1012
1013
  };
1013
1014
  }
1014
1015
  function getConfig() {
@@ -1022,18 +1023,18 @@ function getConfig() {
1022
1023
  }
1023
1024
  function getAuthHeader() {
1024
1025
  const { ATLASSIAN_API_KEY, ATLASSIAN_EMAIL } = getConfig();
1025
- const auth = Buffer.from(`${ATLASSIAN_EMAIL}:${ATLASSIAN_API_KEY}`).toString("base64");
1026
- return `Basic ${auth}`;
1026
+ const auth2 = Buffer.from(`${ATLASSIAN_EMAIL}:${ATLASSIAN_API_KEY}`).toString("base64");
1027
+ return `Basic ${auth2}`;
1027
1028
  }
1028
- function createSearchConfluenceTool(getAuth, getAuthHeader2, logger4) {
1029
+ function createSearchConfluenceTool(getAuth, getAuthHeader2, logger8) {
1029
1030
  return toolBuilder().name("search-confluence").description("Search for pages in Confluence using keywords or CQL (Confluence Query Language). Returns matching pages with titles, IDs, and excerpts.").category(ToolCategory.WEB).tag("confluence").tag("search").tag("knowledge-base").usageNotes("Use this to find relevant documentation, policies, or information in Confluence. You can search by keywords or use CQL for advanced queries (e.g., 'space=AI AND type=page'). Use get-confluence-page to retrieve full content of specific pages.").suggests(["get-confluence-page"]).schema(z.object({
1030
1031
  query: z.string().describe("Search query or CQL expression (e.g., 'payment processing' or 'space=BL3 AND title~payment')"),
1031
1032
  limit: z.number().optional().describe("Maximum number of results to return (default: 10, max: 25)")
1032
1033
  })).implement(async ({ query, limit = 10 }) => {
1033
- logger4.info("search-confluence called", { query, limit });
1034
+ logger8.info("search-confluence called", { query, limit });
1034
1035
  try {
1035
1036
  const { ATLASSIAN_SITE_URL } = getAuth();
1036
- const response = await axios12.get(`${ATLASSIAN_SITE_URL}/wiki/rest/api/content/search`, {
1037
+ const response = await axios16.get(`${ATLASSIAN_SITE_URL}/wiki/rest/api/content/search`, {
1037
1038
  headers: {
1038
1039
  Authorization: getAuthHeader2(),
1039
1040
  Accept: "application/json"
@@ -1055,13 +1056,13 @@ function createSearchConfluenceTool(getAuth, getAuthHeader2, logger4) {
1055
1056
  lastModified: page.version?.when || ""
1056
1057
  }));
1057
1058
  if (results.length === 0) {
1058
- logger4.warn("search-confluence returned NO RESULTS - this is a valid outcome, agent should not retry", {
1059
+ logger8.warn("search-confluence returned NO RESULTS - this is a valid outcome, agent should not retry", {
1059
1060
  query,
1060
1061
  limit,
1061
1062
  totalSize: response.data.totalSize
1062
1063
  });
1063
1064
  } else {
1064
- logger4.info("search-confluence result", {
1065
+ logger8.info("search-confluence result", {
1065
1066
  query,
1066
1067
  resultCount: results.length,
1067
1068
  totalSize: response.data.totalSize,
@@ -1076,7 +1077,7 @@ function createSearchConfluenceTool(getAuth, getAuthHeader2, logger4) {
1076
1077
  results
1077
1078
  });
1078
1079
  } catch (error) {
1079
- logger4.error("search-confluence error", {
1080
+ logger8.error("search-confluence error", {
1080
1081
  query,
1081
1082
  error: error.response?.data?.message || error.message,
1082
1083
  status: error.response?.status
@@ -1088,14 +1089,14 @@ function createSearchConfluenceTool(getAuth, getAuthHeader2, logger4) {
1088
1089
  }
1089
1090
  }).build();
1090
1091
  }
1091
- function createGetConfluencePageTool(getAuth, getAuthHeader2, logger4) {
1092
+ function createGetConfluencePageTool(getAuth, getAuthHeader2, logger8) {
1092
1093
  return toolBuilder().name("get-confluence-page").description("Get the full content of a specific Confluence page by its ID. Returns the page title, content (in storage format), space, and metadata.").category(ToolCategory.WEB).tag("confluence").tag("page").tag("content").usageNotes("Use this after search-confluence to retrieve the full content of a specific page. The page ID can be found in search results.").requires(["search-confluence"]).schema(z.object({
1093
1094
  page_id: z.string().describe("The Confluence page ID (from search results)")
1094
1095
  })).implement(async ({ page_id }) => {
1095
- logger4.info("get-confluence-page called", { page_id });
1096
+ logger8.info("get-confluence-page called", { page_id });
1096
1097
  try {
1097
1098
  const { ATLASSIAN_SITE_URL } = getAuth();
1098
- const response = await axios12.get(`${ATLASSIAN_SITE_URL}/wiki/rest/api/content/${page_id}`, {
1099
+ const response = await axios16.get(`${ATLASSIAN_SITE_URL}/wiki/rest/api/content/${page_id}`, {
1099
1100
  headers: {
1100
1101
  Authorization: getAuthHeader2(),
1101
1102
  Accept: "application/json"
@@ -1105,7 +1106,7 @@ function createGetConfluencePageTool(getAuth, getAuthHeader2, logger4) {
1105
1106
  }
1106
1107
  });
1107
1108
  const page = response.data;
1108
- logger4.info("get-confluence-page result", {
1109
+ logger8.info("get-confluence-page result", {
1109
1110
  page_id,
1110
1111
  title: page.title,
1111
1112
  space: page.space?.name,
@@ -1127,7 +1128,7 @@ function createGetConfluencePageTool(getAuth, getAuthHeader2, logger4) {
1127
1128
  }
1128
1129
  });
1129
1130
  } catch (error) {
1130
- logger4.error("get-confluence-page error", {
1131
+ logger8.error("get-confluence-page error", {
1131
1132
  page_id,
1132
1133
  error: error.response?.data?.message || error.message,
1133
1134
  status: error.response?.status
@@ -1139,14 +1140,14 @@ function createGetConfluencePageTool(getAuth, getAuthHeader2, logger4) {
1139
1140
  }
1140
1141
  }).build();
1141
1142
  }
1142
- function createListConfluenceSpacesTool(getAuth, getAuthHeader2, logger4) {
1143
+ function createListConfluenceSpacesTool(getAuth, getAuthHeader2, logger8) {
1143
1144
  return toolBuilder().name("list-confluence-spaces").description("List all available Confluence spaces. Returns space names, keys, types, and descriptions to help identify where to search for information.").category(ToolCategory.WEB).tag("confluence").tag("spaces").tag("list").usageNotes("Use this first to discover available spaces before searching. Helps narrow down searches to specific areas (e.g., 'AI', 'BL3', 'Finance').").follows(["search-confluence"]).schema(z.object({
1144
1145
  limit: z.number().optional().describe("Maximum number of spaces to return (default: 25)")
1145
1146
  })).implement(async ({ limit = 25 }) => {
1146
- logger4.info("list-confluence-spaces called", { limit });
1147
+ logger8.info("list-confluence-spaces called", { limit });
1147
1148
  try {
1148
1149
  const { ATLASSIAN_SITE_URL } = getAuth();
1149
- const response = await axios12.get(`${ATLASSIAN_SITE_URL}/wiki/rest/api/space`, {
1150
+ const response = await axios16.get(`${ATLASSIAN_SITE_URL}/wiki/rest/api/space`, {
1150
1151
  headers: {
1151
1152
  Authorization: getAuthHeader2(),
1152
1153
  Accept: "application/json"
@@ -1162,7 +1163,7 @@ function createListConfluenceSpacesTool(getAuth, getAuthHeader2, logger4) {
1162
1163
  description: space.description?.plain?.value || "",
1163
1164
  url: `${ATLASSIAN_SITE_URL}/wiki${space._links.webui}`
1164
1165
  }));
1165
- logger4.info("list-confluence-spaces result", {
1166
+ logger8.info("list-confluence-spaces result", {
1166
1167
  spaceCount: spaces.length,
1167
1168
  spaceKeys: spaces.map((s) => s.key).slice(0, 5)
1168
1169
  // Log first 5 space keys
@@ -1173,7 +1174,7 @@ function createListConfluenceSpacesTool(getAuth, getAuthHeader2, logger4) {
1173
1174
  spaces
1174
1175
  });
1175
1176
  } catch (error) {
1176
- logger4.error("list-confluence-spaces error", {
1177
+ logger8.error("list-confluence-spaces error", {
1177
1178
  error: error.response?.data?.message || error.message,
1178
1179
  status: error.response?.status
1179
1180
  });
@@ -1184,15 +1185,15 @@ function createListConfluenceSpacesTool(getAuth, getAuthHeader2, logger4) {
1184
1185
  }
1185
1186
  }).build();
1186
1187
  }
1187
- function createGetSpacePagesTool(getAuth, getAuthHeader2, logger4) {
1188
+ function createGetSpacePagesTool(getAuth, getAuthHeader2, logger8) {
1188
1189
  return toolBuilder().name("get-space-pages").description("Get all pages from a specific Confluence space by space key. Useful for browsing content in a particular area.").category(ToolCategory.WEB).tag("confluence").tag("space").tag("pages").usageNotes("Use this to explore all pages in a specific space. Get the space key from list-confluence-spaces first.").requires(["list-confluence-spaces"]).schema(z.object({
1189
1190
  space_key: z.string().describe("The space key (e.g., 'AI', 'BL3', 'FIN')"),
1190
1191
  limit: z.number().optional().describe("Maximum number of pages to return (default: 25)")
1191
1192
  })).implement(async ({ space_key, limit = 25 }) => {
1192
- logger4.info("get-space-pages called", { space_key, limit });
1193
+ logger8.info("get-space-pages called", { space_key, limit });
1193
1194
  try {
1194
1195
  const { ATLASSIAN_SITE_URL } = getAuth();
1195
- const response = await axios12.get(`${ATLASSIAN_SITE_URL}/wiki/rest/api/content`, {
1196
+ const response = await axios16.get(`${ATLASSIAN_SITE_URL}/wiki/rest/api/content`, {
1196
1197
  headers: {
1197
1198
  Authorization: getAuthHeader2(),
1198
1199
  Accept: "application/json"
@@ -1211,12 +1212,12 @@ function createGetSpacePagesTool(getAuth, getAuthHeader2, logger4) {
1211
1212
  lastModified: page.version?.when || ""
1212
1213
  }));
1213
1214
  if (pages.length === 0) {
1214
- logger4.warn("get-space-pages returned NO PAGES - this is a valid outcome, agent should not retry", {
1215
+ logger8.warn("get-space-pages returned NO PAGES - this is a valid outcome, agent should not retry", {
1215
1216
  space_key,
1216
1217
  limit
1217
1218
  });
1218
1219
  } else {
1219
- logger4.info("get-space-pages result", {
1220
+ logger8.info("get-space-pages result", {
1220
1221
  space_key,
1221
1222
  pageCount: pages.length,
1222
1223
  titles: pages.map((p) => p.title).slice(0, 3)
@@ -1230,7 +1231,7 @@ function createGetSpacePagesTool(getAuth, getAuthHeader2, logger4) {
1230
1231
  pages
1231
1232
  });
1232
1233
  } catch (error) {
1233
- logger4.error("get-space-pages error", {
1234
+ logger8.error("get-space-pages error", {
1234
1235
  space_key,
1235
1236
  error: error.response?.data?.message || error.message,
1236
1237
  status: error.response?.status
@@ -1242,14 +1243,14 @@ function createGetSpacePagesTool(getAuth, getAuthHeader2, logger4) {
1242
1243
  }
1243
1244
  }).build();
1244
1245
  }
1245
- function createCreateConfluencePageTool(getAuth, getAuthHeader2, logger4) {
1246
+ function createCreateConfluencePageTool(getAuth, getAuthHeader2, logger8) {
1246
1247
  return toolBuilder().name("create-confluence-page").description("Create a new page in a Confluence space. Requires space key, page title, and content (in HTML storage format).").category(ToolCategory.WEB).tag("confluence").tag("create").tag("write").usageNotes("Use this to create new documentation pages. Content should be in Confluence storage format (HTML). Get the space key from list-confluence-spaces first. Be mindful of creating duplicate pages.").requires(["list-confluence-spaces"]).schema(z.object({
1247
1248
  space_key: z.string().describe("The space key where the page will be created (e.g., 'AI', 'BL3')"),
1248
1249
  title: z.string().describe("The title of the new page"),
1249
1250
  content: z.string().describe("The page content in HTML format (Confluence storage format)"),
1250
1251
  parent_page_id: z.string().optional().describe("Optional parent page ID to create this as a child page")
1251
1252
  })).implement(async ({ space_key, title, content, parent_page_id }) => {
1252
- logger4.info("create-confluence-page called", { space_key, title, hasParent: !!parent_page_id });
1253
+ logger8.info("create-confluence-page called", { space_key, title, hasParent: !!parent_page_id });
1253
1254
  try {
1254
1255
  const { ATLASSIAN_SITE_URL } = getAuth();
1255
1256
  const pageData = {
@@ -1266,7 +1267,7 @@ function createCreateConfluencePageTool(getAuth, getAuthHeader2, logger4) {
1266
1267
  if (parent_page_id) {
1267
1268
  pageData.ancestors = [{ id: parent_page_id }];
1268
1269
  }
1269
- const response = await axios12.post(
1270
+ const response = await axios16.post(
1270
1271
  `${ATLASSIAN_SITE_URL}/wiki/rest/api/content`,
1271
1272
  pageData,
1272
1273
  {
@@ -1276,7 +1277,7 @@ function createCreateConfluencePageTool(getAuth, getAuthHeader2, logger4) {
1276
1277
  }
1277
1278
  }
1278
1279
  );
1279
- logger4.info("create-confluence-page result", {
1280
+ logger8.info("create-confluence-page result", {
1280
1281
  page_id: response.data.id,
1281
1282
  title: response.data.title,
1282
1283
  space: space_key
@@ -1292,7 +1293,7 @@ function createCreateConfluencePageTool(getAuth, getAuthHeader2, logger4) {
1292
1293
  }
1293
1294
  });
1294
1295
  } catch (error) {
1295
- logger4.error("create-confluence-page error", {
1296
+ logger8.error("create-confluence-page error", {
1296
1297
  space_key,
1297
1298
  title,
1298
1299
  error: error.response?.data?.message || error.message,
@@ -1305,16 +1306,16 @@ function createCreateConfluencePageTool(getAuth, getAuthHeader2, logger4) {
1305
1306
  }
1306
1307
  }).build();
1307
1308
  }
1308
- function createUpdateConfluencePageTool(getAuth, getAuthHeader2, logger4) {
1309
+ function createUpdateConfluencePageTool(getAuth, getAuthHeader2, logger8) {
1309
1310
  return toolBuilder().name("update-confluence-page").description("Update an existing Confluence page's content. Requires page ID, new title, and new content.").category(ToolCategory.WEB).tag("confluence").tag("update").tag("write").usageNotes("Use this to update existing documentation. You must provide the page ID (from search results). The tool will automatically handle version incrementing. Always get the current page content first to avoid overwriting important information.").requires(["get-confluence-page"]).schema(z.object({
1310
1311
  page_id: z.string().describe("The ID of the page to update"),
1311
1312
  title: z.string().describe("The new title for the page"),
1312
1313
  content: z.string().describe("The new content in HTML format (Confluence storage format)")
1313
1314
  })).implement(async ({ page_id, title, content }) => {
1314
- logger4.info("update-confluence-page called", { page_id, title });
1315
+ logger8.info("update-confluence-page called", { page_id, title });
1315
1316
  try {
1316
1317
  const { ATLASSIAN_SITE_URL } = getAuth();
1317
- const getResponse = await axios12.get(
1318
+ const getResponse = await axios16.get(
1318
1319
  `${ATLASSIAN_SITE_URL}/wiki/rest/api/content/${page_id}`,
1319
1320
  {
1320
1321
  headers: {
@@ -1324,7 +1325,7 @@ function createUpdateConfluencePageTool(getAuth, getAuthHeader2, logger4) {
1324
1325
  }
1325
1326
  );
1326
1327
  const currentVersion = getResponse.data.version.number;
1327
- const updateResponse = await axios12.put(
1328
+ const updateResponse = await axios16.put(
1328
1329
  `${ATLASSIAN_SITE_URL}/wiki/rest/api/content/${page_id}`,
1329
1330
  {
1330
1331
  type: "page",
@@ -1344,7 +1345,7 @@ function createUpdateConfluencePageTool(getAuth, getAuthHeader2, logger4) {
1344
1345
  }
1345
1346
  }
1346
1347
  );
1347
- logger4.info("update-confluence-page result", {
1348
+ logger8.info("update-confluence-page result", {
1348
1349
  page_id,
1349
1350
  title: updateResponse.data.title,
1350
1351
  previousVersion: currentVersion,
@@ -1361,7 +1362,7 @@ function createUpdateConfluencePageTool(getAuth, getAuthHeader2, logger4) {
1361
1362
  }
1362
1363
  });
1363
1364
  } catch (error) {
1364
- logger4.error("update-confluence-page error", {
1365
+ logger8.error("update-confluence-page error", {
1365
1366
  page_id,
1366
1367
  title,
1367
1368
  error: error.response?.data?.message || error.message,
@@ -1374,15 +1375,15 @@ function createUpdateConfluencePageTool(getAuth, getAuthHeader2, logger4) {
1374
1375
  }
1375
1376
  }).build();
1376
1377
  }
1377
- function createArchiveConfluencePageTool(getAuth, getAuthHeader2, logger4) {
1378
+ function createArchiveConfluencePageTool(getAuth, getAuthHeader2, logger8) {
1378
1379
  return toolBuilder().name("archive-confluence-page").description("Archive a Confluence page by moving it to trash. The page can be restored by space admins. Note: UI may require a note explaining why the page was archived.").category(ToolCategory.WEB).tag("confluence").tag("archive").tag("delete").usageNotes("Use this to archive outdated or obsolete documentation. The page is moved to trash, not permanently deleted. Space admins can restore it if needed. Be very careful - only archive pages that are truly obsolete.").conflicts(["create-confluence-page"]).schema(z.object({
1379
1380
  page_id: z.string().describe("The ID of the page to archive"),
1380
1381
  reason: z.string().optional().describe("Optional reason for archiving (for audit trail)")
1381
1382
  })).implement(async ({ page_id, reason }) => {
1382
- logger4.info("archive-confluence-page called", { page_id, reason });
1383
+ logger8.info("archive-confluence-page called", { page_id, reason });
1383
1384
  try {
1384
1385
  const { ATLASSIAN_SITE_URL } = getAuth();
1385
- const getResponse = await axios12.get(
1386
+ const getResponse = await axios16.get(
1386
1387
  `${ATLASSIAN_SITE_URL}/wiki/rest/api/content/${page_id}`,
1387
1388
  {
1388
1389
  headers: {
@@ -1393,7 +1394,7 @@ function createArchiveConfluencePageTool(getAuth, getAuthHeader2, logger4) {
1393
1394
  );
1394
1395
  const currentVersion = getResponse.data.version.number;
1395
1396
  const pageData = getResponse.data;
1396
- await axios12.put(
1397
+ await axios16.put(
1397
1398
  `${ATLASSIAN_SITE_URL}/wiki/rest/api/content/${page_id}`,
1398
1399
  {
1399
1400
  version: { number: currentVersion + 1 },
@@ -1410,7 +1411,7 @@ function createArchiveConfluencePageTool(getAuth, getAuthHeader2, logger4) {
1410
1411
  }
1411
1412
  }
1412
1413
  );
1413
- logger4.info("archive-confluence-page result", {
1414
+ logger8.info("archive-confluence-page result", {
1414
1415
  page_id,
1415
1416
  title: pageData.title,
1416
1417
  previousVersion: currentVersion,
@@ -1428,7 +1429,7 @@ function createArchiveConfluencePageTool(getAuth, getAuthHeader2, logger4) {
1428
1429
  }
1429
1430
  });
1430
1431
  } catch (error) {
1431
- logger4.error("archive-confluence-page error", {
1432
+ logger8.error("archive-confluence-page error", {
1432
1433
  page_id,
1433
1434
  error: error.response?.data?.message || error.message,
1434
1435
  status: error.response?.status
@@ -2025,6 +2026,1706 @@ function createTransformerTools(config = {}) {
2025
2026
  createObjectOmitTool()
2026
2027
  ];
2027
2028
  }
2029
+ var neo4jQuerySchema = z.object({
2030
+ cypher: z.string().describe("Cypher query to execute"),
2031
+ parameters: z.record(z.any()).optional().describe("Query parameters for parameterized queries"),
2032
+ database: z.string().optional().describe("Database name (defaults to configured database)")
2033
+ });
2034
+ var neo4jGetSchemaSchema = z.object({
2035
+ database: z.string().optional().describe("Database name (defaults to configured database)")
2036
+ });
2037
+ var neo4jFindNodesSchema = z.object({
2038
+ label: z.string().describe("Node label to search for"),
2039
+ properties: z.record(z.any()).optional().describe("Properties to match (key-value pairs)"),
2040
+ limit: z.number().default(100).describe("Maximum number of nodes to return"),
2041
+ database: z.string().optional().describe("Database name (defaults to configured database)")
2042
+ });
2043
+ var neo4jTraverseSchema = z.object({
2044
+ startNodeId: z.union([
2045
+ z.string().describe("Node ID as string"),
2046
+ z.number().describe("Node ID as number")
2047
+ ]).describe("ID of the starting node"),
2048
+ relationshipType: z.string().optional().describe("Type of relationship to follow (optional, follows all if not specified)"),
2049
+ direction: z.enum(["outgoing", "incoming", "both"]).default("outgoing").describe("Direction to traverse"),
2050
+ maxDepth: z.number().default(1).describe("Maximum depth to traverse"),
2051
+ limit: z.number().default(100).describe("Maximum number of nodes to return"),
2052
+ database: z.string().optional().describe("Database name (defaults to configured database)")
2053
+ });
2054
+ var neo4jVectorSearchSchema = z.object({
2055
+ indexName: z.string().describe("Name of the vector index to search"),
2056
+ queryVector: z.array(z.number().describe("Vector dimension value")).describe("Query vector for similarity search"),
2057
+ limit: z.number().default(10).describe("Maximum number of results to return"),
2058
+ database: z.string().optional().describe("Database name (defaults to configured database)")
2059
+ });
2060
+ var neo4jVectorSearchWithEmbeddingSchema = z.object({
2061
+ indexName: z.string().describe("Name of the vector index to search"),
2062
+ queryText: z.string().describe("Text to generate embedding from for similarity search"),
2063
+ limit: z.number().default(10).describe("Maximum number of results to return"),
2064
+ model: z.string().optional().describe("Embedding model to use (defaults to configured model)"),
2065
+ database: z.string().optional().describe("Database name (defaults to configured database)")
2066
+ });
2067
+ var neo4jCreateNodeWithEmbeddingSchema = z.object({
2068
+ label: z.string().describe("Node label"),
2069
+ properties: z.record(z.string(), z.any().describe("Property value")).describe("Node properties (key-value pairs)"),
2070
+ textProperty: z.string().describe("Name of the property containing text to embed"),
2071
+ embeddingProperty: z.string().default("embedding").describe("Name of the property to store the embedding vector"),
2072
+ model: z.string().optional().describe("Embedding model to use (defaults to configured model)"),
2073
+ database: z.string().optional().describe("Database name (defaults to configured database)")
2074
+ });
2075
+ var logger3 = createLogger("agentforge:tools:neo4j:pool");
2076
+ var Neo4jConnectionPool = class {
2077
+ driver = null;
2078
+ config = null;
2079
+ /**
2080
+ * Initialize the connection pool
2081
+ */
2082
+ async initialize(config) {
2083
+ const startTime = Date.now();
2084
+ logger3.debug("Initializing Neo4j connection pool", {
2085
+ uri: config.uri,
2086
+ username: config.username,
2087
+ database: config.database || "neo4j",
2088
+ maxConnectionPoolSize: config.maxConnectionPoolSize || 50,
2089
+ connectionTimeout: config.connectionTimeout || 3e4
2090
+ });
2091
+ if (this.driver) {
2092
+ logger3.debug("Closing existing connection before reinitializing");
2093
+ await this.close();
2094
+ }
2095
+ this.config = config;
2096
+ this.driver = neo4j.driver(
2097
+ config.uri,
2098
+ auth.basic(config.username, config.password),
2099
+ {
2100
+ maxConnectionPoolSize: config.maxConnectionPoolSize || 50,
2101
+ connectionTimeout: config.connectionTimeout || 3e4
2102
+ }
2103
+ );
2104
+ await this.verifyConnectivity();
2105
+ logger3.info("Neo4j connection pool initialized", {
2106
+ uri: config.uri,
2107
+ database: config.database || "neo4j",
2108
+ duration: Date.now() - startTime
2109
+ });
2110
+ }
2111
+ /**
2112
+ * Verify connectivity to Neo4j
2113
+ */
2114
+ async verifyConnectivity() {
2115
+ if (!this.driver) {
2116
+ const error = "Neo4j driver not initialized";
2117
+ logger3.error(error);
2118
+ throw new Error(error);
2119
+ }
2120
+ logger3.debug("Verifying Neo4j connectivity");
2121
+ try {
2122
+ await this.driver.verifyConnectivity();
2123
+ logger3.debug("Neo4j connectivity verified");
2124
+ } catch (error) {
2125
+ const errorMessage = `Failed to connect to Neo4j: ${error instanceof Error ? error.message : "Unknown error"}`;
2126
+ logger3.error("Neo4j connectivity verification failed", {
2127
+ error: error instanceof Error ? error.message : "Unknown error",
2128
+ uri: this.config?.uri
2129
+ });
2130
+ throw new Error(errorMessage);
2131
+ }
2132
+ }
2133
+ /**
2134
+ * Get a session for executing queries
2135
+ */
2136
+ getSession(database) {
2137
+ if (!this.driver) {
2138
+ throw new Error("Neo4j driver not initialized. Call initialize() first.");
2139
+ }
2140
+ return this.driver.session({
2141
+ database: database || this.config?.database || "neo4j"
2142
+ });
2143
+ }
2144
+ /**
2145
+ * Execute a query with automatic session management
2146
+ */
2147
+ async executeQuery(cypher, parameters, database) {
2148
+ const session = this.getSession(database);
2149
+ try {
2150
+ const result = await session.run(cypher, parameters);
2151
+ return result.records.map((record) => record.toObject());
2152
+ } finally {
2153
+ await session.close();
2154
+ }
2155
+ }
2156
+ /**
2157
+ * Execute a read query
2158
+ */
2159
+ async executeReadQuery(cypher, parameters, database) {
2160
+ const session = this.getSession(database);
2161
+ try {
2162
+ const result = await session.executeRead((tx) => tx.run(cypher, parameters));
2163
+ return result.records.map((record) => record.toObject());
2164
+ } finally {
2165
+ await session.close();
2166
+ }
2167
+ }
2168
+ /**
2169
+ * Execute a write query
2170
+ */
2171
+ async executeWriteQuery(cypher, parameters, database) {
2172
+ const session = this.getSession(database);
2173
+ try {
2174
+ const result = await session.executeWrite((tx) => tx.run(cypher, parameters));
2175
+ return result.records.map((record) => record.toObject());
2176
+ } finally {
2177
+ await session.close();
2178
+ }
2179
+ }
2180
+ /**
2181
+ * Check if driver is initialized
2182
+ */
2183
+ isInitialized() {
2184
+ return this.driver !== null;
2185
+ }
2186
+ /**
2187
+ * Get current configuration
2188
+ */
2189
+ getConfig() {
2190
+ return this.config;
2191
+ }
2192
+ /**
2193
+ * Close the connection pool
2194
+ */
2195
+ async close() {
2196
+ if (this.driver) {
2197
+ logger3.debug("Closing Neo4j connection pool");
2198
+ await this.driver.close();
2199
+ this.driver = null;
2200
+ this.config = null;
2201
+ logger3.info("Neo4j connection pool closed");
2202
+ }
2203
+ }
2204
+ };
2205
+ var neo4jPool = new Neo4jConnectionPool();
2206
+ async function initializeFromEnv() {
2207
+ if (!process.env.NEO4J_URI) {
2208
+ logger3.warn("NEO4J_URI environment variable not set, using default", {
2209
+ default: "bolt://localhost:7687"
2210
+ });
2211
+ }
2212
+ if (!process.env.NEO4J_USER) {
2213
+ logger3.warn("NEO4J_USER environment variable not set, using default", {
2214
+ default: "neo4j"
2215
+ });
2216
+ }
2217
+ if (!process.env.NEO4J_PASSWORD) {
2218
+ logger3.warn("NEO4J_PASSWORD environment variable not set, using default", {
2219
+ default: "password"
2220
+ });
2221
+ }
2222
+ if (!process.env.NEO4J_DATABASE) {
2223
+ logger3.debug("NEO4J_DATABASE environment variable not set, using default", {
2224
+ default: "neo4j"
2225
+ });
2226
+ }
2227
+ const config = {
2228
+ uri: process.env.NEO4J_URI || "bolt://localhost:7687",
2229
+ username: process.env.NEO4J_USER || "neo4j",
2230
+ password: process.env.NEO4J_PASSWORD || "password",
2231
+ database: process.env.NEO4J_DATABASE
2232
+ };
2233
+ await neo4jPool.initialize(config);
2234
+ }
2235
+
2236
+ // src/data/neo4j/embeddings/utils.ts
2237
+ var DEFAULT_RETRY_CONFIG2 = {
2238
+ maxRetries: 3,
2239
+ initialDelay: 1e3,
2240
+ // 1 second
2241
+ maxDelay: 1e4,
2242
+ // 10 seconds
2243
+ backoffMultiplier: 2
2244
+ };
2245
+ function isRetryableError2(error) {
2246
+ if (error.code === "ECONNRESET" || error.code === "ETIMEDOUT" || error.code === "ENOTFOUND") {
2247
+ return true;
2248
+ }
2249
+ if (error.code === "ECONNABORTED" || error.message?.includes("timeout")) {
2250
+ return true;
2251
+ }
2252
+ if (error.response?.status >= 500 && error.response?.status < 600) {
2253
+ return true;
2254
+ }
2255
+ if (error.response?.status === 429) {
2256
+ return true;
2257
+ }
2258
+ return false;
2259
+ }
2260
+ function sleep2(ms) {
2261
+ return new Promise((resolve2) => setTimeout(resolve2, ms));
2262
+ }
2263
+ async function retryWithBackoff2(fn, config = DEFAULT_RETRY_CONFIG2) {
2264
+ let lastError;
2265
+ let delay = config.initialDelay;
2266
+ for (let attempt = 0; attempt <= config.maxRetries; attempt++) {
2267
+ try {
2268
+ return await fn();
2269
+ } catch (error) {
2270
+ lastError = error;
2271
+ if (!isRetryableError2(error)) {
2272
+ throw error;
2273
+ }
2274
+ if (attempt === config.maxRetries) {
2275
+ break;
2276
+ }
2277
+ await sleep2(delay);
2278
+ delay = Math.min(delay * config.backoffMultiplier, config.maxDelay);
2279
+ }
2280
+ }
2281
+ throw lastError;
2282
+ }
2283
+ function getOpenAIApiKey() {
2284
+ return process.env.OPENAI_API_KEY;
2285
+ }
2286
+ function getCohereApiKey() {
2287
+ return process.env.COHERE_API_KEY;
2288
+ }
2289
+ function getHuggingFaceApiKey() {
2290
+ return process.env.HUGGINGFACE_API_KEY;
2291
+ }
2292
+ function getVoyageApiKey() {
2293
+ return process.env.VOYAGE_API_KEY;
2294
+ }
2295
+ function getOllamaBaseUrl() {
2296
+ return process.env.OLLAMA_BASE_URL || "http://localhost:11434";
2297
+ }
2298
+ function getEmbeddingProvider() {
2299
+ return process.env.EMBEDDING_PROVIDER || "openai";
2300
+ }
2301
+ function getEmbeddingModel() {
2302
+ return process.env.EMBEDDING_MODEL;
2303
+ }
2304
+ function validateText(text) {
2305
+ if (!text || text.trim().length === 0) {
2306
+ throw new Error("Text cannot be empty");
2307
+ }
2308
+ if (text.length > 5e4) {
2309
+ throw new Error("Text is too long for embedding generation (max ~50,000 characters)");
2310
+ }
2311
+ }
2312
+ function validateBatch(texts) {
2313
+ if (!texts || texts.length === 0) {
2314
+ throw new Error("Batch cannot be empty");
2315
+ }
2316
+ if (texts.length > 2048) {
2317
+ throw new Error("Batch size too large (max 2048 texts)");
2318
+ }
2319
+ texts.forEach((text, index) => {
2320
+ try {
2321
+ validateText(text);
2322
+ } catch (error) {
2323
+ throw new Error(`Invalid text at index ${index}: ${error instanceof Error ? error.message : "Unknown error"}`);
2324
+ }
2325
+ });
2326
+ }
2327
+ var OpenAIEmbeddingProvider = class {
2328
+ name = "openai";
2329
+ defaultModel = "text-embedding-3-small";
2330
+ apiKey;
2331
+ baseURL = "https://api.openai.com/v1";
2332
+ constructor(apiKey) {
2333
+ this.apiKey = apiKey || getOpenAIApiKey();
2334
+ }
2335
+ /**
2336
+ * Check if OpenAI is available (API key is set)
2337
+ */
2338
+ isAvailable() {
2339
+ return !!this.apiKey;
2340
+ }
2341
+ /**
2342
+ * Generate embedding for a single text
2343
+ */
2344
+ async generateEmbedding(text, model) {
2345
+ if (!this.apiKey) {
2346
+ throw new Error(
2347
+ "OpenAI API key not found. Set OPENAI_API_KEY environment variable. Get your key at https://platform.openai.com/api-keys"
2348
+ );
2349
+ }
2350
+ validateText(text);
2351
+ const modelToUse = model || this.defaultModel;
2352
+ return retryWithBackoff2(async () => {
2353
+ try {
2354
+ const response = await axios16.post(
2355
+ `${this.baseURL}/embeddings`,
2356
+ {
2357
+ input: text,
2358
+ model: modelToUse
2359
+ },
2360
+ {
2361
+ headers: {
2362
+ "Authorization": `Bearer ${this.apiKey}`,
2363
+ "Content-Type": "application/json"
2364
+ },
2365
+ timeout: 3e4
2366
+ // 30 seconds
2367
+ }
2368
+ );
2369
+ const data = response.data;
2370
+ if (!data.data || data.data.length === 0) {
2371
+ throw new Error("No embedding returned from OpenAI");
2372
+ }
2373
+ return {
2374
+ embedding: data.data[0].embedding,
2375
+ model: data.model,
2376
+ dimensions: data.data[0].embedding.length,
2377
+ usage: {
2378
+ promptTokens: data.usage.prompt_tokens,
2379
+ totalTokens: data.usage.total_tokens
2380
+ }
2381
+ };
2382
+ } catch (error) {
2383
+ let wrappedError;
2384
+ if (error.response?.status === 401) {
2385
+ wrappedError = new Error(
2386
+ "Invalid OpenAI API key. Get your key at https://platform.openai.com/api-keys"
2387
+ );
2388
+ } else if (error.response?.status === 429) {
2389
+ wrappedError = new Error(
2390
+ "OpenAI API rate limit exceeded. Please try again later or upgrade your plan."
2391
+ );
2392
+ } else if (error.response?.status === 400) {
2393
+ wrappedError = new Error(
2394
+ `OpenAI API error: ${error.response.data?.error?.message || "Bad request"}`
2395
+ );
2396
+ } else {
2397
+ wrappedError = new Error(`OpenAI embedding generation failed: ${error.message}`);
2398
+ }
2399
+ if (error.code) wrappedError.code = error.code;
2400
+ if (error.response) wrappedError.response = error.response;
2401
+ throw wrappedError;
2402
+ }
2403
+ });
2404
+ }
2405
+ /**
2406
+ * Generate embeddings for multiple texts (batch)
2407
+ */
2408
+ async generateBatchEmbeddings(texts, model) {
2409
+ if (!this.apiKey) {
2410
+ throw new Error(
2411
+ "OpenAI API key not found. Set OPENAI_API_KEY environment variable. Get your key at https://platform.openai.com/api-keys"
2412
+ );
2413
+ }
2414
+ validateBatch(texts);
2415
+ const modelToUse = model || this.defaultModel;
2416
+ return retryWithBackoff2(async () => {
2417
+ try {
2418
+ const response = await axios16.post(
2419
+ `${this.baseURL}/embeddings`,
2420
+ {
2421
+ input: texts,
2422
+ model: modelToUse
2423
+ },
2424
+ {
2425
+ headers: {
2426
+ "Authorization": `Bearer ${this.apiKey}`,
2427
+ "Content-Type": "application/json"
2428
+ },
2429
+ timeout: 6e4
2430
+ // 60 seconds for batch
2431
+ }
2432
+ );
2433
+ const data = response.data;
2434
+ if (!data.data || data.data.length === 0) {
2435
+ throw new Error("No embeddings returned from OpenAI");
2436
+ }
2437
+ const sortedData = data.data.sort((a, b) => a.index - b.index);
2438
+ return {
2439
+ embeddings: sortedData.map((item) => item.embedding),
2440
+ model: data.model,
2441
+ dimensions: sortedData[0].embedding.length,
2442
+ usage: {
2443
+ promptTokens: data.usage.prompt_tokens,
2444
+ totalTokens: data.usage.total_tokens
2445
+ }
2446
+ };
2447
+ } catch (error) {
2448
+ let wrappedError;
2449
+ if (error.response?.status === 401) {
2450
+ wrappedError = new Error(
2451
+ "Invalid OpenAI API key. Get your key at https://platform.openai.com/api-keys"
2452
+ );
2453
+ } else if (error.response?.status === 429) {
2454
+ wrappedError = new Error(
2455
+ "OpenAI API rate limit exceeded. Please try again later or upgrade your plan."
2456
+ );
2457
+ } else if (error.response?.status === 400) {
2458
+ wrappedError = new Error(
2459
+ `OpenAI API error: ${error.response.data?.error?.message || "Bad request"}`
2460
+ );
2461
+ } else {
2462
+ wrappedError = new Error(`OpenAI batch embedding generation failed: ${error.message}`);
2463
+ }
2464
+ if (error.code) wrappedError.code = error.code;
2465
+ if (error.response) wrappedError.response = error.response;
2466
+ throw wrappedError;
2467
+ }
2468
+ });
2469
+ }
2470
+ };
2471
+ var CohereEmbeddingProvider = class {
2472
+ name = "cohere";
2473
+ defaultModel = "embed-english-v3.0";
2474
+ apiKey;
2475
+ baseUrl = "https://api.cohere.ai/v1";
2476
+ constructor(apiKey, model) {
2477
+ this.apiKey = apiKey;
2478
+ }
2479
+ /**
2480
+ * Check if Cohere is available (API key is set)
2481
+ */
2482
+ isAvailable() {
2483
+ return !!this.apiKey;
2484
+ }
2485
+ async generateEmbedding(text, model) {
2486
+ const result = await this.generateBatchEmbeddings([text], model);
2487
+ return {
2488
+ embedding: result.embeddings[0],
2489
+ model: result.model,
2490
+ dimensions: result.dimensions,
2491
+ usage: result.usage
2492
+ };
2493
+ }
2494
+ async generateBatchEmbeddings(texts, model) {
2495
+ const modelToUse = model || this.defaultModel;
2496
+ return retryWithBackoff2(async () => {
2497
+ try {
2498
+ const response = await axios16.post(
2499
+ `${this.baseUrl}/embed`,
2500
+ {
2501
+ texts,
2502
+ model: modelToUse,
2503
+ input_type: "search_document",
2504
+ // For storing in vector DB
2505
+ embedding_types: ["float"]
2506
+ },
2507
+ {
2508
+ headers: {
2509
+ "Authorization": `Bearer ${this.apiKey}`,
2510
+ "Content-Type": "application/json"
2511
+ }
2512
+ }
2513
+ );
2514
+ const embeddings = response.data.embeddings.float;
2515
+ const dimensions = embeddings[0]?.length || 0;
2516
+ return {
2517
+ embeddings,
2518
+ model: modelToUse,
2519
+ dimensions,
2520
+ usage: response.data.meta?.billed_units ? {
2521
+ promptTokens: response.data.meta.billed_units.input_tokens || 0,
2522
+ totalTokens: response.data.meta.billed_units.input_tokens || 0
2523
+ } : void 0
2524
+ };
2525
+ } catch (error) {
2526
+ if (axios16.isAxiosError(error)) {
2527
+ const status = error.response?.status;
2528
+ const message = error.response?.data?.message || error.message;
2529
+ let wrappedError;
2530
+ if (status === 401) {
2531
+ wrappedError = new Error(`Cohere API authentication failed. Please check your COHERE_API_KEY. ${message}`);
2532
+ } else if (status === 429) {
2533
+ wrappedError = new Error(`Cohere API rate limit exceeded. ${message}`);
2534
+ } else if (status === 400) {
2535
+ wrappedError = new Error(`Cohere API request invalid: ${message}`);
2536
+ } else {
2537
+ wrappedError = new Error(`Cohere API error (${status}): ${message}`);
2538
+ }
2539
+ if (error.code) wrappedError.code = error.code;
2540
+ if (error.response) wrappedError.response = error.response;
2541
+ throw wrappedError;
2542
+ }
2543
+ throw error;
2544
+ }
2545
+ });
2546
+ }
2547
+ };
2548
+ var HuggingFaceEmbeddingProvider = class {
2549
+ name = "huggingface";
2550
+ defaultModel = "sentence-transformers/all-MiniLM-L6-v2";
2551
+ apiKey;
2552
+ baseUrl = "https://api-inference.huggingface.co/pipeline/feature-extraction";
2553
+ constructor(apiKey, model) {
2554
+ this.apiKey = apiKey;
2555
+ }
2556
+ /**
2557
+ * Check if HuggingFace is available (API key is set)
2558
+ */
2559
+ isAvailable() {
2560
+ return !!this.apiKey;
2561
+ }
2562
+ async generateEmbedding(text, model) {
2563
+ const modelToUse = model || this.defaultModel;
2564
+ return retryWithBackoff2(async () => {
2565
+ try {
2566
+ const response = await axios16.post(
2567
+ `${this.baseUrl}/${modelToUse}`,
2568
+ {
2569
+ inputs: text,
2570
+ options: {
2571
+ wait_for_model: true
2572
+ }
2573
+ },
2574
+ {
2575
+ headers: {
2576
+ "Authorization": `Bearer ${this.apiKey}`,
2577
+ "Content-Type": "application/json"
2578
+ }
2579
+ }
2580
+ );
2581
+ const embedding = Array.isArray(response.data) ? response.data : response.data[0];
2582
+ return {
2583
+ embedding,
2584
+ model: modelToUse,
2585
+ dimensions: embedding.length
2586
+ };
2587
+ } catch (error) {
2588
+ if (axios16.isAxiosError(error)) {
2589
+ const status = error.response?.status;
2590
+ const message = error.response?.data?.error || error.message;
2591
+ let wrappedError;
2592
+ if (status === 401) {
2593
+ wrappedError = new Error(`HuggingFace API authentication failed. Please check your HUGGINGFACE_API_KEY. ${message}`);
2594
+ } else if (status === 429) {
2595
+ wrappedError = new Error(`HuggingFace API rate limit exceeded. ${message}`);
2596
+ } else if (status === 400) {
2597
+ wrappedError = new Error(`HuggingFace API request invalid: ${message}`);
2598
+ } else if (status === 503) {
2599
+ wrappedError = new Error(`HuggingFace model is loading. Please retry in a moment. ${message}`);
2600
+ } else {
2601
+ wrappedError = new Error(`HuggingFace API error (${status}): ${message}`);
2602
+ }
2603
+ if (error.code) wrappedError.code = error.code;
2604
+ if (error.response) wrappedError.response = error.response;
2605
+ throw wrappedError;
2606
+ }
2607
+ throw error;
2608
+ }
2609
+ });
2610
+ }
2611
+ async generateBatchEmbeddings(texts, model) {
2612
+ const modelToUse = model || this.defaultModel;
2613
+ return retryWithBackoff2(async () => {
2614
+ try {
2615
+ const response = await axios16.post(
2616
+ `${this.baseUrl}/${modelToUse}`,
2617
+ {
2618
+ inputs: texts,
2619
+ options: {
2620
+ wait_for_model: true
2621
+ }
2622
+ },
2623
+ {
2624
+ headers: {
2625
+ "Authorization": `Bearer ${this.apiKey}`,
2626
+ "Content-Type": "application/json"
2627
+ }
2628
+ }
2629
+ );
2630
+ const embeddings = response.data;
2631
+ const dimensions = embeddings[0]?.length || 0;
2632
+ return {
2633
+ embeddings,
2634
+ model: modelToUse,
2635
+ dimensions
2636
+ };
2637
+ } catch (error) {
2638
+ if (axios16.isAxiosError(error)) {
2639
+ const status = error.response?.status;
2640
+ const message = error.response?.data?.error || error.message;
2641
+ let wrappedError;
2642
+ if (status === 401) {
2643
+ wrappedError = new Error(`HuggingFace API authentication failed. Please check your HUGGINGFACE_API_KEY. ${message}`);
2644
+ } else if (status === 429) {
2645
+ wrappedError = new Error(`HuggingFace API rate limit exceeded. ${message}`);
2646
+ } else if (status === 400) {
2647
+ wrappedError = new Error(`HuggingFace API request invalid: ${message}`);
2648
+ } else if (status === 503) {
2649
+ wrappedError = new Error(`HuggingFace model is loading. Please retry in a moment. ${message}`);
2650
+ } else {
2651
+ wrappedError = new Error(`HuggingFace API error (${status}): ${message}`);
2652
+ }
2653
+ if (error.code) wrappedError.code = error.code;
2654
+ if (error.response) wrappedError.response = error.response;
2655
+ throw wrappedError;
2656
+ }
2657
+ throw error;
2658
+ }
2659
+ });
2660
+ }
2661
+ };
2662
+ var VoyageEmbeddingProvider = class {
2663
+ name = "voyage";
2664
+ defaultModel = "voyage-2";
2665
+ apiKey;
2666
+ baseUrl = "https://api.voyageai.com/v1";
2667
+ constructor(apiKey, model) {
2668
+ this.apiKey = apiKey;
2669
+ }
2670
+ /**
2671
+ * Check if Voyage AI is available (API key is set)
2672
+ */
2673
+ isAvailable() {
2674
+ return !!this.apiKey;
2675
+ }
2676
+ async generateEmbedding(text, model) {
2677
+ const result = await this.generateBatchEmbeddings([text], model);
2678
+ return {
2679
+ embedding: result.embeddings[0],
2680
+ model: result.model,
2681
+ dimensions: result.dimensions,
2682
+ usage: result.usage
2683
+ };
2684
+ }
2685
+ async generateBatchEmbeddings(texts, model) {
2686
+ const modelToUse = model || this.defaultModel;
2687
+ return retryWithBackoff2(async () => {
2688
+ try {
2689
+ const response = await axios16.post(
2690
+ `${this.baseUrl}/embeddings`,
2691
+ {
2692
+ input: texts,
2693
+ model: modelToUse,
2694
+ input_type: "document"
2695
+ // For storing in vector DB
2696
+ },
2697
+ {
2698
+ headers: {
2699
+ "Authorization": `Bearer ${this.apiKey}`,
2700
+ "Content-Type": "application/json"
2701
+ }
2702
+ }
2703
+ );
2704
+ const embeddings = response.data.data.map((item) => item.embedding);
2705
+ const dimensions = embeddings[0]?.length || 0;
2706
+ return {
2707
+ embeddings,
2708
+ model: modelToUse,
2709
+ dimensions,
2710
+ usage: response.data.usage ? {
2711
+ promptTokens: response.data.usage.total_tokens || 0,
2712
+ totalTokens: response.data.usage.total_tokens || 0
2713
+ } : void 0
2714
+ };
2715
+ } catch (error) {
2716
+ if (axios16.isAxiosError(error)) {
2717
+ const status = error.response?.status;
2718
+ const message = error.response?.data?.message || error.message;
2719
+ let wrappedError;
2720
+ if (status === 401) {
2721
+ wrappedError = new Error(`Voyage AI API authentication failed. Please check your VOYAGE_API_KEY. ${message}`);
2722
+ } else if (status === 429) {
2723
+ wrappedError = new Error(`Voyage AI API rate limit exceeded. ${message}`);
2724
+ } else if (status === 400) {
2725
+ wrappedError = new Error(`Voyage AI API request invalid: ${message}`);
2726
+ } else {
2727
+ wrappedError = new Error(`Voyage AI API error (${status}): ${message}`);
2728
+ }
2729
+ if (error.code) wrappedError.code = error.code;
2730
+ if (error.response) wrappedError.response = error.response;
2731
+ throw wrappedError;
2732
+ }
2733
+ throw error;
2734
+ }
2735
+ });
2736
+ }
2737
+ };
2738
+ var OllamaEmbeddingProvider = class {
2739
+ name = "ollama";
2740
+ defaultModel = "nomic-embed-text";
2741
+ baseUrl;
2742
+ constructor(model, baseUrl = "http://localhost:11434") {
2743
+ this.baseUrl = baseUrl;
2744
+ }
2745
+ /**
2746
+ * Check if Ollama is available (always true for local service)
2747
+ */
2748
+ isAvailable() {
2749
+ return true;
2750
+ }
2751
+ async generateEmbedding(text, model) {
2752
+ const modelToUse = model || this.defaultModel;
2753
+ return retryWithBackoff2(async () => {
2754
+ try {
2755
+ const response = await axios16.post(
2756
+ `${this.baseUrl}/api/embeddings`,
2757
+ {
2758
+ model: modelToUse,
2759
+ prompt: text
2760
+ },
2761
+ {
2762
+ headers: {
2763
+ "Content-Type": "application/json"
2764
+ }
2765
+ }
2766
+ );
2767
+ const embedding = response.data.embedding;
2768
+ return {
2769
+ embedding,
2770
+ model: modelToUse,
2771
+ dimensions: embedding.length
2772
+ };
2773
+ } catch (error) {
2774
+ if (axios16.isAxiosError(error)) {
2775
+ const status = error.response?.status;
2776
+ const message = error.response?.data?.error || error.message;
2777
+ let wrappedError;
2778
+ if (error.code === "ECONNREFUSED") {
2779
+ wrappedError = new Error(`Cannot connect to Ollama at ${this.baseUrl}. Make sure Ollama is running locally.`);
2780
+ } else if (status === 404) {
2781
+ wrappedError = new Error(`Ollama model not found. Pull it with: ollama pull <model-name>`);
2782
+ } else if (status === 400) {
2783
+ wrappedError = new Error(`Ollama API request invalid: ${message}`);
2784
+ } else {
2785
+ wrappedError = new Error(`Ollama API error (${status}): ${message}`);
2786
+ }
2787
+ if (error.code) wrappedError.code = error.code;
2788
+ if (error.response) wrappedError.response = error.response;
2789
+ throw wrappedError;
2790
+ }
2791
+ throw error;
2792
+ }
2793
+ });
2794
+ }
2795
+ async generateBatchEmbeddings(texts, model) {
2796
+ const modelToUse = model || this.defaultModel;
2797
+ const embeddings = [];
2798
+ for (const text of texts) {
2799
+ const result = await this.generateEmbedding(text, modelToUse);
2800
+ embeddings.push(result.embedding);
2801
+ }
2802
+ const dimensions = embeddings[0]?.length || 0;
2803
+ return {
2804
+ embeddings,
2805
+ model: modelToUse,
2806
+ dimensions
2807
+ };
2808
+ }
2809
+ };
2810
+
2811
+ // src/data/neo4j/embeddings/embedding-manager.ts
2812
+ var logger4 = createLogger("agentforge:tools:neo4j:embeddings");
2813
+ var EmbeddingManager = class {
2814
+ provider = null;
2815
+ config = null;
2816
+ /**
2817
+ * Initialize with configuration
2818
+ */
2819
+ initialize(config) {
2820
+ logger4.debug("Initializing embedding manager", {
2821
+ provider: config.provider,
2822
+ model: config.model
2823
+ });
2824
+ this.config = config;
2825
+ this.provider = this.createProvider(config.provider, config.apiKey);
2826
+ logger4.info("Embedding manager initialized", {
2827
+ provider: config.provider,
2828
+ model: config.model || this.getDefaultModel(config.provider)
2829
+ });
2830
+ }
2831
+ /**
2832
+ * Initialize from environment variables
2833
+ */
2834
+ initializeFromEnv() {
2835
+ const providerName = getEmbeddingProvider();
2836
+ const model = getEmbeddingModel();
2837
+ logger4.debug("Initializing embedding manager from environment", {
2838
+ provider: providerName,
2839
+ model: model || this.getDefaultModel(providerName)
2840
+ });
2841
+ let apiKey = "";
2842
+ let baseUrl = "";
2843
+ switch (providerName) {
2844
+ case "openai": {
2845
+ const key = getOpenAIApiKey();
2846
+ if (!key) {
2847
+ logger4.error("OPENAI_API_KEY environment variable not set", {
2848
+ provider: "openai",
2849
+ required: true
2850
+ });
2851
+ throw new Error("OPENAI_API_KEY environment variable is required for OpenAI embeddings");
2852
+ }
2853
+ apiKey = key;
2854
+ logger4.debug("OpenAI API key found");
2855
+ break;
2856
+ }
2857
+ case "cohere": {
2858
+ const key = getCohereApiKey();
2859
+ if (!key) {
2860
+ logger4.error("COHERE_API_KEY environment variable not set", {
2861
+ provider: "cohere",
2862
+ required: true
2863
+ });
2864
+ throw new Error("COHERE_API_KEY environment variable is required for Cohere embeddings");
2865
+ }
2866
+ apiKey = key;
2867
+ logger4.debug("Cohere API key found");
2868
+ break;
2869
+ }
2870
+ case "huggingface": {
2871
+ const key = getHuggingFaceApiKey();
2872
+ if (!key) {
2873
+ logger4.error("HUGGINGFACE_API_KEY environment variable not set", {
2874
+ provider: "huggingface",
2875
+ required: true
2876
+ });
2877
+ throw new Error("HUGGINGFACE_API_KEY environment variable is required for HuggingFace embeddings");
2878
+ }
2879
+ apiKey = key;
2880
+ logger4.debug("HuggingFace API key found");
2881
+ break;
2882
+ }
2883
+ case "voyage": {
2884
+ const key = getVoyageApiKey();
2885
+ if (!key) {
2886
+ logger4.error("VOYAGE_API_KEY environment variable not set", {
2887
+ provider: "voyage",
2888
+ required: true
2889
+ });
2890
+ throw new Error("VOYAGE_API_KEY environment variable is required for Voyage AI embeddings");
2891
+ }
2892
+ apiKey = key;
2893
+ logger4.debug("Voyage API key found");
2894
+ break;
2895
+ }
2896
+ case "ollama":
2897
+ baseUrl = getOllamaBaseUrl();
2898
+ logger4.debug("Using Ollama (local, no API key required)", {
2899
+ baseUrl: baseUrl || "http://localhost:11434"
2900
+ });
2901
+ break;
2902
+ default:
2903
+ logger4.error("Unknown embedding provider", {
2904
+ provider: providerName
2905
+ });
2906
+ throw new Error(`Unknown embedding provider: ${providerName}`);
2907
+ }
2908
+ this.provider = this.createProvider(providerName, apiKey, model, baseUrl);
2909
+ this.config = {
2910
+ provider: providerName,
2911
+ model: model || this.getDefaultModel(providerName),
2912
+ apiKey: ""
2913
+ // Not stored when using env
2914
+ };
2915
+ logger4.info("Embedding manager initialized from environment", {
2916
+ provider: providerName,
2917
+ model: this.config.model
2918
+ });
2919
+ }
2920
+ /**
2921
+ * Check if manager is initialized
2922
+ */
2923
+ isInitialized() {
2924
+ return this.provider !== null && this.config !== null;
2925
+ }
2926
+ /**
2927
+ * Get current provider
2928
+ */
2929
+ getProvider() {
2930
+ if (!this.provider) {
2931
+ throw new Error("Embedding manager not initialized. Call initialize() or initializeFromEnv() first.");
2932
+ }
2933
+ return this.provider;
2934
+ }
2935
+ /**
2936
+ * Get current configuration
2937
+ */
2938
+ getConfig() {
2939
+ if (!this.config) {
2940
+ throw new Error("Embedding manager not initialized. Call initialize() or initializeFromEnv() first.");
2941
+ }
2942
+ return this.config;
2943
+ }
2944
+ /**
2945
+ * Generate embedding for a single text
2946
+ */
2947
+ async generateEmbedding(text, model) {
2948
+ const startTime = Date.now();
2949
+ const provider = this.getProvider();
2950
+ const config = this.getConfig();
2951
+ const modelToUse = model || config.model;
2952
+ logger4.debug("Generating embedding", {
2953
+ provider: provider.name,
2954
+ model: modelToUse,
2955
+ textLength: text.length
2956
+ });
2957
+ const result = await provider.generateEmbedding(text, modelToUse);
2958
+ logger4.info("Embedding generated", {
2959
+ provider: provider.name,
2960
+ model: result.model,
2961
+ dimensions: result.dimensions,
2962
+ duration: Date.now() - startTime
2963
+ });
2964
+ return result;
2965
+ }
2966
+ /**
2967
+ * Generate embeddings for multiple texts (batch)
2968
+ */
2969
+ async generateBatchEmbeddings(texts, model) {
2970
+ const startTime = Date.now();
2971
+ const provider = this.getProvider();
2972
+ const config = this.getConfig();
2973
+ const modelToUse = model || config.model;
2974
+ logger4.debug("Generating batch embeddings", {
2975
+ provider: provider.name,
2976
+ model: modelToUse,
2977
+ count: texts.length
2978
+ });
2979
+ const result = await provider.generateBatchEmbeddings(texts, modelToUse);
2980
+ logger4.info("Batch embeddings generated", {
2981
+ provider: provider.name,
2982
+ model: result.model,
2983
+ dimensions: result.dimensions,
2984
+ count: texts.length,
2985
+ duration: Date.now() - startTime
2986
+ });
2987
+ return result;
2988
+ }
2989
+ /**
2990
+ * Get default model for a provider
2991
+ */
2992
+ getDefaultModel(provider) {
2993
+ switch (provider) {
2994
+ case "openai":
2995
+ return "text-embedding-3-small";
2996
+ case "cohere":
2997
+ return "embed-english-v3.0";
2998
+ case "huggingface":
2999
+ return "sentence-transformers/all-MiniLM-L6-v2";
3000
+ case "voyage":
3001
+ return "voyage-2";
3002
+ case "ollama":
3003
+ return "nomic-embed-text";
3004
+ default:
3005
+ return "text-embedding-3-small";
3006
+ }
3007
+ }
3008
+ /**
3009
+ * Create a provider instance
3010
+ */
3011
+ createProvider(providerName, apiKey, model, baseUrl) {
3012
+ switch (providerName) {
3013
+ case "openai":
3014
+ return new OpenAIEmbeddingProvider(apiKey);
3015
+ case "cohere":
3016
+ return new CohereEmbeddingProvider(apiKey);
3017
+ case "huggingface":
3018
+ return new HuggingFaceEmbeddingProvider(apiKey);
3019
+ case "voyage":
3020
+ return new VoyageEmbeddingProvider(apiKey);
3021
+ case "ollama":
3022
+ return new OllamaEmbeddingProvider(model, baseUrl || "http://localhost:11434");
3023
+ default:
3024
+ throw new Error(`Unknown embedding provider: ${providerName}`);
3025
+ }
3026
+ }
3027
+ /**
3028
+ * Reset the manager (for testing)
3029
+ */
3030
+ reset() {
3031
+ this.provider = null;
3032
+ this.config = null;
3033
+ }
3034
+ };
3035
+ var embeddingManager = new EmbeddingManager();
3036
+ function initializeEmbeddings() {
3037
+ embeddingManager.initializeFromEnv();
3038
+ }
3039
+ function initializeEmbeddingsWithConfig(config) {
3040
+ embeddingManager.initialize(config);
3041
+ }
3042
+ async function generateEmbedding(text, model) {
3043
+ return embeddingManager.generateEmbedding(text, model);
3044
+ }
3045
+ async function generateBatchEmbeddings(texts, model) {
3046
+ return embeddingManager.generateBatchEmbeddings(texts, model);
3047
+ }
3048
+ function formatInteger(value) {
3049
+ if (value.inSafeRange()) {
3050
+ return value.toNumber();
3051
+ }
3052
+ return value.toString();
3053
+ }
3054
+ function formatValue(value) {
3055
+ if (value === null || value === void 0) {
3056
+ return value;
3057
+ }
3058
+ if (value instanceof Integer) {
3059
+ return formatInteger(value);
3060
+ }
3061
+ if (value instanceof Node) {
3062
+ return formatNode(value);
3063
+ }
3064
+ if (value instanceof Relationship) {
3065
+ return formatRelationship(value);
3066
+ }
3067
+ if (value instanceof Path) {
3068
+ return formatPath(value);
3069
+ }
3070
+ if (Array.isArray(value)) {
3071
+ return value.map(formatValue);
3072
+ }
3073
+ if (typeof value === "object") {
3074
+ const formatted = {};
3075
+ for (const [key, val] of Object.entries(value)) {
3076
+ formatted[key] = formatValue(val);
3077
+ }
3078
+ return formatted;
3079
+ }
3080
+ return value;
3081
+ }
3082
+ function formatNode(node) {
3083
+ return {
3084
+ identity: formatInteger(node.identity),
3085
+ labels: node.labels,
3086
+ properties: formatValue(node.properties)
3087
+ };
3088
+ }
3089
+ function formatRelationship(rel) {
3090
+ return {
3091
+ identity: formatInteger(rel.identity),
3092
+ type: rel.type,
3093
+ start: formatInteger(rel.start),
3094
+ end: formatInteger(rel.end),
3095
+ properties: formatValue(rel.properties)
3096
+ };
3097
+ }
3098
+ function formatPath(path12) {
3099
+ return {
3100
+ start: formatNode(path12.start),
3101
+ end: formatNode(path12.end),
3102
+ segments: path12.segments.map((segment) => ({
3103
+ start: formatNode(segment.start),
3104
+ relationship: formatRelationship(segment.relationship),
3105
+ end: formatNode(segment.end)
3106
+ })),
3107
+ length: path12.length
3108
+ };
3109
+ }
3110
+ function formatResults(records) {
3111
+ return records.map((record) => {
3112
+ const formatted = {};
3113
+ for (const key of record.keys) {
3114
+ formatted[key] = formatValue(record.get(key));
3115
+ }
3116
+ return formatted;
3117
+ });
3118
+ }
3119
+
3120
+ // src/data/neo4j/tools/neo4j-query.ts
3121
+ var logger5 = createLogger("agentforge:tools:neo4j:query");
3122
+ function createNeo4jQueryTool() {
3123
+ return toolBuilder().name("neo4j-query").description(
3124
+ "Execute a Cypher query against the Neo4j graph database. Supports parameterized queries for safety. Use this for complex queries, graph traversals, and custom operations."
3125
+ ).category(ToolCategory.DATABASE).tags(["neo4j", "graph", "database", "cypher", "query"]).schema(neo4jQuerySchema).implement(async (input) => {
3126
+ if (!neo4jPool.isInitialized()) {
3127
+ logger5.warn("Neo4j query attempted but connection not initialized");
3128
+ return {
3129
+ success: false,
3130
+ error: "Neo4j connection not initialized. Please configure Neo4j connection first."
3131
+ };
3132
+ }
3133
+ const startTime = Date.now();
3134
+ logger5.debug("Executing Neo4j query", {
3135
+ cypherPreview: input.cypher.substring(0, 100),
3136
+ parameterCount: Object.keys(input.parameters || {}).length,
3137
+ database: input.database
3138
+ });
3139
+ try {
3140
+ const session = neo4jPool.getSession(input.database);
3141
+ try {
3142
+ const result = await session.run(input.cypher, input.parameters || {});
3143
+ const formattedResults = formatResults(result.records);
3144
+ const duration = Date.now() - startTime;
3145
+ logger5.info("Neo4j query executed successfully", {
3146
+ recordCount: result.records.length,
3147
+ nodesCreated: result.summary.counters.updates().nodesCreated,
3148
+ nodesDeleted: result.summary.counters.updates().nodesDeleted,
3149
+ relationshipsCreated: result.summary.counters.updates().relationshipsCreated,
3150
+ relationshipsDeleted: result.summary.counters.updates().relationshipsDeleted,
3151
+ propertiesSet: result.summary.counters.updates().propertiesSet,
3152
+ duration
3153
+ });
3154
+ return {
3155
+ success: true,
3156
+ data: formattedResults,
3157
+ recordCount: result.records.length,
3158
+ summary: {
3159
+ query: input.cypher,
3160
+ parameters: input.parameters,
3161
+ counters: {
3162
+ nodesCreated: result.summary.counters.updates().nodesCreated,
3163
+ nodesDeleted: result.summary.counters.updates().nodesDeleted,
3164
+ relationshipsCreated: result.summary.counters.updates().relationshipsCreated,
3165
+ relationshipsDeleted: result.summary.counters.updates().relationshipsDeleted,
3166
+ propertiesSet: result.summary.counters.updates().propertiesSet
3167
+ }
3168
+ }
3169
+ };
3170
+ } finally {
3171
+ await session.close();
3172
+ }
3173
+ } catch (error) {
3174
+ const duration = Date.now() - startTime;
3175
+ const errorMessage = error instanceof Error ? error.message : "Failed to execute query";
3176
+ logger5.error("Neo4j query execution failed", {
3177
+ error: errorMessage,
3178
+ cypherPreview: input.cypher.substring(0, 100),
3179
+ duration
3180
+ });
3181
+ return {
3182
+ success: false,
3183
+ error: errorMessage,
3184
+ query: input.cypher
3185
+ };
3186
+ }
3187
+ }).build();
3188
+ }
3189
+ function createNeo4jGetSchemaTool() {
3190
+ return toolBuilder().name("neo4j-get-schema").description(
3191
+ "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."
3192
+ ).category(ToolCategory.DATABASE).tags(["neo4j", "graph", "database", "schema", "introspection"]).schema(neo4jGetSchemaSchema).implement(async (input) => {
3193
+ if (!neo4jPool.isInitialized()) {
3194
+ return {
3195
+ success: false,
3196
+ error: "Neo4j connection not initialized. Please configure Neo4j connection first."
3197
+ };
3198
+ }
3199
+ try {
3200
+ const session = neo4jPool.getSession(input.database);
3201
+ try {
3202
+ const labelsResult = await session.run("CALL db.labels()");
3203
+ const nodeLabels = labelsResult.records.map((r) => r.get("label"));
3204
+ const relTypesResult = await session.run("CALL db.relationshipTypes()");
3205
+ const relationshipTypes = relTypesResult.records.map((r) => r.get("relationshipType"));
3206
+ const propsResult = await session.run("CALL db.propertyKeys()");
3207
+ const propertyKeys = propsResult.records.map((r) => r.get("propertyKey"));
3208
+ const constraintsResult = await session.run("SHOW CONSTRAINTS");
3209
+ const constraints = constraintsResult.records.map((r) => ({
3210
+ name: r.get("name"),
3211
+ type: r.get("type"),
3212
+ entityType: r.get("entityType"),
3213
+ labelsOrTypes: r.get("labelsOrTypes"),
3214
+ properties: r.get("properties")
3215
+ }));
3216
+ const indexesResult = await session.run("SHOW INDEXES");
3217
+ const indexes = indexesResult.records.map((r) => ({
3218
+ name: r.get("name"),
3219
+ type: r.get("type"),
3220
+ entityType: r.get("entityType"),
3221
+ labelsOrTypes: r.get("labelsOrTypes"),
3222
+ properties: r.get("properties")
3223
+ }));
3224
+ return {
3225
+ success: true,
3226
+ schema: {
3227
+ nodeLabels,
3228
+ relationshipTypes,
3229
+ propertyKeys,
3230
+ constraints,
3231
+ indexes
3232
+ },
3233
+ summary: {
3234
+ totalLabels: nodeLabels.length,
3235
+ totalRelationshipTypes: relationshipTypes.length,
3236
+ totalPropertyKeys: propertyKeys.length,
3237
+ totalConstraints: constraints.length,
3238
+ totalIndexes: indexes.length
3239
+ }
3240
+ };
3241
+ } finally {
3242
+ await session.close();
3243
+ }
3244
+ } catch (error) {
3245
+ return {
3246
+ success: false,
3247
+ error: error instanceof Error ? error.message : "Failed to get schema"
3248
+ };
3249
+ }
3250
+ }).build();
3251
+ }
3252
+
3253
+ // src/data/neo4j/utils/cypher-sanitizer.ts
3254
+ function escapeCypherIdentifier(identifier, type = "identifier") {
3255
+ if (!identifier || typeof identifier !== "string") {
3256
+ throw new Error(`Invalid ${type}: must be a non-empty string`);
3257
+ }
3258
+ const trimmed = identifier.trim();
3259
+ if (trimmed.length === 0) {
3260
+ throw new Error(`Invalid ${type}: cannot be empty or whitespace`);
3261
+ }
3262
+ if (trimmed.includes("\0")) {
3263
+ throw new Error(`Invalid ${type}: cannot contain null bytes`);
3264
+ }
3265
+ const simplePattern = /^[a-zA-Z_][a-zA-Z0-9_]*$/;
3266
+ if (simplePattern.test(trimmed)) {
3267
+ return trimmed;
3268
+ }
3269
+ const escaped = trimmed.replace(/`/g, "``");
3270
+ return `\`${escaped}\``;
3271
+ }
3272
+ function validateLabel(label) {
3273
+ return escapeCypherIdentifier(label, "label");
3274
+ }
3275
+ function validatePropertyKey(key) {
3276
+ return escapeCypherIdentifier(key, "property key");
3277
+ }
3278
+ function validateRelationshipType(type) {
3279
+ return escapeCypherIdentifier(type, "relationship type");
3280
+ }
3281
+ function validateDirection(direction) {
3282
+ const normalized = direction.toUpperCase();
3283
+ if (normalized !== "OUTGOING" && normalized !== "INCOMING" && normalized !== "BOTH") {
3284
+ throw new Error(`Invalid direction: must be 'OUTGOING', 'INCOMING', or 'BOTH'`);
3285
+ }
3286
+ return normalized;
3287
+ }
3288
+ function buildPropertyFilter(properties, nodeVar = "n") {
3289
+ const keys = Object.keys(properties);
3290
+ if (keys.length === 0) {
3291
+ return { whereClause: "", parameters: {} };
3292
+ }
3293
+ const safeNodeVar = escapeCypherIdentifier(nodeVar, "node variable");
3294
+ const conditions = keys.map((key, index) => {
3295
+ const safeKey = validatePropertyKey(key);
3296
+ const paramName = `prop_${index}`;
3297
+ return `${safeNodeVar}.${safeKey} = $${paramName}`;
3298
+ });
3299
+ const parameters = {};
3300
+ keys.forEach((key, index) => {
3301
+ parameters[`prop_${index}`] = properties[key];
3302
+ });
3303
+ return {
3304
+ whereClause: `WHERE ${conditions.join(" AND ")}`,
3305
+ parameters
3306
+ };
3307
+ }
3308
+
3309
+ // src/data/neo4j/tools/neo4j-find-nodes.ts
3310
+ function createNeo4jFindNodesTool() {
3311
+ return toolBuilder().name("neo4j-find-nodes").description(
3312
+ "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."
3313
+ ).category(ToolCategory.DATABASE).tags(["neo4j", "graph", "database", "nodes", "search"]).schema(neo4jFindNodesSchema).implement(async (input) => {
3314
+ if (!neo4jPool.isInitialized()) {
3315
+ return {
3316
+ success: false,
3317
+ error: "Neo4j connection not initialized. Please configure Neo4j connection first."
3318
+ };
3319
+ }
3320
+ try {
3321
+ const safeLabel = validateLabel(input.label);
3322
+ const session = neo4jPool.getSession(input.database);
3323
+ try {
3324
+ let cypher = `MATCH (n:${safeLabel})`;
3325
+ let parameters = {};
3326
+ if (input.properties && Object.keys(input.properties).length > 0) {
3327
+ const { whereClause, parameters: filterParams } = buildPropertyFilter(input.properties, "n");
3328
+ cypher += ` ${whereClause}`;
3329
+ parameters = { ...parameters, ...filterParams };
3330
+ }
3331
+ cypher += ` RETURN n LIMIT $limit`;
3332
+ parameters.limit = input.limit;
3333
+ const result = await session.run(cypher, parameters);
3334
+ const formattedResults = formatResults(result.records);
3335
+ return {
3336
+ success: true,
3337
+ nodes: formattedResults.map((r) => r.n),
3338
+ count: result.records.length,
3339
+ query: {
3340
+ label: input.label,
3341
+ properties: input.properties,
3342
+ limit: input.limit
3343
+ }
3344
+ };
3345
+ } finally {
3346
+ await session.close();
3347
+ }
3348
+ } catch (error) {
3349
+ return {
3350
+ success: false,
3351
+ error: error instanceof Error ? error.message : "Failed to find nodes",
3352
+ query: {
3353
+ label: input.label,
3354
+ properties: input.properties
3355
+ }
3356
+ };
3357
+ }
3358
+ }).build();
3359
+ }
3360
+ function createNeo4jTraverseTool() {
3361
+ return toolBuilder().name("neo4j-traverse").description(
3362
+ "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."
3363
+ ).category(ToolCategory.DATABASE).tags(["neo4j", "graph", "database", "traverse", "relationships"]).schema(neo4jTraverseSchema).implement(async (input) => {
3364
+ if (!neo4jPool.isInitialized()) {
3365
+ return {
3366
+ success: false,
3367
+ error: "Neo4j connection not initialized. Please configure Neo4j connection first."
3368
+ };
3369
+ }
3370
+ try {
3371
+ const safeDirection = validateDirection(input.direction || "outgoing");
3372
+ const safeRelType = input.relationshipType ? validateRelationshipType(input.relationshipType) : null;
3373
+ const session = neo4jPool.getSession(input.database);
3374
+ try {
3375
+ let relPattern = "";
3376
+ if (safeDirection === "OUTGOING") {
3377
+ relPattern = safeRelType ? `-[r:${safeRelType}*1..${input.maxDepth}]->` : `-[r*1..${input.maxDepth}]->`;
3378
+ } else if (safeDirection === "INCOMING") {
3379
+ relPattern = safeRelType ? `<-[r:${safeRelType}*1..${input.maxDepth}]-` : `<-[r*1..${input.maxDepth}]-`;
3380
+ } else {
3381
+ relPattern = safeRelType ? `-[r:${safeRelType}*1..${input.maxDepth}]-` : `-[r*1..${input.maxDepth}]-`;
3382
+ }
3383
+ const cypher = `
3384
+ MATCH path = (start)${relPattern}(end)
3385
+ WHERE id(start) = $startNodeId
3386
+ RETURN start, end, relationships(path) as rels, length(path) as depth
3387
+ LIMIT $limit
3388
+ `;
3389
+ const parameters = {
3390
+ startNodeId: typeof input.startNodeId === "string" ? parseInt(input.startNodeId, 10) : input.startNodeId,
3391
+ limit: input.limit
3392
+ };
3393
+ const result = await session.run(cypher, parameters);
3394
+ const formattedResults = formatResults(result.records);
3395
+ return {
3396
+ success: true,
3397
+ paths: formattedResults.map((r) => ({
3398
+ start: r.start,
3399
+ end: r.end,
3400
+ relationships: r.rels,
3401
+ depth: r.depth
3402
+ })),
3403
+ count: result.records.length,
3404
+ query: {
3405
+ startNodeId: input.startNodeId,
3406
+ relationshipType: input.relationshipType,
3407
+ direction: input.direction,
3408
+ maxDepth: input.maxDepth
3409
+ }
3410
+ };
3411
+ } finally {
3412
+ await session.close();
3413
+ }
3414
+ } catch (error) {
3415
+ return {
3416
+ success: false,
3417
+ error: error instanceof Error ? error.message : "Failed to traverse graph",
3418
+ query: {
3419
+ startNodeId: input.startNodeId,
3420
+ relationshipType: input.relationshipType,
3421
+ direction: input.direction
3422
+ }
3423
+ };
3424
+ }
3425
+ }).build();
3426
+ }
3427
+ function createNeo4jVectorSearchTool() {
3428
+ return toolBuilder().name("neo4j-vector-search").description(
3429
+ "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."
3430
+ ).category(ToolCategory.DATABASE).tags(["neo4j", "graph", "database", "vector", "search", "semantic", "graphrag"]).schema(neo4jVectorSearchSchema).implement(async (input) => {
3431
+ if (!neo4jPool.isInitialized()) {
3432
+ return {
3433
+ success: false,
3434
+ error: "Neo4j connection not initialized. Please configure Neo4j connection first."
3435
+ };
3436
+ }
3437
+ try {
3438
+ const session = neo4jPool.getSession(input.database);
3439
+ try {
3440
+ const cypher = `
3441
+ CALL db.index.vector.queryNodes($indexName, $limit, $queryVector)
3442
+ YIELD node, score
3443
+ RETURN node, score
3444
+ ORDER BY score DESC
3445
+ `;
3446
+ const parameters = {
3447
+ indexName: input.indexName,
3448
+ limit: input.limit,
3449
+ queryVector: input.queryVector
3450
+ };
3451
+ const result = await session.run(cypher, parameters);
3452
+ const formattedResults = formatResults(result.records);
3453
+ return {
3454
+ success: true,
3455
+ results: formattedResults.map((r) => ({
3456
+ node: r.node,
3457
+ score: r.score
3458
+ })),
3459
+ count: result.records.length,
3460
+ query: {
3461
+ indexName: input.indexName,
3462
+ vectorDimension: input.queryVector.length,
3463
+ limit: input.limit
3464
+ }
3465
+ };
3466
+ } finally {
3467
+ await session.close();
3468
+ }
3469
+ } catch (error) {
3470
+ const errorMessage = error instanceof Error ? error.message : "Failed to perform vector search";
3471
+ let helpText = "";
3472
+ if (errorMessage.includes("index") || errorMessage.includes("not found")) {
3473
+ helpText = " Make sure the vector index exists. Create one with: CREATE VECTOR INDEX <name> FOR (n:Label) ON (n.embedding)";
3474
+ }
3475
+ return {
3476
+ success: false,
3477
+ error: errorMessage + helpText,
3478
+ query: {
3479
+ indexName: input.indexName,
3480
+ vectorDimension: input.queryVector.length
3481
+ }
3482
+ };
3483
+ }
3484
+ }).build();
3485
+ }
3486
+ var logger6 = createLogger("agentforge:tools:neo4j:vector-search");
3487
+ function createNeo4jVectorSearchWithEmbeddingTool() {
3488
+ return toolBuilder().name("neo4j-vector-search-with-embedding").description(
3489
+ "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."
3490
+ ).category(ToolCategory.DATABASE).tags(["neo4j", "graph", "database", "vector", "search", "semantic", "graphrag", "embedding", "ai"]).schema(neo4jVectorSearchWithEmbeddingSchema).implement(async (input) => {
3491
+ if (!neo4jPool.isInitialized()) {
3492
+ logger6.warn("Vector search attempted but Neo4j connection not initialized");
3493
+ return {
3494
+ success: false,
3495
+ error: "Neo4j connection not initialized. Please configure Neo4j connection first."
3496
+ };
3497
+ }
3498
+ if (!embeddingManager.isInitialized()) {
3499
+ logger6.warn("Vector search attempted but embedding manager not initialized");
3500
+ return {
3501
+ success: false,
3502
+ error: "Embedding manager not initialized. Please configure embedding provider (set OPENAI_API_KEY and optionally EMBEDDING_MODEL)."
3503
+ };
3504
+ }
3505
+ const startTime = Date.now();
3506
+ logger6.debug("Performing vector search with embedding", {
3507
+ queryTextLength: input.queryText.length,
3508
+ indexName: input.indexName,
3509
+ limit: input.limit,
3510
+ model: input.model
3511
+ });
3512
+ try {
3513
+ const embeddingResult = await embeddingManager.generateEmbedding(input.queryText, input.model);
3514
+ const session = neo4jPool.getSession(input.database);
3515
+ try {
3516
+ const cypher = `
3517
+ CALL db.index.vector.queryNodes($indexName, $limit, $queryVector)
3518
+ YIELD node, score
3519
+ RETURN node, score
3520
+ ORDER BY score DESC
3521
+ `;
3522
+ const parameters = {
3523
+ indexName: input.indexName,
3524
+ limit: input.limit,
3525
+ queryVector: embeddingResult.embedding
3526
+ };
3527
+ const result = await session.run(cypher, parameters);
3528
+ const formattedResults = formatResults(result.records);
3529
+ const duration = Date.now() - startTime;
3530
+ logger6.info("Vector search completed successfully", {
3531
+ resultCount: result.records.length,
3532
+ indexName: input.indexName,
3533
+ embeddingModel: embeddingResult.model,
3534
+ embeddingDimensions: embeddingResult.dimensions,
3535
+ duration
3536
+ });
3537
+ return {
3538
+ success: true,
3539
+ results: formattedResults.map((r) => ({
3540
+ node: r.node,
3541
+ score: r.score
3542
+ })),
3543
+ count: result.records.length,
3544
+ query: {
3545
+ text: input.queryText,
3546
+ indexName: input.indexName,
3547
+ embeddingModel: embeddingResult.model,
3548
+ vectorDimension: embeddingResult.dimensions,
3549
+ limit: input.limit
3550
+ },
3551
+ embedding: {
3552
+ model: embeddingResult.model,
3553
+ dimensions: embeddingResult.dimensions,
3554
+ usage: embeddingResult.usage
3555
+ }
3556
+ };
3557
+ } finally {
3558
+ await session.close();
3559
+ }
3560
+ } catch (error) {
3561
+ const duration = Date.now() - startTime;
3562
+ const errorMessage = error instanceof Error ? error.message : "Failed to perform vector search with embedding";
3563
+ let helpText = "";
3564
+ if (errorMessage.includes("index") || errorMessage.includes("not found")) {
3565
+ helpText = " Make sure the vector index exists. Create one with: CREATE VECTOR INDEX <name> FOR (n:Label) ON (n.embedding)";
3566
+ } else if (errorMessage.includes("API key") || errorMessage.includes("not initialized")) {
3567
+ helpText = " Make sure OPENAI_API_KEY is set in your environment variables.";
3568
+ } else if (errorMessage.includes("dimension")) {
3569
+ helpText = " Make sure the vector index dimensions match your embedding model dimensions.";
3570
+ }
3571
+ logger6.error("Vector search failed", {
3572
+ error: errorMessage,
3573
+ indexName: input.indexName,
3574
+ queryTextLength: input.queryText.length,
3575
+ duration
3576
+ });
3577
+ return {
3578
+ success: false,
3579
+ error: errorMessage + helpText,
3580
+ query: {
3581
+ text: input.queryText,
3582
+ indexName: input.indexName
3583
+ }
3584
+ };
3585
+ }
3586
+ }).build();
3587
+ }
3588
+ function createNeo4jCreateNodeWithEmbeddingTool() {
3589
+ return toolBuilder().name("neo4j-create-node-with-embedding").description(
3590
+ "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."
3591
+ ).category(ToolCategory.DATABASE).tags(["neo4j", "graph", "database", "create", "node", "embedding", "graphrag", "ai"]).schema(neo4jCreateNodeWithEmbeddingSchema).implement(async (input) => {
3592
+ if (!neo4jPool.isInitialized()) {
3593
+ return {
3594
+ success: false,
3595
+ error: "Neo4j connection not initialized. Please configure Neo4j connection first."
3596
+ };
3597
+ }
3598
+ if (!embeddingManager.isInitialized()) {
3599
+ return {
3600
+ success: false,
3601
+ error: "Embedding manager not initialized. Please configure embedding provider (set OPENAI_API_KEY and optionally EMBEDDING_MODEL)."
3602
+ };
3603
+ }
3604
+ try {
3605
+ const textToEmbed = input.properties[input.textProperty];
3606
+ if (!textToEmbed || typeof textToEmbed !== "string") {
3607
+ return {
3608
+ success: false,
3609
+ error: `Property '${input.textProperty}' not found or is not a string in the provided properties.`
3610
+ };
3611
+ }
3612
+ const embeddingResult = await embeddingManager.generateEmbedding(textToEmbed, input.model);
3613
+ const safeLabel = validateLabel(input.label);
3614
+ const safeEmbeddingProp = validatePropertyKey(input.embeddingProperty || "embedding");
3615
+ const session = neo4jPool.getSession(input.database);
3616
+ try {
3617
+ const allProperties = {
3618
+ ...input.properties
3619
+ };
3620
+ allProperties[input.embeddingProperty || "embedding"] = embeddingResult.embedding;
3621
+ const cypher = `
3622
+ CREATE (n:${safeLabel})
3623
+ SET n = $properties
3624
+ RETURN n, id(n) as nodeId
3625
+ `;
3626
+ const parameters = {
3627
+ properties: allProperties
3628
+ };
3629
+ const result = await session.run(cypher, parameters);
3630
+ const formattedResults = formatResults(result.records);
3631
+ if (formattedResults.length === 0) {
3632
+ return {
3633
+ success: false,
3634
+ error: "Failed to create node"
3635
+ };
3636
+ }
3637
+ const createdNode = formattedResults[0];
3638
+ return {
3639
+ success: true,
3640
+ node: createdNode.n,
3641
+ nodeId: createdNode.nodeId,
3642
+ embedding: {
3643
+ model: embeddingResult.model,
3644
+ dimensions: embeddingResult.dimensions,
3645
+ property: safeEmbeddingProp,
3646
+ usage: embeddingResult.usage
3647
+ },
3648
+ message: `Created node with label '${input.label}' and ${embeddingResult.dimensions}-dimensional embedding`
3649
+ };
3650
+ } finally {
3651
+ await session.close();
3652
+ }
3653
+ } catch (error) {
3654
+ const errorMessage = error instanceof Error ? error.message : "Failed to create node with embedding";
3655
+ let helpText = "";
3656
+ if (errorMessage.includes("API key") || errorMessage.includes("not initialized")) {
3657
+ helpText = " Make sure OPENAI_API_KEY is set in your environment variables.";
3658
+ } else if (errorMessage.includes("Syntax error") || errorMessage.includes("Invalid")) {
3659
+ helpText = " Check that the label and property names are valid.";
3660
+ }
3661
+ return {
3662
+ success: false,
3663
+ error: errorMessage + helpText
3664
+ };
3665
+ }
3666
+ }).build();
3667
+ }
3668
+
3669
+ // src/data/neo4j/index.ts
3670
+ var neo4jQuery = createNeo4jQueryTool();
3671
+ var neo4jGetSchema = createNeo4jGetSchemaTool();
3672
+ var neo4jFindNodes = createNeo4jFindNodesTool();
3673
+ var neo4jTraverse = createNeo4jTraverseTool();
3674
+ var neo4jVectorSearch = createNeo4jVectorSearchTool();
3675
+ var neo4jVectorSearchWithEmbedding = createNeo4jVectorSearchWithEmbeddingTool();
3676
+ var neo4jCreateNodeWithEmbedding = createNeo4jCreateNodeWithEmbeddingTool();
3677
+ var neo4jTools = [
3678
+ neo4jQuery,
3679
+ neo4jGetSchema,
3680
+ neo4jFindNodes,
3681
+ neo4jTraverse,
3682
+ neo4jVectorSearch,
3683
+ neo4jVectorSearchWithEmbedding,
3684
+ neo4jCreateNodeWithEmbedding
3685
+ ];
3686
+ var neo4jCoreTools = [
3687
+ neo4jQuery,
3688
+ neo4jGetSchema,
3689
+ neo4jFindNodes,
3690
+ neo4jTraverse,
3691
+ neo4jVectorSearch
3692
+ ];
3693
+ function createNeo4jTools(config = {}, includeEmbeddingTools = true) {
3694
+ if (config.uri && config.username && config.password) {
3695
+ neo4jPool.initialize({
3696
+ uri: config.uri,
3697
+ username: config.username,
3698
+ password: config.password,
3699
+ database: config.database,
3700
+ maxConnectionPoolSize: config.maxConnectionPoolSize,
3701
+ connectionTimeout: config.connectionTimeout
3702
+ }).catch((error) => {
3703
+ console.error("Failed to initialize Neo4j connection:", error);
3704
+ });
3705
+ }
3706
+ const coreTools = [
3707
+ createNeo4jQueryTool(),
3708
+ createNeo4jGetSchemaTool(),
3709
+ createNeo4jFindNodesTool(),
3710
+ createNeo4jTraverseTool(),
3711
+ createNeo4jVectorSearchTool()
3712
+ ];
3713
+ if (includeEmbeddingTools) {
3714
+ return [
3715
+ ...coreTools,
3716
+ createNeo4jVectorSearchWithEmbeddingTool(),
3717
+ createNeo4jCreateNodeWithEmbeddingTool()
3718
+ ];
3719
+ }
3720
+ return coreTools;
3721
+ }
3722
+ async function initializeNeo4jTools() {
3723
+ await initializeFromEnv();
3724
+ try {
3725
+ embeddingManager.initializeFromEnv();
3726
+ } catch (error) {
3727
+ }
3728
+ }
2028
3729
  var fileReaderSchema = z.object({
2029
3730
  path: z.string().describe("Path to the file to read"),
2030
3731
  encoding: z.enum(["utf8", "utf-8", "ascii", "base64", "hex", "binary"]).default("utf8").describe("File encoding")
@@ -3174,7 +4875,7 @@ var AskHumanInputSchema = z.object({
3174
4875
  suggestions: z.array(z.string()).optional().describe("Suggested responses for the human")
3175
4876
  });
3176
4877
  var logLevel3 = process.env.LOG_LEVEL?.toLowerCase() || LogLevel.INFO;
3177
- var logger3 = createLogger("askHuman", { level: logLevel3 });
4878
+ var logger7 = createLogger("askHuman", { level: logLevel3 });
3178
4879
  function createAskHumanTool() {
3179
4880
  return toolBuilder().name("ask-human").description(
3180
4881
  "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."
@@ -3207,13 +4908,13 @@ function createAskHumanTool() {
3207
4908
  suggestions: validatedInput.suggestions,
3208
4909
  status: "pending"
3209
4910
  };
3210
- logger3.debug("About to call interrupt()", { humanRequest });
4911
+ logger7.debug("About to call interrupt()", { humanRequest });
3211
4912
  let response;
3212
4913
  try {
3213
4914
  response = interrupt(humanRequest);
3214
- logger3.debug("interrupt() returned successfully", { response, responseType: typeof response });
4915
+ logger7.debug("interrupt() returned successfully", { response, responseType: typeof response });
3215
4916
  } catch (error) {
3216
- logger3.debug("interrupt() threw error (expected for GraphInterrupt)", {
4917
+ logger7.debug("interrupt() threw error (expected for GraphInterrupt)", {
3217
4918
  errorType: error?.constructor?.name,
3218
4919
  error: error instanceof Error ? error.message : String(error)
3219
4920
  });
@@ -3238,6 +4939,6 @@ function createAskHumanTool() {
3238
4939
  }
3239
4940
  var askHumanTool = createAskHumanTool();
3240
4941
 
3241
- export { AskHumanInputSchema, CalculatorSchema, CreditCardValidatorSchema, CurrentDateTimeSchema, DateArithmeticSchema, DateComparisonSchema, DateDifferenceSchema, DateFormatterSchema, DuckDuckGoProvider, EmailValidatorSchema, HttpMethod, IpValidatorSchema, MathFunctionsSchema, PhoneValidatorSchema, RandomNumberSchema, SerperProvider, StatisticsSchema, StringCaseConverterSchema, StringJoinSchema, StringLengthSchema, StringReplaceSchema, StringSplitSchema, StringSubstringSchema, StringTrimSchema, UrlValidatorSimpleSchema, UuidValidatorSchema, archiveConfluencePage, arrayFilter, arrayFilterSchema, arrayGroupBy, arrayGroupBySchema, arrayMap, arrayMapSchema, arraySort, arraySortSchema, askHumanTool, calculator, confluenceTools, createArrayFilterTool, createArrayGroupByTool, createArrayMapTool, createArraySortTool, createAskHumanTool, createCalculatorTool, createConfluencePage, createConfluenceTools, createCreditCardValidatorTool, createCsvGeneratorTool, createCsvParserTool, createCsvToJsonTool, createCsvTools, createCurrentDateTimeTool, createDateArithmeticTool, createDateComparisonTool, createDateDifferenceTool, createDateFormatterTool, createDateTimeTools, createDirectoryCreateTool, createDirectoryDeleteTool, createDirectoryListTool, createDirectoryOperationTools, createDuckDuckGoProvider, createEmailValidatorTool, createExtractImagesTool, createExtractLinksTool, createFileAppendTool, createFileDeleteTool, createFileExistsTool, createFileOperationTools, createFileReaderTool, createFileSearchTool, createFileWriterTool, createHtmlParserTool, createHtmlParserTools, createHttpTools, createIpValidatorTool, createJsonMergeTool, createJsonParserTool, createJsonQueryTool, createJsonStringifyTool, createJsonToCsvTool, createJsonToXmlTool, createJsonTools, createJsonValidatorTool, createMathFunctionsTool, createMathOperationTools, createObjectOmitTool, createObjectPickTool, createPathBasenameTool, createPathDirnameTool, createPathExtensionTool, createPathJoinTool, createPathNormalizeTool, createPathParseTool, createPathRelativeTool, createPathResolveTool, createPathUtilityTools, createPhoneValidatorTool, createRandomNumberTool, createScraperTools, createSerperProvider, createSlackTools, createStatisticsTool, createStringCaseConverterTool, createStringJoinTool, createStringLengthTool, createStringReplaceTool, createStringSplitTool, createStringSubstringTool, createStringTrimTool, createStringUtilityTools, createTransformerTools, createUrlBuilderTool, createUrlQueryParserTool, createUrlValidatorSimpleTool, createUrlValidatorTool, createUrlValidatorTools, createUuidValidatorTool, createValidationTools, createWebScraperTool, createXmlGeneratorTool, createXmlParserTool, createXmlToJsonTool, createXmlTools, creditCardValidator, csvGenerator, csvGeneratorSchema, csvParser, csvParserSchema, csvToJson, csvToJsonSchema, csvTools, currentDateTime, dateArithmetic, dateComparison, dateDifference, dateFormatter, dateTimeTools, directoryCreate, directoryCreateSchema, directoryDelete, directoryDeleteSchema, directoryList, directoryListSchema, directoryOperationTools, emailValidator, extractImages, extractImagesSchema, extractLinks, extractLinksSchema, fileAppend, fileAppendSchema, fileDelete, fileDeleteSchema, fileExists, fileExistsSchema, fileOperationTools, fileReader, fileReaderSchema, fileSearch, fileSearchSchema, fileWriter, fileWriterSchema, getConfluencePage, getSlackChannels, getSlackMessages, getSpacePages, htmlParser, htmlParserSchema, htmlParserTools, httpClient, httpGet, httpGetSchema, httpPost, httpPostSchema, httpRequestSchema, httpTools, ipValidator, jsonMerge, jsonMergeSchema, jsonParser, jsonParserSchema, jsonQuery, jsonQuerySchema, jsonStringify, jsonStringifySchema, jsonToCsv, jsonToCsvSchema, jsonToXml, jsonToXmlSchema, jsonTools, jsonValidator, jsonValidatorSchema, listConfluenceSpaces, mathFunctions, mathOperationTools, notifySlack, objectOmit, objectOmitSchema, objectPick, objectPickSchema, pathBasename, pathBasenameSchema, pathDirname, pathDirnameSchema, pathExtension, pathExtensionSchema, pathJoin, pathJoinSchema, pathNormalize, pathNormalizeSchema, pathParse, pathParseSchema, pathRelative, pathRelativeSchema, pathResolve, pathResolveSchema, pathUtilityTools, phoneValidator, randomNumber, scraperTools, searchConfluence, searchResultSchema, sendSlackMessage, slackTools, statistics, stringCaseConverter, stringJoin, stringLength, stringReplace, stringSplit, stringSubstring, stringTrim, stringUtilityTools, transformerTools, updateConfluencePage, urlBuilder, urlBuilderSchema, urlQueryParser, urlQueryParserSchema, urlValidator, urlValidatorSchema, urlValidatorSimple, urlValidatorTools, uuidValidator, validationTools, webScraper, webScraperSchema, webSearch, webSearchOutputSchema, webSearchSchema, xmlGenerator, xmlGeneratorSchema, xmlParser, xmlParserSchema, xmlToJson, xmlToJsonSchema, xmlTools };
4942
+ export { AskHumanInputSchema, CalculatorSchema, CreditCardValidatorSchema, CurrentDateTimeSchema, DEFAULT_RETRY_CONFIG2 as DEFAULT_RETRY_CONFIG, DateArithmeticSchema, DateComparisonSchema, DateDifferenceSchema, DateFormatterSchema, DuckDuckGoProvider, EmailValidatorSchema, EmbeddingManager, HttpMethod, IpValidatorSchema, MathFunctionsSchema, OpenAIEmbeddingProvider, PhoneValidatorSchema, RandomNumberSchema, SerperProvider, StatisticsSchema, StringCaseConverterSchema, StringJoinSchema, StringLengthSchema, StringReplaceSchema, StringSplitSchema, StringSubstringSchema, StringTrimSchema, UrlValidatorSimpleSchema, UuidValidatorSchema, archiveConfluencePage, arrayFilter, arrayFilterSchema, arrayGroupBy, arrayGroupBySchema, arrayMap, arrayMapSchema, arraySort, arraySortSchema, askHumanTool, calculator, confluenceTools, createArrayFilterTool, createArrayGroupByTool, createArrayMapTool, createArraySortTool, createAskHumanTool, createCalculatorTool, createConfluencePage, createConfluenceTools, createCreditCardValidatorTool, createCsvGeneratorTool, createCsvParserTool, createCsvToJsonTool, createCsvTools, createCurrentDateTimeTool, createDateArithmeticTool, createDateComparisonTool, createDateDifferenceTool, createDateFormatterTool, createDateTimeTools, createDirectoryCreateTool, createDirectoryDeleteTool, createDirectoryListTool, createDirectoryOperationTools, createDuckDuckGoProvider, createEmailValidatorTool, createExtractImagesTool, createExtractLinksTool, createFileAppendTool, createFileDeleteTool, createFileExistsTool, createFileOperationTools, createFileReaderTool, createFileSearchTool, createFileWriterTool, createHtmlParserTool, createHtmlParserTools, createHttpTools, createIpValidatorTool, createJsonMergeTool, createJsonParserTool, createJsonQueryTool, createJsonStringifyTool, createJsonToCsvTool, createJsonToXmlTool, createJsonTools, createJsonValidatorTool, createMathFunctionsTool, createMathOperationTools, createNeo4jCreateNodeWithEmbeddingTool, createNeo4jFindNodesTool, createNeo4jGetSchemaTool, createNeo4jQueryTool, createNeo4jTools, createNeo4jTraverseTool, createNeo4jVectorSearchTool, createNeo4jVectorSearchWithEmbeddingTool, createObjectOmitTool, createObjectPickTool, createPathBasenameTool, createPathDirnameTool, createPathExtensionTool, createPathJoinTool, createPathNormalizeTool, createPathParseTool, createPathRelativeTool, createPathResolveTool, createPathUtilityTools, createPhoneValidatorTool, createRandomNumberTool, createScraperTools, createSerperProvider, createSlackTools, createStatisticsTool, createStringCaseConverterTool, createStringJoinTool, createStringLengthTool, createStringReplaceTool, createStringSplitTool, createStringSubstringTool, createStringTrimTool, createStringUtilityTools, createTransformerTools, createUrlBuilderTool, createUrlQueryParserTool, createUrlValidatorSimpleTool, createUrlValidatorTool, createUrlValidatorTools, createUuidValidatorTool, createValidationTools, createWebScraperTool, createXmlGeneratorTool, createXmlParserTool, createXmlToJsonTool, createXmlTools, creditCardValidator, csvGenerator, csvGeneratorSchema, csvParser, csvParserSchema, csvToJson, csvToJsonSchema, csvTools, currentDateTime, dateArithmetic, dateComparison, dateDifference, dateFormatter, dateTimeTools, directoryCreate, directoryCreateSchema, directoryDelete, directoryDeleteSchema, directoryList, directoryListSchema, directoryOperationTools, emailValidator, embeddingManager, extractImages, extractImagesSchema, extractLinks, extractLinksSchema, fileAppend, fileAppendSchema, fileDelete, fileDeleteSchema, fileExists, fileExistsSchema, fileOperationTools, fileReader, fileReaderSchema, fileSearch, fileSearchSchema, fileWriter, fileWriterSchema, generateBatchEmbeddings, generateEmbedding, getCohereApiKey, getConfluencePage, getEmbeddingModel, getEmbeddingProvider, getHuggingFaceApiKey, getOllamaBaseUrl, getOpenAIApiKey, getSlackChannels, getSlackMessages, getSpacePages, getVoyageApiKey, htmlParser, htmlParserSchema, htmlParserTools, httpClient, httpGet, httpGetSchema, httpPost, httpPostSchema, httpRequestSchema, httpTools, initializeEmbeddings, initializeEmbeddingsWithConfig, initializeFromEnv, initializeNeo4jTools, ipValidator, isRetryableError2 as isRetryableError, jsonMerge, jsonMergeSchema, jsonParser, jsonParserSchema, jsonQuery, jsonQuerySchema, jsonStringify, jsonStringifySchema, jsonToCsv, jsonToCsvSchema, jsonToXml, jsonToXmlSchema, jsonTools, jsonValidator, jsonValidatorSchema, listConfluenceSpaces, mathFunctions, mathOperationTools, neo4jCoreTools, neo4jCreateNodeWithEmbedding, neo4jCreateNodeWithEmbeddingSchema, neo4jFindNodes, neo4jFindNodesSchema, neo4jGetSchema, neo4jGetSchemaSchema, neo4jPool, neo4jQuery, neo4jQuerySchema, neo4jTools, neo4jTraverse, neo4jTraverseSchema, neo4jVectorSearch, neo4jVectorSearchSchema, neo4jVectorSearchWithEmbedding, neo4jVectorSearchWithEmbeddingSchema, notifySlack, objectOmit, objectOmitSchema, objectPick, objectPickSchema, pathBasename, pathBasenameSchema, pathDirname, pathDirnameSchema, pathExtension, pathExtensionSchema, pathJoin, pathJoinSchema, pathNormalize, pathNormalizeSchema, pathParse, pathParseSchema, pathRelative, pathRelativeSchema, pathResolve, pathResolveSchema, pathUtilityTools, phoneValidator, randomNumber, retryWithBackoff2 as retryWithBackoff, scraperTools, searchConfluence, searchResultSchema, sendSlackMessage, slackTools, statistics, stringCaseConverter, stringJoin, stringLength, stringReplace, stringSplit, stringSubstring, stringTrim, stringUtilityTools, transformerTools, updateConfluencePage, urlBuilder, urlBuilderSchema, urlQueryParser, urlQueryParserSchema, urlValidator, urlValidatorSchema, urlValidatorSimple, urlValidatorTools, uuidValidator, validateBatch, validateText, validationTools, webScraper, webScraperSchema, webSearch, webSearchOutputSchema, webSearchSchema, xmlGenerator, xmlGeneratorSchema, xmlParser, xmlParserSchema, xmlToJson, xmlToJsonSchema, xmlTools };
3242
4943
  //# sourceMappingURL=index.js.map
3243
4944
  //# sourceMappingURL=index.js.map