@agentforge/tools 0.17.2 → 0.18.1

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,4 +1,4 @@
1
- import { toolBuilder, ToolCategory, LogLevel, createLogger } from '@agentforge/core';
1
+ import { toolBuilder, ToolCategory, LogLevel, createLogger, createTool } from '@agentforge/core';
2
2
  import { lookup } from 'dns/promises';
3
3
  import net from 'net';
4
4
  import axios11 from 'axios';
@@ -12,6 +12,7 @@ import neo4j, { auth, Integer, Node, Relationship, Path } from 'neo4j-driver';
12
12
  import { sql } from 'drizzle-orm';
13
13
  import { EventEmitter } from 'events';
14
14
  import { Readable } from 'stream';
15
+ import { AsyncLocalStorage } from 'async_hooks';
15
16
  import { createHash, randomUUID } from 'crypto';
16
17
  import { promises } from 'fs';
17
18
  import * as path from 'path';
@@ -995,7 +996,7 @@ function getDefaultSlackClient() {
995
996
  }
996
997
  };
997
998
  }
998
- function createSendSlackMessageTool(getSlackClient, logger19) {
999
+ function createSendSlackMessageTool(getSlackClient, logger20) {
999
1000
  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(
1000
1001
  "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."
1001
1002
  ).suggests(["get-slack-channels"]).schema(
@@ -1004,7 +1005,7 @@ function createSendSlackMessageTool(getSlackClient, logger19) {
1004
1005
  message: z.string().describe("Message content to send")
1005
1006
  })
1006
1007
  ).implementSafe(async ({ channel, message }) => {
1007
- logger19.info("send-slack-message called", { channel, messageLength: message.length });
1008
+ logger20.info("send-slack-message called", { channel, messageLength: message.length });
1008
1009
  try {
1009
1010
  const { client: slack, config } = getSlackClient();
1010
1011
  const result = await slack.chat.postMessage({
@@ -1013,7 +1014,7 @@ function createSendSlackMessageTool(getSlackClient, logger19) {
1013
1014
  username: config.botName,
1014
1015
  icon_emoji: config.botIcon
1015
1016
  });
1016
- logger19.info("send-slack-message result", {
1017
+ logger20.info("send-slack-message result", {
1017
1018
  ...result.channel ? { channel: result.channel } : {},
1018
1019
  ...result.ts ? { timestamp: result.ts } : {},
1019
1020
  messageLength: message.length,
@@ -1026,7 +1027,7 @@ function createSendSlackMessageTool(getSlackClient, logger19) {
1026
1027
  message_id: result.ts
1027
1028
  };
1028
1029
  } catch (error) {
1029
- logger19.error("send-slack-message failed", {
1030
+ logger20.error("send-slack-message failed", {
1030
1031
  channel,
1031
1032
  error: error.message,
1032
1033
  data: error.data
@@ -1035,7 +1036,7 @@ function createSendSlackMessageTool(getSlackClient, logger19) {
1035
1036
  }
1036
1037
  }).build();
1037
1038
  }
1038
- function createNotifySlackTool(getSlackClient, logger19) {
1039
+ function createNotifySlackTool(getSlackClient, logger20) {
1039
1040
  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(
1040
1041
  "Use this for urgent notifications that require @mentions. For general messages without mentions, use send-slack-message instead."
1041
1042
  ).suggests(["get-slack-channels"]).schema(
@@ -1045,7 +1046,7 @@ function createNotifySlackTool(getSlackClient, logger19) {
1045
1046
  mentions: z.array(z.string()).optional().describe("List of usernames to mention (without @)")
1046
1047
  })
1047
1048
  ).implementSafe(async ({ channel, message, mentions = [] }) => {
1048
- logger19.info("notify-slack called", {
1049
+ logger20.info("notify-slack called", {
1049
1050
  channel,
1050
1051
  messageLength: message.length,
1051
1052
  mentionCount: mentions.length
@@ -1059,7 +1060,7 @@ function createNotifySlackTool(getSlackClient, logger19) {
1059
1060
  username: config.botName,
1060
1061
  icon_emoji: config.botIcon
1061
1062
  });
1062
- logger19.info("notify-slack result", {
1063
+ logger20.info("notify-slack result", {
1063
1064
  ...result.channel ? { channel: result.channel } : {},
1064
1065
  ...result.ts ? { timestamp: result.ts } : {},
1065
1066
  mentions: mentions.length
@@ -1073,7 +1074,7 @@ function createNotifySlackTool(getSlackClient, logger19) {
1073
1074
  };
1074
1075
  }).build();
1075
1076
  }
1076
- function createGetSlackChannelsTool(getSlackClient, logger19) {
1077
+ function createGetSlackChannelsTool(getSlackClient, logger20) {
1077
1078
  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(
1078
1079
  "Use this first to discover available channels before sending messages. Helps ensure you are sending to the correct channel."
1079
1080
  ).follows(["send-slack-message", "notify-slack"]).schema(
@@ -1081,7 +1082,7 @@ function createGetSlackChannelsTool(getSlackClient, logger19) {
1081
1082
  include_private: z.boolean().optional().describe("Include private channels (default: false)")
1082
1083
  })
1083
1084
  ).implementSafe(async ({ include_private = false }) => {
1084
- logger19.info("get-slack-channels called", { include_private });
1085
+ logger20.info("get-slack-channels called", { include_private });
1085
1086
  const { client: slack } = getSlackClient();
1086
1087
  const publicChannels = await slack.conversations.list({
1087
1088
  types: "public_channel",
@@ -1095,7 +1096,7 @@ function createGetSlackChannelsTool(getSlackClient, logger19) {
1095
1096
  });
1096
1097
  allChannels = [...allChannels, ...privateChannels.channels || []];
1097
1098
  }
1098
- logger19.info("get-slack-channels result", {
1099
+ logger20.info("get-slack-channels result", {
1099
1100
  channelCount: allChannels.length,
1100
1101
  includePrivate: include_private
1101
1102
  });
@@ -1110,7 +1111,7 @@ function createGetSlackChannelsTool(getSlackClient, logger19) {
1110
1111
  };
1111
1112
  }).build();
1112
1113
  }
1113
- function createGetSlackMessagesTool(getSlackClient, logger19) {
1114
+ function createGetSlackMessagesTool(getSlackClient, logger20) {
1114
1115
  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(
1115
1116
  "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)."
1116
1117
  ).suggests(["get-slack-channels"]).schema(
@@ -1119,7 +1120,7 @@ function createGetSlackMessagesTool(getSlackClient, logger19) {
1119
1120
  limit: z.number().int().min(1).max(100).optional().describe("Number of messages to retrieve (default: 20, max: 100)")
1120
1121
  })
1121
1122
  ).implementSafe(async ({ channel, limit = 20 }) => {
1122
- logger19.info("get-slack-messages called", { channel, limit });
1123
+ logger20.info("get-slack-messages called", { channel, limit });
1123
1124
  try {
1124
1125
  const { client: slack } = getSlackClient();
1125
1126
  let channelId = channel;
@@ -1130,7 +1131,7 @@ function createGetSlackMessagesTool(getSlackClient, logger19) {
1130
1131
  });
1131
1132
  const found = channels.channels?.find((c) => c.name === channel);
1132
1133
  if (!found) {
1133
- logger19.error("get-slack-messages: channel not found", { channel });
1134
+ logger20.error("get-slack-messages: channel not found", { channel });
1134
1135
  throw new Error(
1135
1136
  `Channel '${channel}' not found. Use get-slack-channels to see available channels.`
1136
1137
  );
@@ -1142,7 +1143,7 @@ function createGetSlackMessagesTool(getSlackClient, logger19) {
1142
1143
  limit: Math.min(limit, 100)
1143
1144
  // Cap at 100 for performance
1144
1145
  });
1145
- logger19.info("get-slack-messages result", {
1146
+ logger20.info("get-slack-messages result", {
1146
1147
  channel: channelId,
1147
1148
  messageCount: result.messages?.length || 0,
1148
1149
  limit
@@ -1160,7 +1161,7 @@ function createGetSlackMessagesTool(getSlackClient, logger19) {
1160
1161
  })) || []
1161
1162
  };
1162
1163
  } catch (error) {
1163
- logger19.error("get-slack-messages failed", {
1164
+ logger20.error("get-slack-messages failed", {
1164
1165
  channel,
1165
1166
  error: error.message,
1166
1167
  data: error.data
@@ -1241,12 +1242,12 @@ function getAuthHeader() {
1241
1242
  const auth2 = Buffer.from(`${ATLASSIAN_EMAIL}:${ATLASSIAN_API_KEY}`).toString("base64");
1242
1243
  return `Basic ${auth2}`;
1243
1244
  }
1244
- function createSearchConfluenceTool(getAuth, getAuthHeader2, logger19) {
1245
+ function createSearchConfluenceTool(getAuth, getAuthHeader2, logger20) {
1245
1246
  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({
1246
1247
  query: z.string().describe("Search query or CQL expression (e.g., 'payment processing' or 'space=BL3 AND title~payment')"),
1247
1248
  limit: z.number().optional().describe("Maximum number of results to return (default: 10, max: 25)")
1248
1249
  })).implement(async ({ query, limit = 10 }) => {
1249
- logger19.info("search-confluence called", { query, limit });
1250
+ logger20.info("search-confluence called", { query, limit });
1250
1251
  try {
1251
1252
  const { ATLASSIAN_SITE_URL } = getAuth();
1252
1253
  const response = await axios11.get(`${ATLASSIAN_SITE_URL}/wiki/rest/api/content/search`, {
@@ -1271,13 +1272,13 @@ function createSearchConfluenceTool(getAuth, getAuthHeader2, logger19) {
1271
1272
  lastModified: page.version?.when || ""
1272
1273
  }));
1273
1274
  if (results.length === 0) {
1274
- logger19.warn("search-confluence returned NO RESULTS - this is a valid outcome, agent should not retry", {
1275
+ logger20.warn("search-confluence returned NO RESULTS - this is a valid outcome, agent should not retry", {
1275
1276
  query,
1276
1277
  limit,
1277
1278
  totalSize: response.data.totalSize
1278
1279
  });
1279
1280
  } else {
1280
- logger19.info("search-confluence result", {
1281
+ logger20.info("search-confluence result", {
1281
1282
  query,
1282
1283
  resultCount: results.length,
1283
1284
  totalSize: response.data.totalSize,
@@ -1292,7 +1293,7 @@ function createSearchConfluenceTool(getAuth, getAuthHeader2, logger19) {
1292
1293
  results
1293
1294
  });
1294
1295
  } catch (error) {
1295
- logger19.error("search-confluence error", {
1296
+ logger20.error("search-confluence error", {
1296
1297
  query,
1297
1298
  error: error.response?.data?.message || error.message,
1298
1299
  status: error.response?.status
@@ -1304,11 +1305,11 @@ function createSearchConfluenceTool(getAuth, getAuthHeader2, logger19) {
1304
1305
  }
1305
1306
  }).build();
1306
1307
  }
1307
- function createGetConfluencePageTool(getAuth, getAuthHeader2, logger19) {
1308
+ function createGetConfluencePageTool(getAuth, getAuthHeader2, logger20) {
1308
1309
  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({
1309
1310
  page_id: z.string().describe("The Confluence page ID (from search results)")
1310
1311
  })).implement(async ({ page_id }) => {
1311
- logger19.info("get-confluence-page called", { page_id });
1312
+ logger20.info("get-confluence-page called", { page_id });
1312
1313
  try {
1313
1314
  const { ATLASSIAN_SITE_URL } = getAuth();
1314
1315
  const response = await axios11.get(`${ATLASSIAN_SITE_URL}/wiki/rest/api/content/${page_id}`, {
@@ -1321,7 +1322,7 @@ function createGetConfluencePageTool(getAuth, getAuthHeader2, logger19) {
1321
1322
  }
1322
1323
  });
1323
1324
  const page = response.data;
1324
- logger19.info("get-confluence-page result", {
1325
+ logger20.info("get-confluence-page result", {
1325
1326
  page_id,
1326
1327
  title: page.title,
1327
1328
  space: page.space?.name,
@@ -1343,7 +1344,7 @@ function createGetConfluencePageTool(getAuth, getAuthHeader2, logger19) {
1343
1344
  }
1344
1345
  });
1345
1346
  } catch (error) {
1346
- logger19.error("get-confluence-page error", {
1347
+ logger20.error("get-confluence-page error", {
1347
1348
  page_id,
1348
1349
  error: error.response?.data?.message || error.message,
1349
1350
  status: error.response?.status
@@ -1355,11 +1356,11 @@ function createGetConfluencePageTool(getAuth, getAuthHeader2, logger19) {
1355
1356
  }
1356
1357
  }).build();
1357
1358
  }
1358
- function createListConfluenceSpacesTool(getAuth, getAuthHeader2, logger19) {
1359
+ function createListConfluenceSpacesTool(getAuth, getAuthHeader2, logger20) {
1359
1360
  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({
1360
1361
  limit: z.number().optional().describe("Maximum number of spaces to return (default: 25)")
1361
1362
  })).implement(async ({ limit = 25 }) => {
1362
- logger19.info("list-confluence-spaces called", { limit });
1363
+ logger20.info("list-confluence-spaces called", { limit });
1363
1364
  try {
1364
1365
  const { ATLASSIAN_SITE_URL } = getAuth();
1365
1366
  const response = await axios11.get(`${ATLASSIAN_SITE_URL}/wiki/rest/api/space`, {
@@ -1378,7 +1379,7 @@ function createListConfluenceSpacesTool(getAuth, getAuthHeader2, logger19) {
1378
1379
  description: space.description?.plain?.value || "",
1379
1380
  url: `${ATLASSIAN_SITE_URL}/wiki${space._links.webui}`
1380
1381
  }));
1381
- logger19.info("list-confluence-spaces result", {
1382
+ logger20.info("list-confluence-spaces result", {
1382
1383
  spaceCount: spaces.length,
1383
1384
  spaceKeys: spaces.map((s) => s.key).slice(0, 5)
1384
1385
  // Log first 5 space keys
@@ -1389,7 +1390,7 @@ function createListConfluenceSpacesTool(getAuth, getAuthHeader2, logger19) {
1389
1390
  spaces
1390
1391
  });
1391
1392
  } catch (error) {
1392
- logger19.error("list-confluence-spaces error", {
1393
+ logger20.error("list-confluence-spaces error", {
1393
1394
  error: error.response?.data?.message || error.message,
1394
1395
  status: error.response?.status
1395
1396
  });
@@ -1400,12 +1401,12 @@ function createListConfluenceSpacesTool(getAuth, getAuthHeader2, logger19) {
1400
1401
  }
1401
1402
  }).build();
1402
1403
  }
1403
- function createGetSpacePagesTool(getAuth, getAuthHeader2, logger19) {
1404
+ function createGetSpacePagesTool(getAuth, getAuthHeader2, logger20) {
1404
1405
  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({
1405
1406
  space_key: z.string().describe("The space key (e.g., 'AI', 'BL3', 'FIN')"),
1406
1407
  limit: z.number().optional().describe("Maximum number of pages to return (default: 25)")
1407
1408
  })).implement(async ({ space_key, limit = 25 }) => {
1408
- logger19.info("get-space-pages called", { space_key, limit });
1409
+ logger20.info("get-space-pages called", { space_key, limit });
1409
1410
  try {
1410
1411
  const { ATLASSIAN_SITE_URL } = getAuth();
1411
1412
  const response = await axios11.get(`${ATLASSIAN_SITE_URL}/wiki/rest/api/content`, {
@@ -1427,12 +1428,12 @@ function createGetSpacePagesTool(getAuth, getAuthHeader2, logger19) {
1427
1428
  lastModified: page.version?.when || ""
1428
1429
  }));
1429
1430
  if (pages.length === 0) {
1430
- logger19.warn("get-space-pages returned NO PAGES - this is a valid outcome, agent should not retry", {
1431
+ logger20.warn("get-space-pages returned NO PAGES - this is a valid outcome, agent should not retry", {
1431
1432
  space_key,
1432
1433
  limit
1433
1434
  });
1434
1435
  } else {
1435
- logger19.info("get-space-pages result", {
1436
+ logger20.info("get-space-pages result", {
1436
1437
  space_key,
1437
1438
  pageCount: pages.length,
1438
1439
  titles: pages.map((p) => p.title).slice(0, 3)
@@ -1446,7 +1447,7 @@ function createGetSpacePagesTool(getAuth, getAuthHeader2, logger19) {
1446
1447
  pages
1447
1448
  });
1448
1449
  } catch (error) {
1449
- logger19.error("get-space-pages error", {
1450
+ logger20.error("get-space-pages error", {
1450
1451
  space_key,
1451
1452
  error: error.response?.data?.message || error.message,
1452
1453
  status: error.response?.status
@@ -1458,14 +1459,14 @@ function createGetSpacePagesTool(getAuth, getAuthHeader2, logger19) {
1458
1459
  }
1459
1460
  }).build();
1460
1461
  }
1461
- function createCreateConfluencePageTool(getAuth, getAuthHeader2, logger19) {
1462
+ function createCreateConfluencePageTool(getAuth, getAuthHeader2, logger20) {
1462
1463
  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({
1463
1464
  space_key: z.string().describe("The space key where the page will be created (e.g., 'AI', 'BL3')"),
1464
1465
  title: z.string().describe("The title of the new page"),
1465
1466
  content: z.string().describe("The page content in HTML format (Confluence storage format)"),
1466
1467
  parent_page_id: z.string().optional().describe("Optional parent page ID to create this as a child page")
1467
1468
  })).implement(async ({ space_key, title, content, parent_page_id }) => {
1468
- logger19.info("create-confluence-page called", { space_key, title, hasParent: !!parent_page_id });
1469
+ logger20.info("create-confluence-page called", { space_key, title, hasParent: !!parent_page_id });
1469
1470
  try {
1470
1471
  const { ATLASSIAN_SITE_URL } = getAuth();
1471
1472
  const pageData = {
@@ -1492,7 +1493,7 @@ function createCreateConfluencePageTool(getAuth, getAuthHeader2, logger19) {
1492
1493
  }
1493
1494
  }
1494
1495
  );
1495
- logger19.info("create-confluence-page result", {
1496
+ logger20.info("create-confluence-page result", {
1496
1497
  page_id: response.data.id,
1497
1498
  title: response.data.title,
1498
1499
  space: space_key
@@ -1508,7 +1509,7 @@ function createCreateConfluencePageTool(getAuth, getAuthHeader2, logger19) {
1508
1509
  }
1509
1510
  });
1510
1511
  } catch (error) {
1511
- logger19.error("create-confluence-page error", {
1512
+ logger20.error("create-confluence-page error", {
1512
1513
  space_key,
1513
1514
  title,
1514
1515
  error: error.response?.data?.message || error.message,
@@ -1521,13 +1522,13 @@ function createCreateConfluencePageTool(getAuth, getAuthHeader2, logger19) {
1521
1522
  }
1522
1523
  }).build();
1523
1524
  }
1524
- function createUpdateConfluencePageTool(getAuth, getAuthHeader2, logger19) {
1525
+ function createUpdateConfluencePageTool(getAuth, getAuthHeader2, logger20) {
1525
1526
  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({
1526
1527
  page_id: z.string().describe("The ID of the page to update"),
1527
1528
  title: z.string().describe("The new title for the page"),
1528
1529
  content: z.string().describe("The new content in HTML format (Confluence storage format)")
1529
1530
  })).implement(async ({ page_id, title, content }) => {
1530
- logger19.info("update-confluence-page called", { page_id, title });
1531
+ logger20.info("update-confluence-page called", { page_id, title });
1531
1532
  try {
1532
1533
  const { ATLASSIAN_SITE_URL } = getAuth();
1533
1534
  const getResponse = await axios11.get(
@@ -1560,7 +1561,7 @@ function createUpdateConfluencePageTool(getAuth, getAuthHeader2, logger19) {
1560
1561
  }
1561
1562
  }
1562
1563
  );
1563
- logger19.info("update-confluence-page result", {
1564
+ logger20.info("update-confluence-page result", {
1564
1565
  page_id,
1565
1566
  title: updateResponse.data.title,
1566
1567
  previousVersion: currentVersion,
@@ -1577,7 +1578,7 @@ function createUpdateConfluencePageTool(getAuth, getAuthHeader2, logger19) {
1577
1578
  }
1578
1579
  });
1579
1580
  } catch (error) {
1580
- logger19.error("update-confluence-page error", {
1581
+ logger20.error("update-confluence-page error", {
1581
1582
  page_id,
1582
1583
  title,
1583
1584
  error: error.response?.data?.message || error.message,
@@ -1590,12 +1591,12 @@ function createUpdateConfluencePageTool(getAuth, getAuthHeader2, logger19) {
1590
1591
  }
1591
1592
  }).build();
1592
1593
  }
1593
- function createArchiveConfluencePageTool(getAuth, getAuthHeader2, logger19) {
1594
+ function createArchiveConfluencePageTool(getAuth, getAuthHeader2, logger20) {
1594
1595
  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({
1595
1596
  page_id: z.string().describe("The ID of the page to archive"),
1596
1597
  reason: z.string().optional().describe("Optional reason for archiving (for audit trail)")
1597
1598
  })).implement(async ({ page_id, reason }) => {
1598
- logger19.info("archive-confluence-page called", {
1599
+ logger20.info("archive-confluence-page called", {
1599
1600
  page_id,
1600
1601
  ...reason ? { reason } : {}
1601
1602
  });
@@ -1629,7 +1630,7 @@ function createArchiveConfluencePageTool(getAuth, getAuthHeader2, logger19) {
1629
1630
  }
1630
1631
  }
1631
1632
  );
1632
- logger19.info("archive-confluence-page result", {
1633
+ logger20.info("archive-confluence-page result", {
1633
1634
  page_id,
1634
1635
  title: pageData.title,
1635
1636
  previousVersion: currentVersion,
@@ -1647,7 +1648,7 @@ function createArchiveConfluencePageTool(getAuth, getAuthHeader2, logger19) {
1647
1648
  }
1648
1649
  });
1649
1650
  } catch (error) {
1650
- logger19.error("archive-confluence-page error", {
1651
+ logger20.error("archive-confluence-page error", {
1651
1652
  page_id,
1652
1653
  error: error.response?.data?.message || error.message,
1653
1654
  status: error.response?.status
@@ -4157,11 +4158,41 @@ function getInstallationInstructions(vendor) {
4157
4158
 
4158
4159
  // src/data/relational/utils/sql-sanitizer.ts
4159
4160
  var DANGEROUS_SQL_STATEMENT_PATTERN = /^(create|drop|truncate|alter)\b/i;
4161
+ var TRANSACTION_CONTROL_STATEMENT_PATTERN = /^(begin|start\s+transaction|commit|end|abort|rollback|prepare\s+transaction|savepoint|release(?:\s+savepoint)?|set\s+(?:(?:local|session|global)\s+)?(?:characteristics\s+as\s+)?transaction)\b/i;
4162
+ var AUTOCOMMIT_STATEMENT_PATTERN = /^set\s+(?:(?:session|local|global)\s+|@@(?:(?:session|local|global)\.)?)?autocommit\b/i;
4163
+ var MYSQL_IMPLICIT_COMMIT_STATEMENT_PATTERN = /^(?:(?:alter|create|drop)\s+|truncate\s+table\b|rename\s+(?:table|user)\b|(?:lock|unlock)\s+tables\b|(?:grant|revoke)\b|set\s+password\b|(?:analyze|check|optimize|repair)\s+table\b|cache\s+index\b|load\s+(?:data|xml|index\s+into\s+cache)\b|flush\b|reset\b|(?:start|stop)\s+(?:replica|slave)\b|change\s+(?:replication\s+source|master)\s+to\b|(?:install|uninstall)\s+(?:plugin|component)\b)/i;
4164
+ var MYSQL_INDIRECT_EXECUTION_STATEMENT_PATTERN = /^(?:call\b|prepare\b|execute(?:\s+immediate)?\b|deallocate\s+prepare\b)/i;
4165
+ var MANAGED_TRANSACTION_CONTROL_ERROR_MESSAGE = "Transaction control statements are not allowed in scoped Tools.";
4160
4166
  var NUMBERED_PLACEHOLDER_PATTERN = /\$(\d+)/;
4161
4167
  var QUESTION_PLACEHOLDER_PATTERN = /\?/;
4162
4168
  var NAMED_PLACEHOLDER_PATTERN = /(?<!:):[a-zA-Z_][a-zA-Z0-9_]*/;
4163
4169
  var PARAMETER_REQUIRED_PATTERN = /^(insert|update|delete)\b/i;
4164
4170
  var MUTATION_PATTERN = /\b(insert|update|delete)\b/i;
4171
+ function sqlStripOptions(vendor, rejectExecutableComments = false, backslashEscapes = vendor === "mysql") {
4172
+ return {
4173
+ backslashEscapes,
4174
+ hashComments: vendor === "mysql",
4175
+ mysqlDashDashComments: vendor === "mysql",
4176
+ nestedBlockComments: vendor === "postgresql",
4177
+ postgresEscapeStrings: vendor === "postgresql",
4178
+ rejectExecutableComments
4179
+ };
4180
+ }
4181
+ function isPostgresEscapeStringQuote(sqlString, quoteIndex) {
4182
+ const prefixIndex = quoteIndex - 1;
4183
+ if (prefixIndex < 0 || !/[eE]/.test(sqlString[prefixIndex])) {
4184
+ return false;
4185
+ }
4186
+ const beforePrefix = prefixIndex - 1;
4187
+ return beforePrefix < 0 || !/[A-Za-z0-9_$]/.test(sqlString[beforePrefix]);
4188
+ }
4189
+ function isWhitespaceOrControlCharacter(value) {
4190
+ if (!value) {
4191
+ return false;
4192
+ }
4193
+ const codePoint = value.charCodeAt(0);
4194
+ return /\s/u.test(value) || codePoint < 32 || codePoint === 127;
4195
+ }
4165
4196
  function stripSqlCommentsAndStrings(sqlString, options) {
4166
4197
  const { backslashEscapes } = options;
4167
4198
  let result = "";
@@ -4170,7 +4201,17 @@ function stripSqlCommentsAndStrings(sqlString, options) {
4170
4201
  while (i < len) {
4171
4202
  const ch = sqlString[i];
4172
4203
  const next = i + 1 < len ? sqlString[i + 1] : "";
4173
- if (ch === "-" && next === "-") {
4204
+ if (options.hashComments && ch === "#") {
4205
+ result += " ";
4206
+ i += 1;
4207
+ while (i < len && sqlString[i] !== "\n") {
4208
+ i += 1;
4209
+ }
4210
+ continue;
4211
+ }
4212
+ const afterDoubleDash = i + 2 < len ? sqlString[i + 2] : "";
4213
+ const doubleDashStartsComment = ch === "-" && next === "-" && (!options.mysqlDashDashComments || isWhitespaceOrControlCharacter(afterDoubleDash));
4214
+ if (doubleDashStartsComment) {
4174
4215
  result += " ";
4175
4216
  i += 2;
4176
4217
  while (i < len && sqlString[i] !== "\n") {
@@ -4179,12 +4220,23 @@ function stripSqlCommentsAndStrings(sqlString, options) {
4179
4220
  continue;
4180
4221
  }
4181
4222
  if (ch === "/" && next === "*") {
4223
+ const executableComment = sqlString[i + 2] === "!" || sqlString[i + 2]?.toLowerCase() === "m" && sqlString[i + 3] === "!";
4224
+ if (options.rejectExecutableComments && executableComment) {
4225
+ throw new Error(MANAGED_TRANSACTION_CONTROL_ERROR_MESSAGE);
4226
+ }
4182
4227
  result += " ";
4183
4228
  i += 2;
4184
- while (i < len) {
4229
+ let depth = 1;
4230
+ while (i < len && depth > 0) {
4231
+ if (options.nestedBlockComments && sqlString[i] === "/" && i + 1 < len && sqlString[i + 1] === "*") {
4232
+ depth += 1;
4233
+ i += 2;
4234
+ continue;
4235
+ }
4185
4236
  if (sqlString[i] === "*" && i + 1 < len && sqlString[i + 1] === "/") {
4237
+ depth -= 1;
4186
4238
  i += 2;
4187
- break;
4239
+ continue;
4188
4240
  }
4189
4241
  i += 1;
4190
4242
  }
@@ -4192,9 +4244,10 @@ function stripSqlCommentsAndStrings(sqlString, options) {
4192
4244
  }
4193
4245
  if (ch === "'") {
4194
4246
  result += "''";
4247
+ const singleQuoteBackslashEscapes = backslashEscapes || options.postgresEscapeStrings && isPostgresEscapeStringQuote(sqlString, i);
4195
4248
  i += 1;
4196
4249
  while (i < len) {
4197
- if (backslashEscapes && sqlString[i] === "\\" && i + 1 < len) {
4250
+ if (singleQuoteBackslashEscapes && sqlString[i] === "\\" && i + 1 < len) {
4198
4251
  i += 2;
4199
4252
  continue;
4200
4253
  }
@@ -4252,6 +4305,20 @@ function stripSqlCommentsAndStrings(sqlString, options) {
4252
4305
  }
4253
4306
  return result;
4254
4307
  }
4308
+ function strippedSqlVariants(sqlString, vendor, rejectExecutableComments = false, analyzeMysqlNoBackslashEscapes = false) {
4309
+ const variants = [
4310
+ stripSqlCommentsAndStrings(sqlString, sqlStripOptions(vendor, rejectExecutableComments))
4311
+ ];
4312
+ if (vendor === "mysql" && analyzeMysqlNoBackslashEscapes) {
4313
+ variants.push(
4314
+ stripSqlCommentsAndStrings(
4315
+ sqlString,
4316
+ sqlStripOptions(vendor, rejectExecutableComments, false)
4317
+ )
4318
+ );
4319
+ }
4320
+ return [...new Set(variants)];
4321
+ }
4255
4322
  function nextNonWhitespaceChar(sqlString, index) {
4256
4323
  for (let i = index; i < sqlString.length; i += 1) {
4257
4324
  if (!/\s/.test(sqlString[i])) {
@@ -4301,6 +4368,16 @@ function hasParameters(params) {
4301
4368
  }
4302
4369
  return Object.keys(params).length > 0;
4303
4370
  }
4371
+ function sqlStatements(sqlString, vendor, rejectExecutableComments = false, analyzeMysqlNoBackslashEscapes = false) {
4372
+ return strippedSqlVariants(
4373
+ sqlString,
4374
+ vendor,
4375
+ rejectExecutableComments,
4376
+ analyzeMysqlNoBackslashEscapes
4377
+ ).flatMap(
4378
+ (normalized) => normalized.split(";").map((statement) => statement.trim()).filter((statement) => statement.length > 0)
4379
+ );
4380
+ }
4304
4381
  function validateSqlString(sqlString, vendor) {
4305
4382
  if (typeof sqlString !== "string" || sqlString.trim().length === 0) {
4306
4383
  throw new Error("SQL query must not be empty");
@@ -4308,18 +4385,20 @@ function validateSqlString(sqlString, vendor) {
4308
4385
  if (sqlString.includes("\0")) {
4309
4386
  throw new Error("SQL query contains null bytes");
4310
4387
  }
4311
- const normalizedForSafetyCheck = stripSqlCommentsAndStrings(sqlString, {
4312
- backslashEscapes: vendor === "mysql"
4313
- });
4314
- const statements = normalizedForSafetyCheck.split(";").map((statement) => statement.trim()).filter((statement) => statement.length > 0);
4388
+ const statements = sqlStatements(sqlString, vendor);
4315
4389
  if (statements.some((statement) => DANGEROUS_SQL_STATEMENT_PATTERN.test(statement))) {
4316
4390
  throw new Error("Detected dangerous SQL operation. CREATE, DROP, TRUNCATE, and ALTER are not allowed.");
4317
4391
  }
4318
4392
  }
4393
+ function validateManagedTransactionSql(sqlString, vendor) {
4394
+ if (sqlStatements(sqlString, vendor, true, true).some(
4395
+ (statement) => TRANSACTION_CONTROL_STATEMENT_PATTERN.test(statement) || AUTOCOMMIT_STATEMENT_PATTERN.test(statement) || vendor === "mysql" && (MYSQL_IMPLICIT_COMMIT_STATEMENT_PATTERN.test(statement) || MYSQL_INDIRECT_EXECUTION_STATEMENT_PATTERN.test(statement))
4396
+ )) {
4397
+ throw new Error(MANAGED_TRANSACTION_CONTROL_ERROR_MESSAGE);
4398
+ }
4399
+ }
4319
4400
  function enforceParameterizedQueryUsage(sqlString, params, vendor) {
4320
- const normalizedForAnalysis = stripSqlCommentsAndStrings(sqlString, {
4321
- backslashEscapes: vendor === "mysql"
4322
- });
4401
+ const normalizedForAnalysis = stripSqlCommentsAndStrings(sqlString, sqlStripOptions(vendor));
4323
4402
  const normalized = normalizedForAnalysis.trim().toLowerCase();
4324
4403
  const hasPlaceholders = hasSqlPlaceholders(normalizedForAnalysis, vendor);
4325
4404
  const hasParams = hasParameters(params);
@@ -4378,19 +4457,19 @@ var ConnectionState = /* @__PURE__ */ ((ConnectionState2) => {
4378
4457
  ConnectionState2["ERROR"] = "error";
4379
4458
  return ConnectionState2;
4380
4459
  })(ConnectionState || {});
4381
- function cancelPendingReconnection(timer, logger19, vendor, message) {
4460
+ function cancelPendingReconnection(timer, logger20, vendor, message) {
4382
4461
  if (!timer) {
4383
4462
  return null;
4384
4463
  }
4385
- logger19.debug(message, { vendor });
4464
+ logger20.debug(message, { vendor });
4386
4465
  clearTimeout(timer);
4387
4466
  return null;
4388
4467
  }
4389
- async function waitForInFlightConnection(promise, logger19, vendor, phase) {
4468
+ async function waitForInFlightConnection(promise, logger20, vendor, phase) {
4390
4469
  if (!promise) {
4391
4470
  return null;
4392
4471
  }
4393
- logger19.debug(`Waiting for in-flight connection attempt to complete before ${phase}`, {
4472
+ logger20.debug(`Waiting for in-flight connection attempt to complete before ${phase}`, {
4394
4473
  vendor
4395
4474
  });
4396
4475
  try {
@@ -4407,9 +4486,9 @@ function shutdownClient(vendor, client) {
4407
4486
  client.close();
4408
4487
  }
4409
4488
  }
4410
- function scheduleReconnection(context, logger19) {
4489
+ function scheduleReconnection(context, logger20) {
4411
4490
  if (context.reconnectionConfig.maxAttempts > 0 && context.reconnectionAttempts >= context.reconnectionConfig.maxAttempts) {
4412
- logger19.error("Max reconnection attempts reached", {
4491
+ logger20.error("Max reconnection attempts reached", {
4413
4492
  vendor: context.vendor,
4414
4493
  attempts: context.reconnectionAttempts,
4415
4494
  maxAttempts: context.reconnectionConfig.maxAttempts
@@ -4423,7 +4502,7 @@ function scheduleReconnection(context, logger19) {
4423
4502
  const nextAttempt = context.reconnectionAttempts + 1;
4424
4503
  context.setReconnectionAttempts(nextAttempt);
4425
4504
  context.setState("reconnecting" /* RECONNECTING */);
4426
- logger19.info("Scheduling reconnection attempt", {
4505
+ logger20.info("Scheduling reconnection attempt", {
4427
4506
  vendor: context.vendor,
4428
4507
  attempt: nextAttempt,
4429
4508
  maxAttempts: context.reconnectionConfig.maxAttempts,
@@ -4437,7 +4516,7 @@ function scheduleReconnection(context, logger19) {
4437
4516
  const timer = setTimeout(async () => {
4438
4517
  context.setReconnectionTimer(null);
4439
4518
  try {
4440
- logger19.info("Attempting reconnection", {
4519
+ logger20.info("Attempting reconnection", {
4441
4520
  vendor: context.vendor,
4442
4521
  attempt: nextAttempt
4443
4522
  });
@@ -4447,7 +4526,7 @@ function scheduleReconnection(context, logger19) {
4447
4526
  context.setConnectPromise(promise);
4448
4527
  await promise;
4449
4528
  } catch (error) {
4450
- logger19.error("Reconnection attempt failed", {
4529
+ logger20.error("Reconnection attempt failed", {
4451
4530
  vendor: context.vendor,
4452
4531
  attempt: nextAttempt,
4453
4532
  error: error instanceof Error ? error.message : String(error)
@@ -4458,17 +4537,17 @@ function scheduleReconnection(context, logger19) {
4458
4537
  }
4459
4538
 
4460
4539
  // src/data/relational/connection/connection-cleanup.ts
4461
- async function cleanupConnectionResources(vendor, client, clearConnection, logger19) {
4540
+ async function cleanupConnectionResources(vendor, client, clearConnection, logger20) {
4462
4541
  if (!client) {
4463
4542
  return;
4464
4543
  }
4465
- logger19.debug("Cleaning up cancelled connection", {
4544
+ logger20.debug("Cleaning up cancelled connection", {
4466
4545
  vendor
4467
4546
  });
4468
4547
  try {
4469
4548
  await shutdownClient(vendor, client);
4470
4549
  } catch (error) {
4471
- logger19.debug("Error during cancelled connection cleanup", {
4550
+ logger20.debug("Error during cancelled connection cleanup", {
4472
4551
  vendor,
4473
4552
  error: error instanceof Error ? error.message : String(error)
4474
4553
  });
@@ -4476,10 +4555,10 @@ async function cleanupConnectionResources(vendor, client, clearConnection, logge
4476
4555
  clearConnection();
4477
4556
  }
4478
4557
  }
4479
- async function closeManagedConnection(runtime, logger19) {
4558
+ async function closeManagedConnection(runtime, logger20) {
4480
4559
  const client = runtime.getClient();
4481
4560
  if (client) {
4482
- logger19.info("Closing database connection", {
4561
+ logger20.info("Closing database connection", {
4483
4562
  vendor: runtime.vendor,
4484
4563
  state: runtime.getState()
4485
4564
  });
@@ -4487,12 +4566,12 @@ async function closeManagedConnection(runtime, logger19) {
4487
4566
  await shutdownClient(runtime.vendor, client);
4488
4567
  runtime.setState("disconnected" /* DISCONNECTED */);
4489
4568
  runtime.emitDisconnected();
4490
- logger19.debug("Database connection closed successfully", {
4569
+ logger20.debug("Database connection closed successfully", {
4491
4570
  vendor: runtime.vendor,
4492
4571
  state: runtime.getState()
4493
4572
  });
4494
4573
  } catch (error) {
4495
- logger19.error("Error closing database connection", {
4574
+ logger20.error("Error closing database connection", {
4496
4575
  vendor: runtime.vendor,
4497
4576
  error: error instanceof Error ? error.message : String(error),
4498
4577
  state: runtime.getState()
@@ -4665,11 +4744,11 @@ async function initializeSQLiteConnection(connection) {
4665
4744
  }
4666
4745
 
4667
4746
  // src/data/relational/connection/connection-initialization.ts
4668
- async function initializeManagedConnection(runtime, logger19) {
4747
+ async function initializeManagedConnection(runtime, logger20) {
4669
4748
  const startTime = Date.now();
4670
4749
  const currentGeneration = runtime.getConnectionGeneration();
4671
4750
  if (runtime.getState() === "connected" /* CONNECTED */ && runtime.getClient()) {
4672
- logger19.warn("Re-initializing an already connected manager; emitting disconnected before cleanup", {
4751
+ logger20.warn("Re-initializing an already connected manager; emitting disconnected before cleanup", {
4673
4752
  vendor: runtime.vendor
4674
4753
  });
4675
4754
  runtime.setState("disconnected" /* DISCONNECTED */);
@@ -4681,11 +4760,11 @@ async function initializeManagedConnection(runtime, logger19) {
4681
4760
  runtime.setClient(null);
4682
4761
  runtime.setDb(null);
4683
4762
  },
4684
- logger19
4763
+ logger20
4685
4764
  );
4686
4765
  }
4687
4766
  runtime.setState("connecting" /* CONNECTING */);
4688
- logger19.info("Initializing database connection", {
4767
+ logger20.info("Initializing database connection", {
4689
4768
  vendor: runtime.vendor,
4690
4769
  connectionType: typeof runtime.config.connection,
4691
4770
  state: runtime.getState()
@@ -4695,7 +4774,7 @@ async function initializeManagedConnection(runtime, logger19) {
4695
4774
  runtime.setClient(initialized.client);
4696
4775
  runtime.setDb(initialized.db);
4697
4776
  if (currentGeneration !== runtime.getConnectionGeneration()) {
4698
- logger19.debug("Connection cancelled during initialization", {
4777
+ logger20.debug("Connection cancelled during initialization", {
4699
4778
  vendor: runtime.vendor,
4700
4779
  currentGeneration,
4701
4780
  newGeneration: runtime.getConnectionGeneration()
@@ -4707,18 +4786,18 @@ async function initializeManagedConnection(runtime, logger19) {
4707
4786
  runtime.setClient(null);
4708
4787
  runtime.setDb(null);
4709
4788
  },
4710
- logger19
4789
+ logger20
4711
4790
  );
4712
4791
  runtime.setState("disconnected" /* DISCONNECTED */);
4713
4792
  throw new Error("Connection cancelled during initialization");
4714
4793
  }
4715
- logger19.debug("Validating connection health", { vendor: runtime.vendor });
4794
+ logger20.debug("Validating connection health", { vendor: runtime.vendor });
4716
4795
  const healthy = await runtime.isHealthy();
4717
4796
  if (!healthy) {
4718
4797
  throw new Error(`Failed to establish healthy connection to ${runtime.vendor} database`);
4719
4798
  }
4720
4799
  if (currentGeneration !== runtime.getConnectionGeneration()) {
4721
- logger19.debug("Connection cancelled during health check", {
4800
+ logger20.debug("Connection cancelled during health check", {
4722
4801
  vendor: runtime.vendor,
4723
4802
  currentGeneration,
4724
4803
  newGeneration: runtime.getConnectionGeneration()
@@ -4730,7 +4809,7 @@ async function initializeManagedConnection(runtime, logger19) {
4730
4809
  runtime.setClient(null);
4731
4810
  runtime.setDb(null);
4732
4811
  },
4733
- logger19
4812
+ logger20
4734
4813
  );
4735
4814
  runtime.setState("disconnected" /* DISCONNECTED */);
4736
4815
  throw new Error("Connection cancelled during health check");
@@ -4738,7 +4817,7 @@ async function initializeManagedConnection(runtime, logger19) {
4738
4817
  runtime.setState("connected" /* CONNECTED */);
4739
4818
  runtime.emitConnected();
4740
4819
  runtime.resetReconnectionAttempts();
4741
- logger19.info("Database connection initialized successfully", {
4820
+ logger20.info("Database connection initialized successfully", {
4742
4821
  vendor: runtime.vendor,
4743
4822
  duration: Date.now() - startTime,
4744
4823
  state: runtime.getState()
@@ -4759,7 +4838,7 @@ async function initializeManagedConnection(runtime, logger19) {
4759
4838
  if (runtime.errorListenerCount() > 0) {
4760
4839
  runtime.emitError(normalizedError);
4761
4840
  }
4762
- logger19.error(errorMessage, {
4841
+ logger20.error(errorMessage, {
4763
4842
  vendor: runtime.vendor,
4764
4843
  error: normalizedError.message,
4765
4844
  duration: Date.now() - startTime,
@@ -4772,13 +4851,13 @@ async function initializeManagedConnection(runtime, logger19) {
4772
4851
  runtime.setClient(null);
4773
4852
  runtime.setDb(null);
4774
4853
  },
4775
- logger19
4854
+ logger20
4776
4855
  );
4777
4856
  if (runtime.getReconnectionConfig().enabled && currentGeneration === runtime.getConnectionGeneration()) {
4778
4857
  runtime.scheduleReconnection();
4779
4858
  }
4780
4859
  } else {
4781
- logger19.debug("Connection initialization cancelled", {
4860
+ logger20.debug("Connection initialization cancelled", {
4782
4861
  vendor: runtime.vendor,
4783
4862
  duration: Date.now() - startTime,
4784
4863
  state: runtime.getState()
@@ -4796,17 +4875,17 @@ function isSqliteNonQueryError(error) {
4796
4875
  const hasNonQueryMessage = error.message.includes("does not return data");
4797
4876
  return isSqliteOrTypeError && hasNonQueryMessage;
4798
4877
  }
4799
- async function checkConnectionHealth(args, logger19) {
4878
+ async function checkConnectionHealth(args, logger20) {
4800
4879
  if (!args.db || !args.client) {
4801
- logger19.debug("Health check failed: connection not initialized", { vendor: args.vendor });
4880
+ logger20.debug("Health check failed: connection not initialized", { vendor: args.vendor });
4802
4881
  return false;
4803
4882
  }
4804
4883
  try {
4805
4884
  await args.execute(sql`SELECT 1`);
4806
- logger19.debug("Health check passed", { vendor: args.vendor });
4885
+ logger20.debug("Health check passed", { vendor: args.vendor });
4807
4886
  return true;
4808
4887
  } catch (error) {
4809
- logger19.debug("Health check failed", {
4888
+ logger20.debug("Health check failed", {
4810
4889
  vendor: args.vendor,
4811
4890
  error: error instanceof Error ? error.message : String(error)
4812
4891
  });
@@ -4849,8 +4928,8 @@ function getConnectionPoolMetrics(vendor, client) {
4849
4928
  }
4850
4929
 
4851
4930
  // src/data/relational/connection/query-execution.ts
4852
- async function executeQuery(context, query, logger19) {
4853
- logger19.debug("Executing SQL query", {
4931
+ async function executeQuery(context, query, logger20) {
4932
+ logger20.debug("Executing SQL query", {
4854
4933
  vendor: context.vendor
4855
4934
  });
4856
4935
  if (context.vendor === "sqlite") {
@@ -4901,7 +4980,7 @@ async function executeInDedicatedConnection(context, callback) {
4901
4980
  try {
4902
4981
  const { drizzle } = await import('drizzle-orm/node-postgres');
4903
4982
  const sessionDb = drizzle({ client: poolClient });
4904
- return await callback((query) => sessionDb.execute(query));
4983
+ return await callback(async (query) => sessionDb.execute(query));
4905
4984
  } finally {
4906
4985
  poolClient.release();
4907
4986
  }
@@ -5226,7 +5305,7 @@ function buildParameterizedQuery(sqlString, params) {
5226
5305
  return sqlChunks.length > 0 ? sql.join(sqlChunks, sql.raw("")) : sql.raw(sqlString);
5227
5306
  }
5228
5307
  }
5229
- async function executeQuery2(manager, input, context) {
5308
+ async function executeQuery2(executor, input, context) {
5230
5309
  const startTime = Date.now();
5231
5310
  logger8.debug("Executing query", {
5232
5311
  vendor: input.vendor,
@@ -5235,10 +5314,13 @@ async function executeQuery2(manager, input, context) {
5235
5314
  });
5236
5315
  try {
5237
5316
  validateSqlString(input.sql, input.vendor);
5317
+ if (context?.transaction) {
5318
+ validateManagedTransactionSql(input.sql, input.vendor);
5319
+ }
5238
5320
  enforceParameterizedQueryUsage(input.sql, input.params, input.vendor);
5239
5321
  const parameterizedQuery = buildParameterizedQuery(input.sql, input.params);
5240
- const executor = context?.transaction ?? manager;
5241
- const result = await executor.execute(parameterizedQuery);
5322
+ const session = context?.transaction ?? executor;
5323
+ const result = await session.execute(parameterizedQuery);
5242
5324
  const executionTime = Date.now() - startTime;
5243
5325
  const rows = Array.isArray(result) ? result : result.rows || [];
5244
5326
  const rowCount = result.rowCount || result.affectedRows || rows.length;
@@ -5261,7 +5343,7 @@ async function executeQuery2(manager, input, context) {
5261
5343
  });
5262
5344
  if (error instanceof Error) {
5263
5345
  const message = error.message;
5264
- if (message.includes("Missing parameter") || message.includes("Missing parameters") || message.includes("Parameters provided but no placeholders") || message.includes("Mixed parameter styles") || message.includes("SQL query must not be empty") || message.includes("SQL query contains null bytes") || message.includes("Detected dangerous SQL operation") || message.includes("Parameters are required for INSERT/UPDATE/DELETE queries")) {
5346
+ if (message.includes("Missing parameter") || message.includes("Missing parameters") || message.includes("Parameters provided but no placeholders") || message.includes("Mixed parameter styles") || message.includes("SQL query must not be empty") || message.includes("SQL query contains null bytes") || message.includes("Detected dangerous SQL operation") || message === MANAGED_TRANSACTION_CONTROL_ERROR_MESSAGE || message.includes("Parameters are required for INSERT/UPDATE/DELETE queries")) {
5265
5347
  throw error;
5266
5348
  }
5267
5349
  }
@@ -6118,6 +6200,8 @@ var ManagedTransaction = class {
6118
6200
  executeQuery;
6119
6201
  completed = false;
6120
6202
  cancelledReason = null;
6203
+ inFlight = /* @__PURE__ */ new Set();
6204
+ inFlightFailure = null;
6121
6205
  savepointCounter = 0;
6122
6206
  sqliteReadUncommittedOriginal = null;
6123
6207
  shouldRestoreSqliteReadUncommitted = false;
@@ -6130,14 +6214,36 @@ var ManagedTransaction = class {
6130
6214
  isActive() {
6131
6215
  return !this.completed;
6132
6216
  }
6133
- async execute(query) {
6217
+ execute(query) {
6218
+ try {
6219
+ return this.executeScopedQuery(query);
6220
+ } catch (error) {
6221
+ return Promise.reject(error instanceof Error ? error : new Error(String(error)));
6222
+ }
6223
+ }
6224
+ async settle() {
6225
+ while (this.inFlight.size > 0) {
6226
+ await Promise.allSettled([...this.inFlight]);
6227
+ }
6228
+ return this.inFlightFailure;
6229
+ }
6230
+ executeScopedQuery(query) {
6134
6231
  if (this.cancelledReason) {
6135
6232
  throw new Error(this.cancelledReason);
6136
6233
  }
6137
6234
  if (!this.isActive()) {
6138
6235
  throw new Error("Transaction is no longer active");
6139
6236
  }
6140
- return this.executeQuery(query);
6237
+ const execution = this.executeQuery(query);
6238
+ this.inFlight.add(execution);
6239
+ void execution.then(
6240
+ () => this.inFlight.delete(execution),
6241
+ (reason) => {
6242
+ this.inFlightFailure ??= { reason };
6243
+ this.inFlight.delete(execution);
6244
+ }
6245
+ );
6246
+ return execution;
6141
6247
  }
6142
6248
  async begin() {
6143
6249
  if (this.vendor === "mysql") {
@@ -6174,7 +6280,7 @@ var ManagedTransaction = class {
6174
6280
  throw new Error("Transaction is no longer active");
6175
6281
  }
6176
6282
  const savepointName = this.normalizeSavepointName(name ?? `sp_${++this.savepointCounter}`);
6177
- await this.executeQuery(sql.raw(`SAVEPOINT ${savepointName}`));
6283
+ await this.executeScopedQuery(sql.raw(`SAVEPOINT ${savepointName}`));
6178
6284
  return savepointName;
6179
6285
  }
6180
6286
  async rollbackToSavepoint(name) {
@@ -6182,14 +6288,14 @@ var ManagedTransaction = class {
6182
6288
  throw new Error("Transaction is no longer active");
6183
6289
  }
6184
6290
  const savepointName = this.normalizeSavepointName(name);
6185
- await this.executeQuery(sql.raw(`ROLLBACK TO SAVEPOINT ${savepointName}`));
6291
+ await this.executeScopedQuery(sql.raw(`ROLLBACK TO SAVEPOINT ${savepointName}`));
6186
6292
  }
6187
6293
  async releaseSavepoint(name) {
6188
6294
  if (!this.isActive()) {
6189
6295
  throw new Error("Transaction is no longer active");
6190
6296
  }
6191
6297
  const savepointName = this.normalizeSavepointName(name);
6192
- await this.executeQuery(sql.raw(`RELEASE SAVEPOINT ${savepointName}`));
6298
+ await this.executeScopedQuery(sql.raw(`RELEASE SAVEPOINT ${savepointName}`));
6193
6299
  }
6194
6300
  async withSavepoint(operation, name) {
6195
6301
  const savepointName = await this.createSavepoint(name);
@@ -6288,17 +6394,24 @@ async function withTransaction(manager, operation, options) {
6288
6394
  executeQuery: executeQuery3,
6289
6395
  options: resolvedOptions
6290
6396
  });
6291
- await transaction.begin();
6397
+ let timedOut = false;
6292
6398
  try {
6399
+ await transaction.begin();
6293
6400
  const result = await withTransactionTimeout(
6294
6401
  operation(transaction),
6295
6402
  resolvedOptions.timeoutMs,
6296
6403
  () => {
6297
6404
  if (resolvedOptions.timeoutMs) {
6405
+ timedOut = true;
6298
6406
  transaction.cancel(`Transaction timed out after ${resolvedOptions.timeoutMs}ms`);
6299
6407
  }
6300
6408
  }
6301
6409
  );
6410
+ transaction.cancel("Transaction is no longer active");
6411
+ const inFlightFailure = await transaction.settle();
6412
+ if (inFlightFailure) {
6413
+ throw inFlightFailure.reason;
6414
+ }
6302
6415
  if (transaction.isActive()) {
6303
6416
  await transaction.commit();
6304
6417
  }
@@ -6309,6 +6422,10 @@ async function withTransaction(manager, operation, options) {
6309
6422
  });
6310
6423
  return result;
6311
6424
  } catch (error) {
6425
+ if (!timedOut) {
6426
+ transaction.cancel(error instanceof Error ? error.message : "Transaction failed");
6427
+ }
6428
+ await transaction.settle();
6312
6429
  if (transaction.isActive()) {
6313
6430
  try {
6314
6431
  await transaction.rollback();
@@ -6813,16 +6930,33 @@ async function inspectSQLite(runQueryRows) {
6813
6930
  // src/data/relational/schema/schema-inspector.ts
6814
6931
  var logger11 = createLogger("agentforge:tools:data:relational:schema-inspector");
6815
6932
  var DEFAULT_CACHE_TTL_MS = 6e4;
6816
- var schemaCache = /* @__PURE__ */ new Map();
6817
- var SchemaInspector = class _SchemaInspector {
6818
- constructor(manager, vendor, config) {
6819
- this.manager = manager;
6933
+ var SchemaCache = class {
6934
+ entries = /* @__PURE__ */ new Map();
6935
+ get(cacheKey) {
6936
+ return this.entries.get(cacheKey);
6937
+ }
6938
+ set(cacheKey, entry) {
6939
+ this.entries.set(cacheKey, entry);
6940
+ }
6941
+ delete(cacheKey) {
6942
+ this.entries.delete(cacheKey);
6943
+ }
6944
+ clear() {
6945
+ this.entries.clear();
6946
+ }
6947
+ };
6948
+ var schemaCache = new SchemaCache();
6949
+ var SchemaInspector = class {
6950
+ constructor(executor, vendor, config) {
6951
+ this.executor = executor;
6820
6952
  this.vendor = vendor;
6821
6953
  this.cacheTtlMs = config?.cacheTtlMs ?? DEFAULT_CACHE_TTL_MS;
6822
6954
  this.cacheKey = config?.cacheKey;
6955
+ this.cache = config?.cache ?? schemaCache;
6823
6956
  }
6824
6957
  cacheTtlMs;
6825
6958
  cacheKey;
6959
+ cache;
6826
6960
  static clearCache(cacheKey) {
6827
6961
  if (cacheKey) {
6828
6962
  schemaCache.delete(cacheKey);
@@ -6831,21 +6965,25 @@ var SchemaInspector = class _SchemaInspector {
6831
6965
  schemaCache.clear();
6832
6966
  }
6833
6967
  invalidateCache() {
6834
- _SchemaInspector.clearCache(this.cacheKey);
6968
+ if (this.cacheKey) {
6969
+ this.cache.delete(this.cacheKey);
6970
+ return;
6971
+ }
6972
+ this.cache.clear();
6835
6973
  }
6836
6974
  async inspect(options) {
6837
6975
  const tableFilters = validateTableFilters(options?.tables);
6838
6976
  const bypassCache = options?.bypassCache ?? false;
6839
- if (!bypassCache && this.cacheKey) {
6840
- const cached = schemaCache.get(this.cacheKey);
6977
+ if (!bypassCache && this.cacheKey && this.cacheTtlMs > 0) {
6978
+ const cached = this.cache.get(this.cacheKey);
6841
6979
  if (cached && cached.expiresAt > Date.now()) {
6842
6980
  logger11.debug("Schema cache hit", { vendor: this.vendor });
6843
6981
  return filterSchemaTables(cloneSchema(cached.schema), tableFilters);
6844
6982
  }
6845
6983
  }
6846
6984
  const schema = await this.inspectFromDatabase();
6847
- if (this.cacheKey && this.cacheTtlMs > 0) {
6848
- schemaCache.set(this.cacheKey, {
6985
+ if (!bypassCache && this.cacheKey && this.cacheTtlMs > 0) {
6986
+ this.cache.set(this.cacheKey, {
6849
6987
  schema: cloneSchema(schema),
6850
6988
  expiresAt: Date.now() + this.cacheTtlMs
6851
6989
  });
@@ -6877,7 +7015,7 @@ var SchemaInspector = class _SchemaInspector {
6877
7015
  }
6878
7016
  }
6879
7017
  async runQueryRows(query) {
6880
- const result = await executeQuery2(this.manager, {
7018
+ const result = await executeQuery2(this.executor, {
6881
7019
  sql: query,
6882
7020
  vendor: this.vendor
6883
7021
  });
@@ -7336,95 +7474,29 @@ function sortedReplacer(_key, value) {
7336
7474
  }
7337
7475
  return value;
7338
7476
  }
7339
- var relationalQuerySchema = z.object({
7340
- sql: z.string().describe("SQL query string to execute"),
7341
- params: z.union([
7342
- z.array(z.unknown()).describe("Positional parameters (e.g., [value1, value2])"),
7343
- z.record(z.string(), z.unknown()).describe('Named parameters (e.g., { name: "John", age: 30 })')
7344
- ]).optional().describe("Query parameters for parameter binding (prevents SQL injection)"),
7345
- vendor: z.enum(["postgresql", "mysql", "sqlite"]).describe("Database vendor"),
7346
- connectionString: z.string().min(1, "Database connection string is required").describe("Database connection string used to create a new connection")
7347
- });
7348
- var relationalQuery = toolBuilder().name("relational-query").displayName("Relational Query").description("Execute raw SQL queries against relational databases (PostgreSQL, MySQL, SQLite) with parameter binding to prevent SQL injection").category(ToolCategory.DATABASE).tags(["database", "sql", "query", "postgresql", "mysql", "sqlite"]).schema(relationalQuerySchema).example({
7349
- description: "SELECT query with positional parameters",
7350
- input: {
7351
- sql: "SELECT * FROM users WHERE id = $1",
7352
- params: [42],
7353
- vendor: "postgresql",
7354
- connectionString: "postgresql://user:pass@localhost:5432/mydb"
7355
- }
7356
- }).example({
7357
- description: "INSERT query with named parameters",
7358
- input: {
7359
- sql: "INSERT INTO users (name, email) VALUES (:name, :email)",
7360
- params: { name: "John Doe", email: "john@example.com" },
7361
- vendor: "postgresql",
7362
- connectionString: "postgresql://user:pass@localhost:5432/mydb"
7363
- }
7364
- }).example({
7365
- description: "UPDATE query with positional parameters",
7366
- input: {
7367
- sql: "UPDATE users SET status = ? WHERE id = ?",
7368
- params: ["active", 42],
7369
- vendor: "mysql",
7370
- connectionString: "mysql://user:pass@localhost:3306/mydb"
7371
- }
7372
- }).usageNotes("Always use parameter binding (params) instead of string concatenation to prevent SQL injection. Supports positional ($1, ?) and named (:name) parameters.").limitation("Requires database-specific driver as peer dependency (pg, mysql2, or better-sqlite3)").limitation("Connection string must be valid for the specified vendor").limitation("Large result sets may impact performance").limitation("Placeholder characters ($n, ?, :name) inside SQL string literals are not supported. Use parameter binding for all dynamic values instead of embedding them in string literals").implement(async (input) => {
7373
- const manager = new ConnectionManager({
7374
- vendor: input.vendor,
7375
- connection: input.connectionString || ""
7376
- });
7377
- try {
7378
- await manager.initialize();
7379
- const result = await executeQuery2(manager, {
7380
- sql: input.sql,
7381
- params: input.params,
7382
- vendor: input.vendor
7383
- });
7384
- return {
7385
- success: true,
7386
- rows: result.rows,
7387
- rowCount: result.rowCount,
7388
- executionTime: result.executionTime
7389
- };
7390
- } catch (error) {
7391
- return {
7392
- success: false,
7393
- error: error instanceof Error ? error.message : "Unknown error occurred",
7394
- rows: [],
7395
- rowCount: 0
7396
- };
7397
- } finally {
7398
- await manager.close();
7399
- }
7400
- }).build();
7401
- var whereOperatorSchema = z.enum([
7477
+
7478
+ // src/data/relational/connection-failure-messages.ts
7479
+ var QUERY_CONNECTION_FAILURE = "Failed to connect to the configured database.";
7480
+ var SELECT_CONNECTION_FAILURE = "Failed to execute SELECT query. Please verify the configured database connection.";
7481
+ var INSERT_CONNECTION_FAILURE = "Failed to execute INSERT query. Please verify the configured database connection.";
7482
+ var UPDATE_CONNECTION_FAILURE = "Failed to execute UPDATE query. Please verify the configured database connection.";
7483
+ var DELETE_CONNECTION_FAILURE = "Failed to execute DELETE query. Please verify the configured database connection.";
7484
+ var deleteWhereOperatorSchema = z.enum([
7402
7485
  "eq",
7403
- // equals
7404
7486
  "ne",
7405
- // not equals
7406
7487
  "gt",
7407
- // greater than
7408
7488
  "lt",
7409
- // less than
7410
7489
  "gte",
7411
- // greater than or equal
7412
7490
  "lte",
7413
- // less than or equal
7414
7491
  "like",
7415
- // LIKE pattern matching
7416
7492
  "in",
7417
- // IN array
7418
7493
  "notIn",
7419
- // NOT IN array
7420
7494
  "isNull",
7421
- // IS NULL
7422
7495
  "isNotNull"
7423
- // IS NOT NULL
7424
7496
  ]);
7425
- var whereConditionSchema = z.object({
7497
+ var deleteWhereConditionSchema = z.object({
7426
7498
  column: z.string().min(1, "Column name must not be empty").describe("Column name to filter on"),
7427
- operator: whereOperatorSchema.describe("Comparison operator"),
7499
+ operator: deleteWhereOperatorSchema.describe("Comparison operator"),
7428
7500
  value: z.union([
7429
7501
  z.string(),
7430
7502
  z.number(),
@@ -7432,10 +7504,10 @@ var whereConditionSchema = z.object({
7432
7504
  z.array(z.union([z.string(), z.number()])),
7433
7505
  z.null()
7434
7506
  ]).optional().describe("Value to compare against (not required for isNull/isNotNull)")
7435
- }).superRefine((val, ctx) => {
7436
- const op = val.operator;
7507
+ }).superRefine((value, ctx) => {
7508
+ const op = value.operator;
7437
7509
  if (op === "isNull" || op === "isNotNull") {
7438
- if (val.value !== void 0) {
7510
+ if (value.value !== void 0) {
7439
7511
  ctx.addIssue({
7440
7512
  code: z.ZodIssueCode.custom,
7441
7513
  path: ["value"],
@@ -7444,7 +7516,7 @@ var whereConditionSchema = z.object({
7444
7516
  }
7445
7517
  return;
7446
7518
  }
7447
- if (val.value === void 0) {
7519
+ if (value.value === void 0) {
7448
7520
  ctx.addIssue({
7449
7521
  code: z.ZodIssueCode.custom,
7450
7522
  path: ["value"],
@@ -7453,13 +7525,13 @@ var whereConditionSchema = z.object({
7453
7525
  return;
7454
7526
  }
7455
7527
  if (op === "in" || op === "notIn") {
7456
- if (!Array.isArray(val.value)) {
7528
+ if (!Array.isArray(value.value)) {
7457
7529
  ctx.addIssue({
7458
7530
  code: z.ZodIssueCode.custom,
7459
7531
  path: ["value"],
7460
7532
  message: `${op === "in" ? "IN" : "NOT IN"} operator requires an array value`
7461
7533
  });
7462
- } else if (val.value.length === 0) {
7534
+ } else if (value.value.length === 0) {
7463
7535
  ctx.addIssue({
7464
7536
  code: z.ZodIssueCode.custom,
7465
7537
  path: ["value"],
@@ -7468,7 +7540,7 @@ var whereConditionSchema = z.object({
7468
7540
  }
7469
7541
  return;
7470
7542
  }
7471
- if (val.value === null) {
7543
+ if (value.value === null) {
7472
7544
  ctx.addIssue({
7473
7545
  code: z.ZodIssueCode.custom,
7474
7546
  path: ["value"],
@@ -7476,7 +7548,7 @@ var whereConditionSchema = z.object({
7476
7548
  });
7477
7549
  return;
7478
7550
  }
7479
- if (op === "like" && typeof val.value !== "string") {
7551
+ if (op === "like" && typeof value.value !== "string") {
7480
7552
  ctx.addIssue({
7481
7553
  code: z.ZodIssueCode.custom,
7482
7554
  path: ["value"],
@@ -7484,326 +7556,124 @@ var whereConditionSchema = z.object({
7484
7556
  });
7485
7557
  return;
7486
7558
  }
7487
- if ((op === "gt" || op === "lt" || op === "gte" || op === "lte") && typeof val.value !== "string" && typeof val.value !== "number") {
7559
+ if ((op === "gt" || op === "lt" || op === "gte" || op === "lte") && typeof value.value !== "string" && typeof value.value !== "number") {
7488
7560
  ctx.addIssue({
7489
7561
  code: z.ZodIssueCode.custom,
7490
7562
  path: ["value"],
7491
7563
  message: `${op.toUpperCase()} operator requires a string or number value`
7492
7564
  });
7493
- return;
7494
7565
  }
7495
- if ((op === "eq" || op === "ne") && Array.isArray(val.value)) {
7566
+ });
7567
+ var deleteSoftDeleteSchema = z.object({
7568
+ column: z.string().min(1, "Soft delete column must not be empty").default("deleted_at").describe("Column to write deletion timestamp/value into"),
7569
+ value: z.union([z.string(), z.number()]).optional().describe("Optional explicit value for the soft-delete column (defaults to ISO timestamp)")
7570
+ }).describe("Enable soft delete by updating a column instead of physically deleting rows");
7571
+ var deleteBatchOperationSchema = z.object({
7572
+ where: z.array(deleteWhereConditionSchema).optional().describe("WHERE conditions (combined with AND)"),
7573
+ allowFullTableDelete: z.boolean().default(false).describe("Explicitly allow DELETE without WHERE conditions for this operation"),
7574
+ cascade: z.boolean().default(false).describe("Enable cascade-aware error messaging for this operation"),
7575
+ softDelete: deleteSoftDeleteSchema.optional().describe("Use soft-delete semantics instead of hard DELETE for this operation")
7576
+ }).superRefine((value, ctx) => {
7577
+ const hasWhere = (value.where?.length ?? 0) > 0;
7578
+ if (!value.allowFullTableDelete && !hasWhere) {
7496
7579
  ctx.addIssue({
7497
7580
  code: z.ZodIssueCode.custom,
7498
- path: ["value"],
7499
- message: `${op.toUpperCase()} operator requires a scalar value`
7581
+ path: ["where"],
7582
+ message: "WHERE conditions are required unless allowFullTableDelete is true"
7500
7583
  });
7501
7584
  }
7502
7585
  });
7503
- var orderDirectionSchema = z.enum(["asc", "desc"]);
7504
- var orderBySchema = z.object({
7505
- column: z.string().min(1, "Column name must not be empty").describe("Column name to order by"),
7506
- direction: orderDirectionSchema.describe("Sort direction (ascending or descending)")
7507
- });
7508
- var streamingOptionsSchema = z.object({
7509
- enabled: z.boolean().default(true).describe("Enable chunked streaming mode for large result sets"),
7510
- chunkSize: z.number().int().min(1).max(5e3).optional().describe("Rows fetched per chunk (default: 100)"),
7511
- maxRows: z.number().int().positive().optional().describe("Optional cap on streamed rows"),
7512
- sampleSize: z.number().int().min(0).max(5e3).optional().describe("Number of rows to include in the response payload"),
7513
- benchmark: z.boolean().optional().default(false).describe("Run memory benchmark comparing regular vs streaming execution (adds two extra query executions; use side-effect-free SELECT statements)")
7586
+ var deleteBatchOptionsSchema = z.object({
7587
+ enabled: z.boolean().optional().default(true).describe("Enable batched processing for operations[] payloads"),
7588
+ batchSize: z.number().int().positive().max(5e3).optional().default(100).describe("Operations per batch chunk"),
7589
+ continueOnError: z.boolean().optional().default(true).describe("Continue processing remaining chunks when one chunk fails"),
7590
+ maxRetries: z.number().int().min(0).max(5).optional().default(0).describe("Retry attempts per failed chunk"),
7591
+ retryDelayMs: z.number().int().min(0).max(6e4).optional().default(0).describe("Delay between retry attempts in milliseconds"),
7592
+ benchmark: z.boolean().optional().default(false).describe("Run synthetic benchmark metadata comparing individual vs batched execution callbacks")
7514
7593
  });
7515
- var relationalSelectSchema = z.object({
7594
+ var relationalDeleteSchema = z.object({
7516
7595
  table: z.string().min(1, "Table name is required").regex(
7517
7596
  VALID_QUALIFIED_IDENTIFIER_PATTERN,
7518
7597
  "Table name contains invalid characters. Only alphanumeric characters, underscores, and dots (for schema qualification) are allowed."
7519
- ).describe("Table name to select from (schema-qualified names supported, e.g. public.users)"),
7520
- columns: z.array(z.string().min(1, "Column name must not be empty")).min(1, "Columns array must not be empty").optional().describe("Columns to select (omit for SELECT *)"),
7521
- where: z.array(whereConditionSchema).optional().describe("WHERE conditions (combined with AND)"),
7522
- orderBy: z.array(orderBySchema).optional().describe("ORDER BY clauses"),
7523
- limit: z.number().int().positive().optional().describe("Maximum number of rows to return"),
7524
- offset: z.number().int().nonnegative().optional().describe("Number of rows to skip"),
7525
- streaming: streamingOptionsSchema.optional().describe("Optional streaming mode configuration"),
7598
+ ).describe("Table name to delete from (schema-qualified names supported, e.g. public.users)"),
7599
+ where: z.array(deleteWhereConditionSchema).optional().describe("WHERE conditions (combined with AND)"),
7600
+ allowFullTableDelete: z.boolean().default(false).describe("Explicitly allow DELETE without WHERE conditions"),
7601
+ cascade: z.boolean().default(false).describe("Enable cascade-aware error messaging for foreign key constraints"),
7602
+ softDelete: deleteSoftDeleteSchema.optional().describe("Use soft-delete semantics instead of hard DELETE"),
7603
+ operations: z.array(deleteBatchOperationSchema).min(1, "operations must contain at least one batch item").optional().describe("Batch delete operation list"),
7604
+ batch: deleteBatchOptionsSchema.optional().describe("Optional batch execution controls for operations payloads"),
7526
7605
  vendor: z.enum(["postgresql", "mysql", "sqlite"]).describe("Database vendor"),
7527
7606
  connectionString: z.string().min(1, "Database connection string is required").describe("Database connection string")
7607
+ }).superRefine((value, ctx) => {
7608
+ const hasOperations = (value.operations?.length ?? 0) > 0;
7609
+ if (hasOperations && (value.where?.length ?? 0) > 0) {
7610
+ ctx.addIssue({
7611
+ code: z.ZodIssueCode.custom,
7612
+ path: ["where"],
7613
+ message: "where cannot be provided when operations[] is used"
7614
+ });
7615
+ }
7616
+ if (hasOperations && value.allowFullTableDelete) {
7617
+ ctx.addIssue({
7618
+ code: z.ZodIssueCode.custom,
7619
+ path: ["allowFullTableDelete"],
7620
+ message: "allowFullTableDelete cannot be provided when operations[] is used"
7621
+ });
7622
+ }
7623
+ if (hasOperations && value.cascade) {
7624
+ ctx.addIssue({
7625
+ code: z.ZodIssueCode.custom,
7626
+ path: ["cascade"],
7627
+ message: "cascade cannot be provided when operations[] is used"
7628
+ });
7629
+ }
7630
+ if (hasOperations && value.softDelete) {
7631
+ ctx.addIssue({
7632
+ code: z.ZodIssueCode.custom,
7633
+ path: ["softDelete"],
7634
+ message: "softDelete cannot be provided when operations[] is used"
7635
+ });
7636
+ }
7637
+ if (hasOperations) {
7638
+ return;
7639
+ }
7640
+ const hasWhere = (value.where?.length ?? 0) > 0;
7641
+ if (!value.allowFullTableDelete && !hasWhere) {
7642
+ ctx.addIssue({
7643
+ code: z.ZodIssueCode.custom,
7644
+ path: ["where"],
7645
+ message: "WHERE conditions are required unless allowFullTableDelete is true"
7646
+ });
7647
+ }
7528
7648
  });
7529
7649
 
7530
- // src/data/relational/tools/relational-select/query-builder.ts
7531
- function buildSelectQuery2(input) {
7532
- const queryInput = {
7533
- table: input.table,
7534
- columns: input.columns,
7535
- where: input.where,
7536
- orderBy: input.orderBy,
7537
- limit: input.limit,
7538
- offset: input.offset,
7539
- vendor: input.vendor
7540
- };
7541
- return buildSelectQuery(queryInput);
7542
- }
7543
-
7544
- // src/data/relational/tools/relational-select/error-utils.ts
7545
- var SAFE_VALIDATION_ERROR_PATTERNS = [
7650
+ // src/data/relational/tools/relational-delete/error-utils.ts
7651
+ var SAFE_DELETE_VALIDATION_PATTERNS = [
7546
7652
  "must not be empty",
7547
7653
  "contains invalid characters",
7548
- "requires an array value",
7549
- "requires a non-empty array value",
7550
- "must be an integer between",
7551
- "must be a positive integer",
7654
+ "WHERE conditions are required for DELETE queries",
7655
+ "WHERE conditions are required unless allowFullTableDelete is true",
7656
+ "value is required for this operator",
7552
7657
  "null is only allowed with isNull/isNotNull operators",
7553
- "LIKE operator requires a string value",
7658
+ "operator requires a value",
7554
7659
  "operator requires a string or number value",
7555
- "operator requires a scalar value",
7556
- "Stream cancelled by caller"
7557
- ];
7558
- function isSafeValidationError(error) {
7559
- if (!(error instanceof Error)) {
7560
- return false;
7561
- }
7562
- return SAFE_VALIDATION_ERROR_PATTERNS.some(
7563
- (pattern) => error.message.includes(pattern)
7564
- );
7565
- }
7566
-
7567
- // src/data/relational/tools/relational-select/executor.ts
7568
- var logger16 = createLogger("agentforge:tools:data:relational:select");
7569
- function toSelectQueryInput(input) {
7570
- return {
7571
- table: input.table,
7572
- columns: input.columns,
7573
- where: input.where,
7574
- orderBy: input.orderBy,
7575
- limit: input.limit,
7576
- offset: input.offset,
7577
- vendor: input.vendor
7578
- };
7579
- }
7580
- async function executeSelect(manager, input, context) {
7581
- const startTime = Date.now();
7582
- logger16.debug("Building SELECT query", {
7583
- vendor: input.vendor,
7584
- table: input.table,
7585
- hasWhere: !!input.where,
7586
- hasOrderBy: !!input.orderBy,
7587
- hasLimit: !!input.limit,
7588
- streamingEnabled: !!input.streaming?.enabled
7589
- });
7590
- try {
7591
- const executor = context?.transaction ?? manager;
7592
- if (input.streaming?.enabled) {
7593
- const streamOptions = {
7594
- chunkSize: input.streaming.chunkSize,
7595
- maxRows: input.streaming.maxRows,
7596
- sampleSize: input.streaming.sampleSize
7597
- };
7598
- const streamInput = toSelectQueryInput(input);
7599
- if (input.streaming.benchmark) {
7600
- logger16.warn("Streaming benchmark enabled; SELECT will execute up to three times (result + benchmark regular + benchmark streaming).", {
7601
- vendor: input.vendor,
7602
- table: input.table,
7603
- chunkSize: streamOptions.chunkSize ?? DEFAULT_CHUNK_SIZE,
7604
- ...streamOptions.maxRows !== void 0 ? { maxRows: streamOptions.maxRows } : {},
7605
- ...streamOptions.sampleSize !== void 0 ? { sampleSize: streamOptions.sampleSize } : {}
7606
- });
7607
- }
7608
- const streamResult = await executeStreamingSelect(executor, streamInput, streamOptions);
7609
- const benchmark = input.streaming.benchmark ? await benchmarkStreamingSelectMemory(executor, streamInput, streamOptions) : void 0;
7610
- const executionTime2 = Date.now() - startTime;
7611
- logger16.debug("Streaming SELECT query executed successfully", {
7612
- vendor: input.vendor,
7613
- table: input.table,
7614
- rowCount: streamResult.rowCount,
7615
- chunkCount: streamResult.chunkCount,
7616
- executionTime: executionTime2,
7617
- cancelled: streamResult.cancelled
7618
- });
7619
- return {
7620
- rows: streamResult.rows,
7621
- rowCount: streamResult.rowCount,
7622
- executionTime: executionTime2,
7623
- streaming: {
7624
- enabled: true,
7625
- chunkSize: input.streaming.chunkSize ?? DEFAULT_CHUNK_SIZE,
7626
- chunkCount: streamResult.chunkCount,
7627
- sampledRowCount: streamResult.rows.length,
7628
- streamedRowCount: streamResult.rowCount,
7629
- cancelled: streamResult.cancelled,
7630
- memoryUsage: streamResult.memoryUsage,
7631
- benchmark
7632
- }
7633
- };
7634
- }
7635
- const query = buildSelectQuery2(input);
7636
- const result = await executor.execute(query);
7637
- const executionTime = Date.now() - startTime;
7638
- const rows = Array.isArray(result) ? result : result.rows || [];
7639
- const rowCount = rows.length;
7640
- logger16.debug("SELECT query executed successfully", {
7641
- vendor: input.vendor,
7642
- table: input.table,
7643
- rowCount,
7644
- executionTime
7645
- });
7646
- return {
7647
- rows,
7648
- rowCount,
7649
- executionTime
7650
- };
7651
- } catch (error) {
7652
- const executionTime = Date.now() - startTime;
7653
- logger16.error("SELECT query execution failed", {
7654
- vendor: input.vendor,
7655
- table: input.table,
7656
- error: error instanceof Error ? error.message : String(error),
7657
- executionTime
7658
- });
7659
- if (isSafeValidationError(error)) {
7660
- throw error;
7661
- }
7662
- throw new Error("SELECT query failed. See logs for details.", { cause: error });
7663
- }
7664
- }
7665
-
7666
- // src/data/relational/tools/relational-select/index.ts
7667
- var relationalSelect = toolBuilder().name("relational-select").displayName("Relational SELECT").description("Execute type-safe SELECT queries with WHERE conditions, ORDER BY, LIMIT, and OFFSET using Drizzle ORM query builder").category(ToolCategory.DATABASE).tags(["database", "sql", "select", "query", "postgresql", "mysql", "sqlite"]).schema(relationalSelectSchema).example({
7668
- description: "SELECT with WHERE condition",
7669
- input: {
7670
- table: "users",
7671
- columns: ["id", "name", "email"],
7672
- where: [{ column: "status", operator: "eq", value: "active" }],
7673
- vendor: "postgresql",
7674
- connectionString: "postgresql://localhost/mydb"
7675
- }
7676
- }).example({
7677
- description: "SELECT with pagination",
7678
- input: {
7679
- table: "products",
7680
- orderBy: [{ column: "created_at", direction: "desc" }],
7681
- limit: 20,
7682
- offset: 0,
7683
- vendor: "mysql",
7684
- connectionString: "mysql://localhost/shop"
7685
- }
7686
- }).example({
7687
- description: "SELECT with streaming mode",
7688
- input: {
7689
- table: "events",
7690
- orderBy: [{ column: "id", direction: "asc" }],
7691
- streaming: {
7692
- enabled: true,
7693
- chunkSize: 250,
7694
- sampleSize: 25
7695
- },
7696
- vendor: "postgresql",
7697
- connectionString: "postgresql://localhost/analytics"
7698
- }
7699
- }).implement(async (input) => {
7700
- const manager = new ConnectionManager({
7701
- vendor: input.vendor,
7702
- connection: input.connectionString
7703
- });
7704
- try {
7705
- await manager.connect();
7706
- const result = await executeSelect(manager, input);
7707
- return {
7708
- success: true,
7709
- rows: result.rows,
7710
- rowCount: result.rowCount,
7711
- executionTime: result.executionTime,
7712
- streaming: result.streaming
7713
- };
7714
- } catch (error) {
7715
- let errorMessage = "Failed to execute SELECT query. Please verify your input and database connection.";
7716
- if (isSafeValidationError(error)) {
7717
- errorMessage = error.message;
7718
- }
7719
- return {
7720
- success: false,
7721
- error: errorMessage,
7722
- rows: [],
7723
- rowCount: 0
7724
- };
7725
- } finally {
7726
- await manager.disconnect();
7727
- }
7728
- }).build();
7729
- var insertValueSchema = z.union([
7730
- z.string().describe("String value"),
7731
- z.number().describe("Numeric value"),
7732
- z.boolean().describe("Boolean value"),
7733
- z.null().describe("Null value")
7734
- ]).describe("Scalar value for an INSERT column");
7735
- var insertRowSchema = z.record(
7736
- z.string().min(1, "Column name must not be empty").describe("Column name"),
7737
- insertValueSchema
7738
- ).describe("One row object where keys are column names and values are scalar values");
7739
- var insertReturningModeSchema = z.enum(["none", "id", "row"]);
7740
- var insertReturningSchema = z.object({
7741
- mode: insertReturningModeSchema.default("none").describe("Returning mode: none, id, or full row"),
7742
- idColumn: z.string().min(1, "idColumn must not be empty").optional().describe('Primary key column to return when mode is "id"')
7743
- }).superRefine((value, ctx) => {
7744
- if (value.idColumn && value.mode !== "id") {
7745
- ctx.addIssue({
7746
- code: z.ZodIssueCode.custom,
7747
- path: ["idColumn"],
7748
- message: 'idColumn can only be provided when returning.mode is "id"'
7749
- });
7750
- }
7751
- });
7752
- var insertBatchOptionsSchema = z.object({
7753
- enabled: z.boolean().optional().default(true).describe("Enable chunked batch execution for array insert payloads"),
7754
- batchSize: z.number().int().positive().max(5e3).optional().default(100).describe("Rows per batch chunk"),
7755
- continueOnError: z.boolean().optional().default(true).describe("Continue processing remaining chunks when one chunk fails"),
7756
- maxRetries: z.number().int().min(0).max(5).optional().default(0).describe("Retry attempts per failed chunk"),
7757
- retryDelayMs: z.number().int().min(0).max(6e4).optional().default(0).describe("Delay between retry attempts in milliseconds"),
7758
- benchmark: z.boolean().optional().default(false).describe("Run synthetic benchmark metadata comparing individual vs batched execution callbacks")
7759
- });
7760
- var relationalInsertSchema = z.object({
7761
- table: z.string().min(1, "Table name is required").regex(
7762
- VALID_QUALIFIED_IDENTIFIER_PATTERN,
7763
- "Table name contains invalid characters. Only alphanumeric characters, underscores, and dots (for schema qualification) are allowed."
7764
- ).describe("Table name to insert into (schema-qualified names supported, e.g. public.users)"),
7765
- data: z.union([
7766
- insertRowSchema,
7767
- z.array(insertRowSchema).min(1, "Insert data array must not be empty")
7768
- ]).describe("Single row object or array of row objects to insert"),
7769
- batch: insertBatchOptionsSchema.optional().describe("Optional batch execution controls for array insert payloads"),
7770
- returning: insertReturningSchema.optional().describe("Optional RETURNING behavior"),
7771
- vendor: z.enum(["postgresql", "mysql", "sqlite"]).describe("Database vendor"),
7772
- connectionString: z.string().min(1, "Database connection string is required").describe("Database connection string")
7773
- });
7774
-
7775
- // src/data/relational/tools/relational-insert/error-utils.ts
7776
- var SAFE_INSERT_VALIDATION_PATTERNS = [
7777
- "must not be empty",
7778
- "contains invalid characters",
7779
- "must be an object",
7780
- "must not be an empty array",
7781
- "must not be undefined",
7782
- "idColumn can only be provided",
7783
- "Returning full rows is not supported for mysql",
7784
- "Batch INSERT with only DEFAULT VALUES is not supported"
7660
+ "operator requires a string value",
7661
+ "operator requires a non-empty array value",
7662
+ "must not include value"
7785
7663
  ];
7786
7664
  var CONSTRAINT_VIOLATION_PATTERNS = [
7787
7665
  {
7788
- pattern: /(unique constraint|duplicate key|duplicate entry)/i,
7789
- message: "Insert failed: unique constraint violation."
7790
- },
7791
- {
7792
- pattern: /(foreign key constraint|violates foreign key constraint)/i,
7793
- message: "Insert failed: foreign key constraint violation."
7794
- },
7795
- {
7796
- pattern: /(not null constraint|cannot be null)/i,
7797
- message: "Insert failed: NOT NULL constraint violation."
7666
+ pattern: /(foreign key constraint|violates foreign key constraint|foreign key mismatch|a foreign key constraint fails)/i,
7667
+ message: "Delete failed: foreign key constraint violation."
7798
7668
  }
7799
7669
  ];
7800
- function isSafeInsertValidationError(error) {
7670
+ function isSafeDeleteValidationError(error) {
7801
7671
  if (!(error instanceof Error)) {
7802
7672
  return false;
7803
7673
  }
7804
- return SAFE_INSERT_VALIDATION_PATTERNS.some((pattern) => error.message.includes(pattern));
7674
+ return SAFE_DELETE_VALIDATION_PATTERNS.some((pattern) => error.message.includes(pattern));
7805
7675
  }
7806
- function getConstraintViolationMessage(error) {
7676
+ function getDeleteConstraintViolationMessage(error, cascade) {
7807
7677
  if (!(error instanceof Error)) {
7808
7678
  return null;
7809
7679
  }
@@ -7813,79 +7683,43 @@ function getConstraintViolationMessage(error) {
7813
7683
  }
7814
7684
  for (const entry of CONSTRAINT_VIOLATION_PATTERNS) {
7815
7685
  if (messages.some((msg) => entry.pattern.test(msg))) {
7686
+ if (cascade) {
7687
+ return `${entry.message} Verify database-level ON DELETE CASCADE is configured for related foreign keys.`;
7688
+ }
7816
7689
  return entry.message;
7817
7690
  }
7818
7691
  }
7819
7692
  return null;
7820
7693
  }
7821
- function isSafeInsertError(error) {
7694
+ function isSafeDeleteError(error) {
7822
7695
  if (!(error instanceof Error)) {
7823
7696
  return false;
7824
7697
  }
7825
- return isSafeInsertValidationError(error) || getConstraintViolationMessage(error) !== null;
7698
+ return SAFE_DELETE_VALIDATION_PATTERNS.some((pattern) => error.message.includes(pattern)) || error.message.startsWith("Delete failed:");
7826
7699
  }
7827
- var insertExecutorLogger = createLogger("agentforge:tools:data:relational:insert");
7700
+ var deleteExecutorLogger = createLogger("agentforge:tools:data:relational:delete");
7828
7701
  function toNumber(value) {
7829
7702
  return typeof value === "number" && Number.isFinite(value) ? value : void 0;
7830
7703
  }
7831
- function isPlainObject(value) {
7832
- return !!value && typeof value === "object" && !Array.isArray(value);
7833
- }
7834
- function normalizeExecutionResult(result) {
7704
+ function normalizeAffectedRows(result) {
7835
7705
  if (Array.isArray(result)) {
7836
- if (result.length > 0 && isPlainObject(result[0])) {
7706
+ if (result.length > 0 && result[0] && typeof result[0] === "object") {
7837
7707
  const first = result[0];
7838
7708
  const affectedRows = toNumber(first.affectedRows) ?? toNumber(first.rowCount) ?? toNumber(first.changes);
7839
- const insertId = toNumber(first.insertId);
7840
- const lastInsertRowid = toNumber(first.lastInsertRowid);
7841
- if (affectedRows !== void 0 || insertId !== void 0 || lastInsertRowid !== void 0) {
7842
- return {
7843
- rows: [],
7844
- rowCount: affectedRows ?? 0,
7845
- insertId,
7846
- lastInsertRowid
7847
- };
7709
+ if (affectedRows !== void 0) {
7710
+ return affectedRows;
7848
7711
  }
7849
7712
  }
7850
- return {
7851
- rows: result,
7852
- rowCount: result.length
7853
- };
7713
+ return result.length;
7854
7714
  }
7855
- if (isPlainObject(result)) {
7856
- const rows = Array.isArray(result.rows) ? result.rows : [];
7857
- const rowCount = toNumber(result.rowCount) ?? toNumber(result.affectedRows) ?? toNumber(result.changes) ?? rows.length;
7858
- return {
7859
- rows,
7860
- rowCount,
7861
- insertId: toNumber(result.insertId),
7862
- lastInsertRowid: toNumber(result.lastInsertRowid)
7863
- };
7715
+ if (result && typeof result === "object") {
7716
+ const resultRecord = result;
7717
+ return toNumber(resultRecord.rowCount) ?? toNumber(resultRecord.affectedRows) ?? toNumber(resultRecord.changes) ?? (Array.isArray(resultRecord.rows) ? resultRecord.rows.length : 0);
7864
7718
  }
7865
- return {
7866
- rows: [],
7867
- rowCount: 0
7868
- };
7719
+ return 0;
7869
7720
  }
7870
- function deriveInsertedIds(options) {
7871
- const { idColumn, inputRows, returnedRows, rowCount, insertId, lastInsertRowid } = options;
7872
- if (returnedRows.length > 0) {
7873
- const idsFromReturn = returnedRows.map((row) => isPlainObject(row) ? row[idColumn] : void 0).filter((id) => typeof id === "number" || typeof id === "string");
7874
- if (idsFromReturn.length > 0) {
7875
- return idsFromReturn;
7876
- }
7877
- }
7878
- if (inputRows.every((row) => typeof row[idColumn] === "number" || typeof row[idColumn] === "string")) {
7879
- return inputRows.map((row) => row[idColumn]);
7880
- }
7881
- if (insertId !== void 0 && rowCount > 0) {
7882
- return Array.from({ length: rowCount }, (_, index) => insertId + index);
7883
- }
7884
- if (lastInsertRowid !== void 0 && rowCount > 0) {
7885
- const startId = lastInsertRowid - rowCount + 1;
7886
- return Array.from({ length: rowCount }, (_, index) => startId + index);
7887
- }
7888
- return [];
7721
+ function toErrorMessage(error) {
7722
+ return error instanceof Error ? error.message : String(error);
7889
7723
  }
7890
7724
  function resolveBatchOptions(batch) {
7891
7725
  if (!batch || batch.enabled === false) {
@@ -7900,79 +7734,87 @@ function resolveBatchOptions(batch) {
7900
7734
  benchmark: batch.benchmark ?? false
7901
7735
  };
7902
7736
  }
7903
- function toSingleInsertRows(row) {
7904
- return [row];
7737
+ function toSingleDeleteOperation(input) {
7738
+ return {
7739
+ where: input.where,
7740
+ allowFullTableDelete: input.allowFullTableDelete,
7741
+ cascade: input.cascade,
7742
+ softDelete: input.softDelete
7743
+ };
7905
7744
  }
7906
7745
 
7907
- // src/data/relational/tools/relational-insert/executor-single.ts
7908
- async function executeInsertOnce(manager, input, context) {
7909
- const built = buildInsertQuery({
7746
+ // src/data/relational/tools/relational-delete/executor-single.ts
7747
+ async function executeSingleDelete(executor, input, operation, context) {
7748
+ const built = buildDeleteQuery({
7910
7749
  table: input.table,
7911
- data: input.data,
7912
- returning: input.returning,
7750
+ where: operation.where,
7751
+ allowFullTableDelete: operation.allowFullTableDelete,
7752
+ softDelete: operation.softDelete,
7913
7753
  vendor: input.vendor
7914
7754
  });
7915
- const executor = manager;
7916
- if (Array.isArray(built.query)) {
7917
- let totalRowCount = 0;
7918
- const allRows = [];
7919
- const allIds = [];
7920
- for (const singleQuery of built.query) {
7921
- const rawResult2 = await executor.execute(singleQuery);
7922
- const normalized2 = normalizeExecutionResult(rawResult2);
7923
- totalRowCount += normalized2.rowCount > 0 ? normalized2.rowCount : 1;
7924
- if (built.returningMode === "row") {
7925
- allRows.push(...normalized2.rows);
7926
- }
7927
- if (built.returningMode === "id") {
7928
- const ids = deriveInsertedIds({
7929
- idColumn: built.idColumn,
7930
- inputRows: toSingleInsertRows(built.rows[allIds.length]),
7931
- returnedRows: normalized2.rows,
7932
- rowCount: 1,
7933
- insertId: normalized2.insertId,
7934
- lastInsertRowid: normalized2.lastInsertRowid
7935
- });
7936
- allIds.push(...ids);
7937
- }
7938
- }
7939
- return {
7940
- rowCount: totalRowCount,
7941
- rows: allRows,
7942
- insertedIds: allIds,
7943
- executionTime: 0
7944
- };
7945
- }
7946
- const rawResult = await executor.execute(built.query);
7947
- const normalized = normalizeExecutionResult(rawResult);
7948
- const rowCount = normalized.rowCount > 0 ? normalized.rowCount : built.rows.length;
7949
- const rows = built.returningMode === "row" ? normalized.rows : [];
7950
- const insertedIds = built.returningMode === "id" ? deriveInsertedIds({
7951
- idColumn: built.idColumn,
7952
- inputRows: built.rows,
7953
- returnedRows: normalized.rows,
7954
- rowCount,
7955
- insertId: normalized.insertId,
7956
- lastInsertRowid: normalized.lastInsertRowid
7957
- }) : [];
7755
+ const session = context?.transaction ?? executor;
7756
+ const rawResult = await session.execute(built.query);
7757
+ const rowCount = normalizeAffectedRows(rawResult);
7958
7758
  return {
7959
7759
  rowCount,
7960
- rows,
7961
- insertedIds,
7962
- executionTime: 0
7760
+ executionTime: 0,
7761
+ softDeleted: built.usesSoftDelete
7963
7762
  };
7964
7763
  }
7965
7764
 
7966
- // src/data/relational/tools/relational-insert/executor-batch.ts
7967
- async function executeInsertInBatchMode(manager, input, context, options) {
7765
+ // src/data/relational/tools/relational-delete/executor-batch.ts
7766
+ async function executeDeleteInBatchMode(executor, input, context, options) {
7968
7767
  const batchResult = await executeBatchedTask(
7969
7768
  {
7970
- operation: "insert",
7971
- items: input.data,
7972
- executeBatch: async (rows2) => executeInsertOnce(manager, {
7973
- ...input,
7974
- data: rows2}),
7975
- getBatchSuccessCount: (result, rows2) => Math.min(result.rowCount, rows2.length)
7769
+ operation: "delete",
7770
+ items: input.operations,
7771
+ executeBatch: async (operations, batchIndex) => {
7772
+ let rowCount2 = 0;
7773
+ let successfulItems = 0;
7774
+ let failedItems = 0;
7775
+ let softDeletedCount = 0;
7776
+ const failures = [];
7777
+ for (const operation of operations) {
7778
+ try {
7779
+ const result = await executeSingleDelete(
7780
+ executor,
7781
+ input,
7782
+ {
7783
+ where: operation.where,
7784
+ allowFullTableDelete: operation.allowFullTableDelete,
7785
+ cascade: operation.cascade,
7786
+ softDelete: operation.softDelete
7787
+ },
7788
+ context
7789
+ );
7790
+ rowCount2 += result.rowCount;
7791
+ successfulItems += 1;
7792
+ if (result.softDeleted) {
7793
+ softDeletedCount += 1;
7794
+ }
7795
+ } catch (error) {
7796
+ if (!options.continueOnError) {
7797
+ throw error;
7798
+ }
7799
+ failedItems += 1;
7800
+ failures.push({
7801
+ operation: "delete",
7802
+ batchIndex,
7803
+ batchSize: operations.length,
7804
+ attempts: 1,
7805
+ error: toErrorMessage(error)
7806
+ });
7807
+ }
7808
+ }
7809
+ return {
7810
+ rowCount: rowCount2,
7811
+ successfulItems,
7812
+ failedItems,
7813
+ softDeletedCount,
7814
+ failures
7815
+ };
7816
+ },
7817
+ getBatchSuccessCount: (result) => result.successfulItems
7976
7818
  },
7977
7819
  {
7978
7820
  batchSize: options.batchSize,
@@ -7980,7 +7822,7 @@ async function executeInsertInBatchMode(manager, input, context, options) {
7980
7822
  maxRetries: options.maxRetries,
7981
7823
  retryDelayMs: options.retryDelayMs,
7982
7824
  onProgress: (progress) => {
7983
- insertExecutorLogger.debug("INSERT batch progress", {
7825
+ deleteExecutorLogger.debug("DELETE batch progress", {
7984
7826
  vendor: input.vendor,
7985
7827
  table: input.table,
7986
7828
  ...progress
@@ -7989,18 +7831,21 @@ async function executeInsertInBatchMode(manager, input, context, options) {
7989
7831
  }
7990
7832
  );
7991
7833
  const rowCount = batchResult.results.reduce((total, result) => total + result.rowCount, 0);
7992
- const rows = batchResult.results.flatMap((result) => result.rows);
7993
- const insertedIds = batchResult.results.flatMap((result) => result.insertedIds);
7834
+ const softDeleted = batchResult.results.some((result) => result.softDeletedCount > 0);
7835
+ const operationFailures = batchResult.results.flatMap((result) => result.failures);
7994
7836
  let benchmark;
7995
7837
  if (options.benchmark) {
7996
- insertExecutorLogger.warn("INSERT batch benchmark enabled. Synthetic benchmark callbacks are side-effect free and do not execute SQL.", {
7997
- vendor: input.vendor,
7998
- table: input.table,
7999
- totalRows: input.data.length,
8000
- batchSize: options.batchSize
8001
- });
7838
+ deleteExecutorLogger.warn(
7839
+ "DELETE batch benchmark enabled. Synthetic benchmark callbacks are side-effect free and do not execute SQL.",
7840
+ {
7841
+ vendor: input.vendor,
7842
+ table: input.table,
7843
+ operationCount: input.operations.length,
7844
+ batchSize: options.batchSize
7845
+ }
7846
+ );
8002
7847
  benchmark = await benchmarkBatchExecution({
8003
- items: input.data,
7848
+ items: input.operations,
8004
7849
  batchSize: options.batchSize,
8005
7850
  runIndividual: async () => {
8006
7851
  },
@@ -8010,9 +7855,8 @@ async function executeInsertInBatchMode(manager, input, context, options) {
8010
7855
  }
8011
7856
  return {
8012
7857
  rowCount,
8013
- rows,
8014
- insertedIds,
8015
7858
  executionTime: batchResult.executionTime,
7859
+ softDeleted,
8016
7860
  batch: {
8017
7861
  enabled: true,
8018
7862
  batchSize: options.batchSize,
@@ -8023,38 +7867,41 @@ async function executeInsertInBatchMode(manager, input, context, options) {
8023
7867
  totalBatches: batchResult.totalBatches,
8024
7868
  retries: batchResult.retries,
8025
7869
  partialSuccess: batchResult.partialSuccess,
8026
- failures: batchResult.failures,
7870
+ failures: [...batchResult.failures, ...operationFailures],
8027
7871
  benchmark
8028
7872
  }
8029
7873
  };
8030
7874
  }
8031
7875
 
8032
- // src/data/relational/tools/relational-insert/executor.ts
8033
- async function executeInsert(manager, input, context) {
7876
+ // src/data/relational/tools/relational-delete/executor.ts
7877
+ async function executeDelete(executor, input, context) {
8034
7878
  const startTime = Date.now();
8035
- insertExecutorLogger.debug("Building INSERT query", {
7879
+ deleteExecutorLogger.debug("Building DELETE query", {
8036
7880
  vendor: input.vendor,
8037
7881
  table: input.table,
8038
- isBatch: Array.isArray(input.data),
8039
- batchModeEnabled: !!input.batch?.enabled,
8040
- returningMode: input.returning?.mode ?? "none"
7882
+ hasWhere: !!input.where?.length,
7883
+ ...input.allowFullTableDelete !== void 0 ? { allowFullTableDelete: input.allowFullTableDelete } : {},
7884
+ ...input.cascade !== void 0 ? { cascade: input.cascade } : {},
7885
+ softDelete: !!input.softDelete,
7886
+ operationCount: input.operations?.length ?? 0,
7887
+ batchModeEnabled: !!input.batch?.enabled
8041
7888
  });
8042
7889
  try {
8043
7890
  const batchOptions = resolveBatchOptions(input.batch);
8044
- const result = Array.isArray(input.data) && batchOptions ? await executeInsertInBatchMode(
8045
- manager,
7891
+ const result = input.operations && batchOptions ? await executeDeleteInBatchMode(
7892
+ executor,
8046
7893
  input,
8047
7894
  context,
8048
7895
  batchOptions
8049
- ) : await executeInsertOnce(manager, input, context);
7896
+ ) : await executeSingleDelete(executor, input, toSingleDeleteOperation(input), context);
8050
7897
  const executionTime = Date.now() - startTime;
8051
- insertExecutorLogger.debug("INSERT query executed successfully", {
7898
+ deleteExecutorLogger.debug("DELETE query executed successfully", {
8052
7899
  vendor: input.vendor,
8053
7900
  table: input.table,
8054
7901
  rowCount: result.rowCount,
8055
- returnedRows: result.rows.length,
8056
- returnedIds: result.insertedIds.length,
8057
7902
  executionTime,
7903
+ softDelete: result.softDeleted,
7904
+ ...input.cascade !== void 0 ? { cascade: input.cascade } : {},
8058
7905
  batchMode: !!result.batch,
8059
7906
  partialSuccess: result.batch?.partialSuccess ?? false
8060
7907
  });
@@ -8064,314 +7911,332 @@ async function executeInsert(manager, input, context) {
8064
7911
  };
8065
7912
  } catch (error) {
8066
7913
  const executionTime = Date.now() - startTime;
8067
- insertExecutorLogger.error("INSERT query execution failed", {
7914
+ deleteExecutorLogger.error("DELETE query execution failed", {
8068
7915
  vendor: input.vendor,
8069
7916
  table: input.table,
8070
7917
  error: error instanceof Error ? error.message : String(error),
8071
- executionTime
7918
+ executionTime,
7919
+ ...input.cascade !== void 0 ? { cascade: input.cascade } : {},
7920
+ softDelete: !!input.softDelete
8072
7921
  });
8073
- const constraintMessage = getConstraintViolationMessage(error);
7922
+ const constraintMessage = getDeleteConstraintViolationMessage(error, input.cascade ?? false);
8074
7923
  if (constraintMessage) {
8075
7924
  throw new Error(constraintMessage, { cause: error });
8076
7925
  }
8077
- if (isSafeInsertValidationError(error)) {
7926
+ if (isSafeDeleteValidationError(error)) {
8078
7927
  throw error;
8079
7928
  }
8080
- throw new Error("INSERT query failed. See logs for details.", { cause: error });
7929
+ throw new Error("DELETE query failed. See logs for details.", { cause: error });
8081
7930
  }
8082
7931
  }
8083
7932
 
8084
- // src/data/relational/tools/relational-insert/index.ts
8085
- var relationalInsert = toolBuilder().name("relational-insert").displayName("Relational INSERT").description("Execute type-safe INSERT queries with single-row and batch insert support").category(ToolCategory.DATABASE).tags(["database", "sql", "insert", "postgresql", "mysql", "sqlite"]).schema(relationalInsertSchema).example({
8086
- description: "Insert a single row and return generated ID",
8087
- input: {
8088
- table: "users",
8089
- data: { name: "Alice", email: "alice@example.com" },
8090
- returning: { mode: "id", idColumn: "id" },
8091
- vendor: "postgresql",
8092
- connectionString: "postgresql://localhost/mydb"
8093
- }
8094
- }).example({
8095
- description: "Batch insert rows",
8096
- input: {
8097
- table: "users",
8098
- data: [
8099
- { name: "Bob", email: "bob@example.com" },
8100
- { name: "Carol", email: "carol@example.com" }
8101
- ],
8102
- batch: {
8103
- batchSize: 250,
8104
- continueOnError: true,
8105
- maxRetries: 1
8106
- },
8107
- vendor: "sqlite",
8108
- connectionString: "data.db"
7933
+ // src/data/relational/tools/legacy-mutation-adapter.ts
7934
+ function replaceMutationConnectionFailure(result, configuredMessage, legacyMessage) {
7935
+ if (!result.success && result.error === configuredMessage) {
7936
+ return { ...result, error: legacyMessage };
8109
7937
  }
8110
- }).implement(async (input) => {
8111
- const manager = new ConnectionManager({
8112
- vendor: input.vendor,
8113
- connection: input.connectionString
7938
+ return result;
7939
+ }
7940
+ async function withEphemeralRelationalMutationToolSet(database, invoke, toErrorResponse) {
7941
+ const toolSet = createLegacyRelationalMutationToolSet({
7942
+ vendor: database.vendor,
7943
+ connection: database.connectionString
8114
7944
  });
8115
7945
  try {
8116
- await manager.connect();
8117
- const result = await executeInsert(manager, input);
7946
+ try {
7947
+ return await invoke(toolSet);
7948
+ } catch (error) {
7949
+ if (error instanceof MissingPeerDependencyError) {
7950
+ throw error;
7951
+ }
7952
+ return toErrorResponse(error);
7953
+ }
7954
+ } finally {
7955
+ await toolSet.dispose();
7956
+ }
7957
+ }
7958
+
7959
+ // src/data/relational/tools/relational-delete/index.ts
7960
+ function toDeleteErrorResponse(error) {
7961
+ const errorMessage = isSafeDeleteError(error) ? error.message : "Failed to execute DELETE query. Please verify your input and database connection.";
7962
+ return {
7963
+ success: false,
7964
+ error: errorMessage,
7965
+ rowCount: 0,
7966
+ softDeleted: false
7967
+ };
7968
+ }
7969
+ async function invokeRelationalDelete(execution, input) {
7970
+ try {
7971
+ const result = await executeDelete(
7972
+ execution.executor,
7973
+ { ...input, vendor: execution.vendor },
7974
+ execution.transaction ? { transaction: execution.transaction } : void 0
7975
+ );
8118
7976
  return {
8119
7977
  success: true,
8120
7978
  rowCount: result.rowCount,
8121
- insertedIds: result.insertedIds,
8122
- rows: result.rows,
8123
7979
  executionTime: result.executionTime,
7980
+ softDeleted: result.softDeleted,
8124
7981
  batch: result.batch
8125
7982
  };
8126
7983
  } catch (error) {
8127
- let errorMessage = "Failed to execute INSERT query. Please verify your input and database connection.";
8128
- if (isSafeInsertError(error)) {
8129
- errorMessage = error.message;
8130
- }
8131
- return {
8132
- success: false,
8133
- error: errorMessage,
8134
- rowCount: 0,
8135
- insertedIds: [],
8136
- rows: []
8137
- };
8138
- } finally {
8139
- await manager.disconnect();
7984
+ return toDeleteErrorResponse(error);
7985
+ }
7986
+ }
7987
+ var relationalDelete = toolBuilder().name("relational-delete").displayName("Relational DELETE").description("Execute type-safe DELETE queries with single and batched operation support").category(ToolCategory.DATABASE).tags(["database", "sql", "delete", "postgresql", "mysql", "sqlite"]).schema(relationalDeleteSchema).example({
7988
+ description: "Delete one row by condition",
7989
+ input: {
7990
+ table: "users",
7991
+ where: [{ column: "id", operator: "eq", value: 123 }],
7992
+ vendor: "postgresql",
7993
+ connectionString: "postgresql://localhost/mydb"
7994
+ }
7995
+ }).example({
7996
+ description: "Soft delete rows",
7997
+ input: {
7998
+ table: "users",
7999
+ where: [{ column: "status", operator: "eq", value: "inactive" }],
8000
+ softDelete: { column: "deleted_at" },
8001
+ vendor: "sqlite",
8002
+ connectionString: "data.db"
8003
+ }
8004
+ }).example({
8005
+ description: "Batch delete operations",
8006
+ input: {
8007
+ table: "users",
8008
+ operations: [
8009
+ {
8010
+ where: [{ column: "status", operator: "eq", value: "inactive" }],
8011
+ softDelete: { column: "deleted_at" }
8012
+ },
8013
+ {
8014
+ where: [{ column: "status", operator: "eq", value: "pending_delete" }],
8015
+ softDelete: { column: "deleted_at" }
8016
+ }
8017
+ ],
8018
+ batch: {
8019
+ batchSize: 100,
8020
+ continueOnError: true
8021
+ },
8022
+ vendor: "postgresql",
8023
+ connectionString: "postgresql://localhost/mydb"
8140
8024
  }
8025
+ }).implement(async (input) => {
8026
+ const { connectionString, vendor, ...operation } = input;
8027
+ return withEphemeralRelationalMutationToolSet(
8028
+ { vendor, connectionString },
8029
+ async (toolSet) => replaceMutationConnectionFailure(
8030
+ await toolSet.delete.invoke(operation),
8031
+ DELETE_CONNECTION_FAILURE,
8032
+ "Failed to execute DELETE query. Please verify your input and database connection."
8033
+ ),
8034
+ toDeleteErrorResponse
8035
+ );
8141
8036
  }).build();
8142
- var updateValueSchema = z.union([
8143
- z.string().describe("String value"),
8144
- z.number().describe("Numeric value"),
8145
- z.boolean().describe("Boolean value"),
8146
- z.null().describe("Null value")
8147
- ]).describe("Scalar value for an UPDATE column");
8148
- var updateDataSchema = z.record(
8149
- z.string().min(1, "Column name must not be empty").describe("Column name"),
8150
- updateValueSchema
8151
- ).superRefine((value, ctx) => {
8152
- if (Object.keys(value).length === 0) {
8153
- ctx.addIssue({
8154
- code: z.ZodIssueCode.custom,
8155
- message: "Update data must not be empty"
8156
- });
8037
+
8038
+ // src/data/relational/tools/relational-get-schema-error-utils.ts
8039
+ var SAFE_VALIDATION_ERROR_PATTERNS = [
8040
+ "must not be empty",
8041
+ "contains invalid characters",
8042
+ "Invalid table filter"
8043
+ ];
8044
+ function isSafeGetSchemaValidationError(error) {
8045
+ if (!(error instanceof Error)) {
8046
+ return false;
8157
8047
  }
8158
- }).describe("Object containing column-value pairs to update");
8159
- var updateWhereOperatorSchema = z.enum([
8160
- "eq",
8161
- "ne",
8162
- "gt",
8163
- "lt",
8164
- "gte",
8165
- "lte",
8166
- "like",
8167
- "in",
8168
- "notIn",
8169
- "isNull",
8170
- "isNotNull"
8171
- ]);
8172
- var updateWhereConditionSchema = z.object({
8173
- column: z.string().min(1, "Column name must not be empty").describe("Column name to filter on"),
8174
- operator: updateWhereOperatorSchema.describe("Comparison operator"),
8175
- value: z.union([
8176
- z.string(),
8177
- z.number(),
8178
- z.boolean(),
8179
- z.array(z.union([z.string(), z.number()])),
8180
- z.null()
8181
- ]).optional().describe("Value to compare against (not required for isNull/isNotNull)")
8182
- }).superRefine((value, ctx) => {
8183
- const op = value.operator;
8184
- if (op === "isNull" || op === "isNotNull") {
8185
- if (value.value !== void 0) {
8186
- ctx.addIssue({
8187
- code: z.ZodIssueCode.custom,
8188
- path: ["value"],
8189
- message: "value must not be provided when using isNull or isNotNull operator"
8190
- });
8191
- }
8192
- return;
8048
+ return SAFE_VALIDATION_ERROR_PATTERNS.some(
8049
+ (pattern) => error.message.includes(pattern)
8050
+ );
8051
+ }
8052
+
8053
+ // src/data/relational/tools/legacy-read-adapter.ts
8054
+ function replaceConnectionFailureMessage(result, configuredMessage, legacyMessage) {
8055
+ if (!result.success && result.error === configuredMessage) {
8056
+ return { ...result, error: legacyMessage };
8193
8057
  }
8194
- if (value.value === void 0) {
8195
- ctx.addIssue({
8196
- code: z.ZodIssueCode.custom,
8197
- path: ["value"],
8198
- message: "value is required for this operator"
8199
- });
8200
- return;
8058
+ return result;
8059
+ }
8060
+ async function withEphemeralRelationalToolSet(database, invoke, options = {}, sharedSchemaCacheKey) {
8061
+ const config = { vendor: database.vendor, connection: database.connectionString };
8062
+ const toolSet = createLegacyRelationalReadToolSet(config, options, sharedSchemaCacheKey);
8063
+ try {
8064
+ return await invoke(toolSet);
8065
+ } finally {
8066
+ await toolSet.dispose();
8201
8067
  }
8202
- if (op === "in" || op === "notIn") {
8203
- if (!Array.isArray(value.value)) {
8204
- ctx.addIssue({
8205
- code: z.ZodIssueCode.custom,
8206
- path: ["value"],
8207
- message: `${op === "in" ? "IN" : "NOT IN"} operator requires an array value`
8208
- });
8209
- } else if (value.value.length === 0) {
8210
- ctx.addIssue({
8211
- code: z.ZodIssueCode.custom,
8212
- path: ["value"],
8213
- message: `${op === "in" ? "IN" : "NOT IN"} operator requires a non-empty array value`
8214
- });
8068
+ }
8069
+
8070
+ // src/data/relational/tools/relational-get-schema.ts
8071
+ var logger16 = createLogger("agentforge:tools:data:relational:get-schema");
8072
+ function buildSchemaCacheKey(vendor, connectionString, database) {
8073
+ const databaseScope = database ?? "default";
8074
+ const connectionHash = createHash("sha256").update(connectionString).digest("hex");
8075
+ return `${vendor}:${databaseScope}:${connectionHash}`;
8076
+ }
8077
+ var relationalGetSchemaInputSchema = z.object({
8078
+ vendor: z.enum(["postgresql", "mysql", "sqlite"]).describe("Database vendor"),
8079
+ connectionString: z.string().min(1, "Database connection string is required").describe("Database connection string used to create a new connection"),
8080
+ database: z.string().min(1).optional().describe("Optional logical database name used for cache scoping"),
8081
+ tables: z.array(
8082
+ z.string().regex(
8083
+ VALID_TABLE_FILTER_PATTERN,
8084
+ "Table filter contains invalid characters. Use alphanumeric, underscore, and optional schema qualification."
8085
+ )
8086
+ ).optional().describe("Optional table filter list. When omitted, all tables are returned."),
8087
+ cacheTtlMs: z.number().int().min(0).optional().describe("Schema cache TTL in milliseconds (0 disables caching)"),
8088
+ refreshCache: z.boolean().optional().describe("Force cache invalidation before introspecting schema")
8089
+ });
8090
+ function toGetSchemaErrorResponse(execution, input, error) {
8091
+ logger16.error("Schema introspection failed", {
8092
+ vendor: execution.vendor,
8093
+ hasTablesFilter: Array.isArray(input.tables),
8094
+ tablesFilterCount: input.tables?.length ?? 0,
8095
+ refreshCache: input.refreshCache ?? false,
8096
+ error: error instanceof Error ? error.message : String(error)
8097
+ });
8098
+ const errorMessage = isSafeGetSchemaValidationError(error) ? error.message : "Failed to inspect schema. See logs for details.";
8099
+ return {
8100
+ success: false,
8101
+ error: errorMessage,
8102
+ schema: null
8103
+ };
8104
+ }
8105
+ async function invokeRelationalGetSchema(execution, input) {
8106
+ const cacheKey = execution.schemaCacheKey ?? (execution.schemaCache ? input.database ?? "default" : void 0);
8107
+ const inspector = new SchemaInspector(
8108
+ execution.transaction ?? execution.executor,
8109
+ execution.vendor,
8110
+ {
8111
+ cacheTtlMs: input.cacheTtlMs,
8112
+ cacheKey,
8113
+ cache: execution.schemaCache
8215
8114
  }
8216
- return;
8115
+ );
8116
+ try {
8117
+ if (input.refreshCache && cacheKey) {
8118
+ inspector.invalidateCache();
8119
+ }
8120
+ const schema = await inspector.inspect(
8121
+ execution.transaction ? { tables: input.tables, bypassCache: true } : { tables: input.tables }
8122
+ );
8123
+ const summary = schema.tables.reduce(
8124
+ (accumulator, table) => {
8125
+ accumulator.tableCount += 1;
8126
+ accumulator.columnCount += table.columns.length;
8127
+ accumulator.foreignKeyCount += table.foreignKeys.length;
8128
+ accumulator.indexCount += table.indexes.length;
8129
+ return accumulator;
8130
+ },
8131
+ { tableCount: 0, columnCount: 0, foreignKeyCount: 0, indexCount: 0 }
8132
+ );
8133
+ return { success: true, schema, summary };
8134
+ } catch (error) {
8135
+ return toGetSchemaErrorResponse(execution, input, error);
8217
8136
  }
8218
- if (value.value === null) {
8219
- ctx.addIssue({
8220
- code: z.ZodIssueCode.custom,
8221
- path: ["value"],
8222
- message: "null is only allowed with isNull/isNotNull operators"
8223
- });
8224
- return;
8137
+ }
8138
+ var relationalGetSchema = toolBuilder().name("relational-get-schema").displayName("Relational Get Schema").description("Inspect relational database schema (tables, columns, primary keys, foreign keys, indexes) across PostgreSQL, MySQL, and SQLite").category(ToolCategory.DATABASE).tags(["database", "schema", "introspection", "postgresql", "mysql", "sqlite"]).schema(relationalGetSchemaInputSchema).example({
8139
+ description: "Inspect all SQLite tables",
8140
+ input: {
8141
+ vendor: "sqlite",
8142
+ connectionString: ":memory:"
8225
8143
  }
8226
- if (op === "like" && typeof value.value !== "string") {
8227
- ctx.addIssue({
8228
- code: z.ZodIssueCode.custom,
8229
- path: ["value"],
8230
- message: "LIKE operator requires a string value"
8231
- });
8232
- return;
8144
+ }).example({
8145
+ description: "Inspect selected PostgreSQL tables with cache control",
8146
+ input: {
8147
+ vendor: "postgresql",
8148
+ connectionString: "postgresql://localhost/app",
8149
+ tables: ["public.users", "public.orders"],
8150
+ cacheTtlMs: 3e4,
8151
+ refreshCache: true
8233
8152
  }
8234
- if ((op === "gt" || op === "lt" || op === "gte" || op === "lte") && typeof value.value !== "string" && typeof value.value !== "number") {
8153
+ }).implement(async (input) => {
8154
+ const { connectionString, vendor, database, cacheTtlMs, tables, refreshCache } = input;
8155
+ return withEphemeralRelationalToolSet(
8156
+ { vendor, connectionString },
8157
+ (toolSet) => toolSet.getSchema.invoke({ tables, refreshCache }),
8158
+ { schemaCacheTtlMs: cacheTtlMs },
8159
+ buildSchemaCacheKey(vendor, connectionString, database)
8160
+ );
8161
+ }).build();
8162
+ var insertValueSchema = z.union([
8163
+ z.string().describe("String value"),
8164
+ z.number().describe("Numeric value"),
8165
+ z.boolean().describe("Boolean value"),
8166
+ z.null().describe("Null value")
8167
+ ]).describe("Scalar value for an INSERT column");
8168
+ var insertRowSchema = z.record(
8169
+ z.string().min(1, "Column name must not be empty").describe("Column name"),
8170
+ insertValueSchema
8171
+ ).describe("One row object where keys are column names and values are scalar values");
8172
+ var insertReturningModeSchema = z.enum(["none", "id", "row"]);
8173
+ var insertReturningSchema = z.object({
8174
+ mode: insertReturningModeSchema.default("none").describe("Returning mode: none, id, or full row"),
8175
+ idColumn: z.string().min(1, "idColumn must not be empty").optional().describe('Primary key column to return when mode is "id"')
8176
+ }).superRefine((value, ctx) => {
8177
+ if (value.idColumn && value.mode !== "id") {
8235
8178
  ctx.addIssue({
8236
8179
  code: z.ZodIssueCode.custom,
8237
- path: ["value"],
8238
- message: `${op.toUpperCase()} operator requires a string or number value`
8180
+ path: ["idColumn"],
8181
+ message: 'idColumn can only be provided when returning.mode is "id"'
8239
8182
  });
8240
8183
  }
8241
8184
  });
8242
- var updateOptimisticLockSchema = z.object({
8243
- column: z.string().min(1, "Optimistic lock column must not be empty").describe("Version or lock column name"),
8244
- expectedValue: z.union([
8245
- z.string().describe("Expected string version value"),
8246
- z.number().describe("Expected numeric version value")
8247
- ]).describe("Expected current value for the lock column")
8248
- }).describe("Optional optimistic lock condition");
8249
- var updateBatchOperationSchema = z.object({
8250
- data: updateDataSchema.describe("Columns and values to update"),
8251
- where: z.array(updateWhereConditionSchema).optional().describe("WHERE conditions (combined with AND)"),
8252
- allowFullTableUpdate: z.boolean().default(false).describe("Explicitly allow UPDATE without WHERE conditions for this operation"),
8253
- optimisticLock: updateOptimisticLockSchema.optional().describe("Optional optimistic locking condition for this operation")
8254
- }).superRefine((value, ctx) => {
8255
- const hasWhere = (value.where?.length ?? 0) > 0;
8256
- const hasOptimisticLock = !!value.optimisticLock;
8257
- if (!value.allowFullTableUpdate && !hasWhere && !hasOptimisticLock) {
8258
- ctx.addIssue({
8259
- code: z.ZodIssueCode.custom,
8260
- path: ["where"],
8261
- message: "WHERE conditions are required unless allowFullTableUpdate is true"
8262
- });
8263
- }
8264
- });
8265
- var updateBatchOptionsSchema = z.object({
8266
- enabled: z.boolean().optional().default(true).describe("Enable batched processing for operations[] payloads"),
8267
- batchSize: z.number().int().positive().max(5e3).optional().default(100).describe("Operations per batch chunk"),
8185
+ var insertBatchOptionsSchema = z.object({
8186
+ enabled: z.boolean().optional().default(true).describe("Enable chunked batch execution for array insert payloads"),
8187
+ batchSize: z.number().int().positive().max(5e3).optional().default(100).describe("Rows per batch chunk"),
8268
8188
  continueOnError: z.boolean().optional().default(true).describe("Continue processing remaining chunks when one chunk fails"),
8269
8189
  maxRetries: z.number().int().min(0).max(5).optional().default(0).describe("Retry attempts per failed chunk"),
8270
8190
  retryDelayMs: z.number().int().min(0).max(6e4).optional().default(0).describe("Delay between retry attempts in milliseconds"),
8271
8191
  benchmark: z.boolean().optional().default(false).describe("Run synthetic benchmark metadata comparing individual vs batched execution callbacks")
8272
8192
  });
8273
- var relationalUpdateSchema = z.object({
8193
+ var relationalInsertSchema = z.object({
8274
8194
  table: z.string().min(1, "Table name is required").regex(
8275
8195
  VALID_QUALIFIED_IDENTIFIER_PATTERN,
8276
8196
  "Table name contains invalid characters. Only alphanumeric characters, underscores, and dots (for schema qualification) are allowed."
8277
- ).describe("Table name to update (schema-qualified names supported, e.g. public.users)"),
8278
- data: updateDataSchema.optional().describe("Columns and values to update"),
8279
- where: z.array(updateWhereConditionSchema).optional().describe("WHERE conditions (combined with AND)"),
8280
- allowFullTableUpdate: z.boolean().default(false).describe("Explicitly allow UPDATE without WHERE conditions"),
8281
- optimisticLock: updateOptimisticLockSchema.optional().describe("Optional optimistic locking condition"),
8282
- operations: z.array(updateBatchOperationSchema).min(1, "operations must contain at least one batch item").optional().describe("Batch update operation list"),
8283
- batch: updateBatchOptionsSchema.optional().describe("Optional batch execution controls for operations payloads"),
8197
+ ).describe("Table name to insert into (schema-qualified names supported, e.g. public.users)"),
8198
+ data: z.union([
8199
+ insertRowSchema,
8200
+ z.array(insertRowSchema).min(1, "Insert data array must not be empty")
8201
+ ]).describe("Single row object or array of row objects to insert"),
8202
+ batch: insertBatchOptionsSchema.optional().describe("Optional batch execution controls for array insert payloads"),
8203
+ returning: insertReturningSchema.optional().describe("Optional RETURNING behavior"),
8284
8204
  vendor: z.enum(["postgresql", "mysql", "sqlite"]).describe("Database vendor"),
8285
8205
  connectionString: z.string().min(1, "Database connection string is required").describe("Database connection string")
8286
- }).superRefine((value, ctx) => {
8287
- const hasOperations = (value.operations?.length ?? 0) > 0;
8288
- if (hasOperations && value.data) {
8289
- ctx.addIssue({
8290
- code: z.ZodIssueCode.custom,
8291
- path: ["data"],
8292
- message: "data cannot be provided when operations[] is used"
8293
- });
8294
- }
8295
- if (!hasOperations && !value.data) {
8296
- ctx.addIssue({
8297
- code: z.ZodIssueCode.custom,
8298
- path: ["data"],
8299
- message: "data is required when operations[] is not provided"
8300
- });
8301
- }
8302
- if (hasOperations && (value.where?.length ?? 0) > 0) {
8303
- ctx.addIssue({
8304
- code: z.ZodIssueCode.custom,
8305
- path: ["where"],
8306
- message: "where cannot be provided when operations[] is used"
8307
- });
8308
- }
8309
- if (hasOperations && value.optimisticLock) {
8310
- ctx.addIssue({
8311
- code: z.ZodIssueCode.custom,
8312
- path: ["optimisticLock"],
8313
- message: "optimisticLock cannot be provided when operations[] is used"
8314
- });
8315
- }
8316
- if (hasOperations && value.allowFullTableUpdate) {
8317
- ctx.addIssue({
8318
- code: z.ZodIssueCode.custom,
8319
- path: ["allowFullTableUpdate"],
8320
- message: "allowFullTableUpdate cannot be provided when operations[] is used"
8321
- });
8322
- }
8323
- if (hasOperations) {
8324
- return;
8325
- }
8326
- const hasWhere = (value.where?.length ?? 0) > 0;
8327
- const hasOptimisticLock = !!value.optimisticLock;
8328
- if (!value.allowFullTableUpdate && !hasWhere && !hasOptimisticLock) {
8329
- ctx.addIssue({
8330
- code: z.ZodIssueCode.custom,
8331
- path: ["where"],
8332
- message: "WHERE conditions are required unless allowFullTableUpdate is true"
8333
- });
8334
- }
8335
8206
  });
8336
8207
 
8337
- // src/data/relational/tools/relational-update/error-utils.ts
8338
- var SAFE_UPDATE_VALIDATION_PATTERNS = [
8208
+ // src/data/relational/tools/relational-insert/error-utils.ts
8209
+ var SAFE_INSERT_VALIDATION_PATTERNS = [
8339
8210
  "must not be empty",
8340
8211
  "contains invalid characters",
8341
- "Update data must be an object",
8342
- "Update data must not be empty",
8212
+ "must be an object",
8213
+ "must not be an empty array",
8343
8214
  "must not be undefined",
8344
- "WHERE conditions are required for UPDATE queries",
8345
- "null is only allowed with isNull/isNotNull operators",
8346
- "operator requires a string or number value",
8347
- "operator requires a non-empty array value",
8348
- "operator requires a string value",
8349
- "must not include value",
8350
- "Optimistic lock expectedValue must not be empty",
8351
- "optimistic lock check failed",
8352
- "WHERE conditions are required unless allowFullTableUpdate is true"
8215
+ "idColumn can only be provided",
8216
+ "Returning full rows is not supported for mysql",
8217
+ "Batch INSERT with only DEFAULT VALUES is not supported"
8353
8218
  ];
8354
8219
  var CONSTRAINT_VIOLATION_PATTERNS2 = [
8355
8220
  {
8356
8221
  pattern: /(unique constraint|duplicate key|duplicate entry)/i,
8357
- message: "Update failed: unique constraint violation."
8222
+ message: "Insert failed: unique constraint violation."
8358
8223
  },
8359
8224
  {
8360
8225
  pattern: /(foreign key constraint|violates foreign key constraint)/i,
8361
- message: "Update failed: foreign key constraint violation."
8226
+ message: "Insert failed: foreign key constraint violation."
8362
8227
  },
8363
8228
  {
8364
8229
  pattern: /(not null constraint|cannot be null)/i,
8365
- message: "Update failed: NOT NULL constraint violation."
8230
+ message: "Insert failed: NOT NULL constraint violation."
8366
8231
  }
8367
8232
  ];
8368
- function isSafeUpdateValidationError(error) {
8233
+ function isSafeInsertValidationError(error) {
8369
8234
  if (!(error instanceof Error)) {
8370
8235
  return false;
8371
8236
  }
8372
- return SAFE_UPDATE_VALIDATION_PATTERNS.some((pattern) => error.message.includes(pattern));
8237
+ return SAFE_INSERT_VALIDATION_PATTERNS.some((pattern) => error.message.includes(pattern));
8373
8238
  }
8374
- function getUpdateConstraintViolationMessage(error) {
8239
+ function getConstraintViolationMessage(error) {
8375
8240
  if (!(error instanceof Error)) {
8376
8241
  return null;
8377
8242
  }
@@ -8386,35 +8251,74 @@ function getUpdateConstraintViolationMessage(error) {
8386
8251
  }
8387
8252
  return null;
8388
8253
  }
8389
- function isSafeUpdateError(error) {
8254
+ function isSafeInsertError(error) {
8390
8255
  if (!(error instanceof Error)) {
8391
8256
  return false;
8392
8257
  }
8393
- return isSafeUpdateValidationError(error) || getUpdateConstraintViolationMessage(error) !== null;
8258
+ return isSafeInsertValidationError(error) || getConstraintViolationMessage(error) !== null;
8394
8259
  }
8395
- var updateExecutorLogger = createLogger("agentforge:tools:data:relational:update");
8260
+ var insertExecutorLogger = createLogger("agentforge:tools:data:relational:insert");
8396
8261
  function toNumber2(value) {
8397
8262
  return typeof value === "number" && Number.isFinite(value) ? value : void 0;
8398
8263
  }
8399
- function normalizeAffectedRows(result) {
8264
+ function isPlainObject(value) {
8265
+ return !!value && typeof value === "object" && !Array.isArray(value);
8266
+ }
8267
+ function normalizeExecutionResult(result) {
8400
8268
  if (Array.isArray(result)) {
8401
- if (result.length > 0 && result[0] && typeof result[0] === "object") {
8269
+ if (result.length > 0 && isPlainObject(result[0])) {
8402
8270
  const first = result[0];
8403
8271
  const affectedRows = toNumber2(first.affectedRows) ?? toNumber2(first.rowCount) ?? toNumber2(first.changes);
8404
- if (affectedRows !== void 0) {
8405
- return affectedRows;
8272
+ const insertId = toNumber2(first.insertId);
8273
+ const lastInsertRowid = toNumber2(first.lastInsertRowid);
8274
+ if (affectedRows !== void 0 || insertId !== void 0 || lastInsertRowid !== void 0) {
8275
+ return {
8276
+ rows: [],
8277
+ rowCount: affectedRows ?? 0,
8278
+ insertId,
8279
+ lastInsertRowid
8280
+ };
8406
8281
  }
8407
8282
  }
8408
- return result.length;
8283
+ return {
8284
+ rows: result,
8285
+ rowCount: result.length
8286
+ };
8409
8287
  }
8410
- if (result && typeof result === "object") {
8411
- const resultRecord = result;
8412
- return toNumber2(resultRecord.rowCount) ?? toNumber2(resultRecord.affectedRows) ?? toNumber2(resultRecord.changes) ?? (Array.isArray(resultRecord.rows) ? resultRecord.rows.length : 0);
8288
+ if (isPlainObject(result)) {
8289
+ const rows = Array.isArray(result.rows) ? result.rows : [];
8290
+ const rowCount = toNumber2(result.rowCount) ?? toNumber2(result.affectedRows) ?? toNumber2(result.changes) ?? rows.length;
8291
+ return {
8292
+ rows,
8293
+ rowCount,
8294
+ insertId: toNumber2(result.insertId),
8295
+ lastInsertRowid: toNumber2(result.lastInsertRowid)
8296
+ };
8413
8297
  }
8414
- return 0;
8298
+ return {
8299
+ rows: [],
8300
+ rowCount: 0
8301
+ };
8415
8302
  }
8416
- function toErrorMessage(error) {
8417
- return error instanceof Error ? error.message : String(error);
8303
+ function deriveInsertedIds(options) {
8304
+ const { idColumn, inputRows, returnedRows, rowCount, insertId, lastInsertRowid } = options;
8305
+ if (returnedRows.length > 0) {
8306
+ const idsFromReturn = returnedRows.map((row) => isPlainObject(row) ? row[idColumn] : void 0).filter((id) => typeof id === "number" || typeof id === "string");
8307
+ if (idsFromReturn.length > 0) {
8308
+ return idsFromReturn;
8309
+ }
8310
+ }
8311
+ if (inputRows.every((row) => typeof row[idColumn] === "number" || typeof row[idColumn] === "string")) {
8312
+ return inputRows.map((row) => row[idColumn]);
8313
+ }
8314
+ if (insertId !== void 0 && rowCount > 0) {
8315
+ return Array.from({ length: rowCount }, (_, index) => insertId + index);
8316
+ }
8317
+ if (lastInsertRowid !== void 0 && rowCount > 0) {
8318
+ const startId = lastInsertRowid - rowCount + 1;
8319
+ return Array.from({ length: rowCount }, (_, index) => startId + index);
8320
+ }
8321
+ return [];
8418
8322
  }
8419
8323
  function resolveBatchOptions2(batch) {
8420
8324
  if (!batch || batch.enabled === false) {
@@ -8429,88 +8333,79 @@ function resolveBatchOptions2(batch) {
8429
8333
  benchmark: batch.benchmark ?? false
8430
8334
  };
8431
8335
  }
8432
- function toSingleUpdateOperation(input) {
8433
- if (!input.data) {
8434
- throw new Error("UPDATE data is required when operations[] is not provided.");
8435
- }
8436
- return {
8437
- data: input.data,
8438
- where: input.where,
8439
- allowFullTableUpdate: input.allowFullTableUpdate,
8440
- optimisticLock: input.optimisticLock
8441
- };
8336
+ function toSingleInsertRows(row) {
8337
+ return [row];
8442
8338
  }
8443
8339
 
8444
- // src/data/relational/tools/relational-update/executor-single.ts
8445
- async function executeSingleUpdate(manager, input, operation, context) {
8446
- const built = buildUpdateQuery({
8340
+ // src/data/relational/tools/relational-insert/executor-single.ts
8341
+ async function executeInsertOnce(executor, input, context) {
8342
+ const built = buildInsertQuery({
8447
8343
  table: input.table,
8448
- data: operation.data,
8449
- where: operation.where,
8450
- allowFullTableUpdate: operation.allowFullTableUpdate,
8451
- optimisticLock: operation.optimisticLock,
8344
+ data: input.data,
8345
+ returning: input.returning,
8452
8346
  vendor: input.vendor
8453
8347
  });
8454
- const executor = manager;
8455
- const rawResult = await executor.execute(built.query);
8456
- const rowCount = normalizeAffectedRows(rawResult);
8457
- if (built.usesOptimisticLock && rowCount === 0) {
8458
- throw new Error("Update failed: optimistic lock check failed.");
8348
+ const session = context?.transaction ?? executor;
8349
+ if (Array.isArray(built.query)) {
8350
+ let totalRowCount = 0;
8351
+ const allRows = [];
8352
+ const allIds = [];
8353
+ for (const singleQuery of built.query) {
8354
+ const rawResult2 = await session.execute(singleQuery);
8355
+ const normalized2 = normalizeExecutionResult(rawResult2);
8356
+ totalRowCount += normalized2.rowCount > 0 ? normalized2.rowCount : 1;
8357
+ if (built.returningMode === "row") {
8358
+ allRows.push(...normalized2.rows);
8359
+ }
8360
+ if (built.returningMode === "id") {
8361
+ const ids = deriveInsertedIds({
8362
+ idColumn: built.idColumn,
8363
+ inputRows: toSingleInsertRows(built.rows[allIds.length]),
8364
+ returnedRows: normalized2.rows,
8365
+ rowCount: 1,
8366
+ insertId: normalized2.insertId,
8367
+ lastInsertRowid: normalized2.lastInsertRowid
8368
+ });
8369
+ allIds.push(...ids);
8370
+ }
8371
+ }
8372
+ return {
8373
+ rowCount: totalRowCount,
8374
+ rows: allRows,
8375
+ insertedIds: allIds,
8376
+ executionTime: 0
8377
+ };
8459
8378
  }
8379
+ const rawResult = await session.execute(built.query);
8380
+ const normalized = normalizeExecutionResult(rawResult);
8381
+ const rowCount = normalized.rowCount > 0 ? normalized.rowCount : built.rows.length;
8382
+ const rows = built.returningMode === "row" ? normalized.rows : [];
8383
+ const insertedIds = built.returningMode === "id" ? deriveInsertedIds({
8384
+ idColumn: built.idColumn,
8385
+ inputRows: built.rows,
8386
+ returnedRows: normalized.rows,
8387
+ rowCount,
8388
+ insertId: normalized.insertId,
8389
+ lastInsertRowid: normalized.lastInsertRowid
8390
+ }) : [];
8460
8391
  return {
8461
8392
  rowCount,
8393
+ rows,
8394
+ insertedIds,
8462
8395
  executionTime: 0
8463
8396
  };
8464
8397
  }
8465
8398
 
8466
- // src/data/relational/tools/relational-update/executor-batch.ts
8467
- async function executeUpdateInBatchMode(manager, input, context, options) {
8399
+ // src/data/relational/tools/relational-insert/executor-batch.ts
8400
+ async function executeInsertInBatchMode(executor, input, context, options) {
8468
8401
  const batchResult = await executeBatchedTask(
8469
8402
  {
8470
- operation: "update",
8471
- items: input.operations,
8472
- executeBatch: async (operations, batchIndex) => {
8473
- let rowCount2 = 0;
8474
- let successfulItems = 0;
8475
- let failedItems = 0;
8476
- const failures = [];
8477
- for (const operation of operations) {
8478
- try {
8479
- const result = await executeSingleUpdate(
8480
- manager,
8481
- input,
8482
- {
8483
- data: operation.data,
8484
- where: operation.where,
8485
- allowFullTableUpdate: operation.allowFullTableUpdate,
8486
- optimisticLock: operation.optimisticLock
8487
- },
8488
- context
8489
- );
8490
- rowCount2 += result.rowCount;
8491
- successfulItems += 1;
8492
- } catch (error) {
8493
- if (!options.continueOnError) {
8494
- throw error;
8495
- }
8496
- failedItems += 1;
8497
- failures.push({
8498
- operation: "update",
8499
- batchIndex,
8500
- batchSize: operations.length,
8501
- attempts: 1,
8502
- error: toErrorMessage(error)
8503
- });
8504
- }
8505
- }
8506
- return {
8507
- rowCount: rowCount2,
8508
- successfulItems,
8509
- failedItems,
8510
- failures
8511
- };
8512
- },
8513
- getBatchSuccessCount: (result) => result.successfulItems
8403
+ operation: "insert",
8404
+ items: input.data,
8405
+ executeBatch: async (rows2) => executeInsertOnce(executor, {
8406
+ ...input,
8407
+ data: rows2}, context),
8408
+ getBatchSuccessCount: (result, rows2) => Math.min(result.rowCount, rows2.length)
8514
8409
  },
8515
8410
  {
8516
8411
  batchSize: options.batchSize,
@@ -8518,7 +8413,7 @@ async function executeUpdateInBatchMode(manager, input, context, options) {
8518
8413
  maxRetries: options.maxRetries,
8519
8414
  retryDelayMs: options.retryDelayMs,
8520
8415
  onProgress: (progress) => {
8521
- updateExecutorLogger.debug("UPDATE batch progress", {
8416
+ insertExecutorLogger.debug("INSERT batch progress", {
8522
8417
  vendor: input.vendor,
8523
8418
  table: input.table,
8524
8419
  ...progress
@@ -8527,165 +8422,601 @@ async function executeUpdateInBatchMode(manager, input, context, options) {
8527
8422
  }
8528
8423
  );
8529
8424
  const rowCount = batchResult.results.reduce((total, result) => total + result.rowCount, 0);
8530
- const operationFailures = batchResult.results.flatMap((result) => result.failures);
8425
+ const rows = batchResult.results.flatMap((result) => result.rows);
8426
+ const insertedIds = batchResult.results.flatMap((result) => result.insertedIds);
8531
8427
  let benchmark;
8532
8428
  if (options.benchmark) {
8533
- updateExecutorLogger.warn("UPDATE batch benchmark enabled. Synthetic benchmark callbacks are side-effect free and do not execute SQL.", {
8429
+ insertExecutorLogger.warn("INSERT batch benchmark enabled. Synthetic benchmark callbacks are side-effect free and do not execute SQL.", {
8534
8430
  vendor: input.vendor,
8535
8431
  table: input.table,
8536
- operationCount: input.operations.length,
8432
+ totalRows: input.data.length,
8537
8433
  batchSize: options.batchSize
8538
8434
  });
8539
- benchmark = await benchmarkBatchExecution({
8540
- items: input.operations,
8541
- batchSize: options.batchSize,
8542
- runIndividual: async () => {
8543
- },
8544
- runBatch: async () => {
8545
- }
8435
+ benchmark = await benchmarkBatchExecution({
8436
+ items: input.data,
8437
+ batchSize: options.batchSize,
8438
+ runIndividual: async () => {
8439
+ },
8440
+ runBatch: async () => {
8441
+ }
8442
+ });
8443
+ }
8444
+ return {
8445
+ rowCount,
8446
+ rows,
8447
+ insertedIds,
8448
+ executionTime: batchResult.executionTime,
8449
+ batch: {
8450
+ enabled: true,
8451
+ batchSize: options.batchSize,
8452
+ totalItems: batchResult.totalItems,
8453
+ processedItems: batchResult.processedItems,
8454
+ successfulItems: batchResult.successfulItems,
8455
+ failedItems: batchResult.failedItems,
8456
+ totalBatches: batchResult.totalBatches,
8457
+ retries: batchResult.retries,
8458
+ partialSuccess: batchResult.partialSuccess,
8459
+ failures: batchResult.failures,
8460
+ benchmark
8461
+ }
8462
+ };
8463
+ }
8464
+
8465
+ // src/data/relational/tools/relational-insert/executor.ts
8466
+ function getCauseMetadata(error) {
8467
+ if (!(error instanceof Error) || !error.cause || typeof error.cause !== "object") {
8468
+ return {};
8469
+ }
8470
+ const cause = error.cause;
8471
+ return {
8472
+ causeType: error.cause instanceof Error ? error.cause.name : "object",
8473
+ ...typeof cause.code === "string" ? { causeCode: cause.code } : {},
8474
+ ...typeof cause.constraint === "string" ? { causeConstraint: cause.constraint } : {}
8475
+ };
8476
+ }
8477
+ async function executeInsert(executor, input, context) {
8478
+ const startTime = Date.now();
8479
+ insertExecutorLogger.debug("Building INSERT query", {
8480
+ vendor: input.vendor,
8481
+ table: input.table,
8482
+ isBatch: Array.isArray(input.data),
8483
+ batchModeEnabled: !!input.batch?.enabled,
8484
+ returningMode: input.returning?.mode ?? "none"
8485
+ });
8486
+ try {
8487
+ const batchOptions = resolveBatchOptions2(input.batch);
8488
+ const result = Array.isArray(input.data) && batchOptions ? await executeInsertInBatchMode(
8489
+ executor,
8490
+ input,
8491
+ context,
8492
+ batchOptions
8493
+ ) : await executeInsertOnce(executor, input, context);
8494
+ const executionTime = Date.now() - startTime;
8495
+ insertExecutorLogger.debug("INSERT query executed successfully", {
8496
+ vendor: input.vendor,
8497
+ table: input.table,
8498
+ rowCount: result.rowCount,
8499
+ returnedRows: result.rows.length,
8500
+ returnedIds: result.insertedIds.length,
8501
+ executionTime,
8502
+ batchMode: !!result.batch,
8503
+ partialSuccess: result.batch?.partialSuccess ?? false
8504
+ });
8505
+ return {
8506
+ ...result,
8507
+ executionTime
8508
+ };
8509
+ } catch (error) {
8510
+ const executionTime = Date.now() - startTime;
8511
+ insertExecutorLogger.error("INSERT query execution failed", {
8512
+ vendor: input.vendor,
8513
+ table: input.table,
8514
+ error: error instanceof Error ? error.message : String(error),
8515
+ ...getCauseMetadata(error),
8516
+ executionTime
8517
+ });
8518
+ const constraintMessage = getConstraintViolationMessage(error);
8519
+ if (constraintMessage) {
8520
+ throw new Error(constraintMessage, { cause: error });
8521
+ }
8522
+ if (isSafeInsertValidationError(error)) {
8523
+ throw error;
8524
+ }
8525
+ throw new Error("INSERT query failed. See logs for details.", { cause: error });
8526
+ }
8527
+ }
8528
+
8529
+ // src/data/relational/tools/relational-insert/index.ts
8530
+ function toInsertErrorResponse(error) {
8531
+ const errorMessage = isSafeInsertError(error) ? error.message : "Failed to execute INSERT query. Please verify your input and database connection.";
8532
+ return {
8533
+ success: false,
8534
+ error: errorMessage,
8535
+ rowCount: 0,
8536
+ insertedIds: [],
8537
+ rows: []
8538
+ };
8539
+ }
8540
+ async function invokeRelationalInsert(execution, input) {
8541
+ try {
8542
+ const result = await executeInsert(
8543
+ execution.executor,
8544
+ { ...input, vendor: execution.vendor },
8545
+ execution.transaction ? { transaction: execution.transaction } : void 0
8546
+ );
8547
+ return {
8548
+ success: true,
8549
+ rowCount: result.rowCount,
8550
+ insertedIds: result.insertedIds,
8551
+ rows: result.rows,
8552
+ executionTime: result.executionTime,
8553
+ batch: result.batch
8554
+ };
8555
+ } catch (error) {
8556
+ return toInsertErrorResponse(error);
8557
+ }
8558
+ }
8559
+ var relationalInsert = toolBuilder().name("relational-insert").displayName("Relational INSERT").description("Execute type-safe INSERT queries with single-row and batch insert support").category(ToolCategory.DATABASE).tags(["database", "sql", "insert", "postgresql", "mysql", "sqlite"]).schema(relationalInsertSchema).example({
8560
+ description: "Insert a single row and return generated ID",
8561
+ input: {
8562
+ table: "users",
8563
+ data: { name: "Alice", email: "alice@example.com" },
8564
+ returning: { mode: "id", idColumn: "id" },
8565
+ vendor: "postgresql",
8566
+ connectionString: "postgresql://localhost/mydb"
8567
+ }
8568
+ }).example({
8569
+ description: "Batch insert rows",
8570
+ input: {
8571
+ table: "users",
8572
+ data: [
8573
+ { name: "Bob", email: "bob@example.com" },
8574
+ { name: "Carol", email: "carol@example.com" }
8575
+ ],
8576
+ batch: {
8577
+ batchSize: 250,
8578
+ continueOnError: true,
8579
+ maxRetries: 1
8580
+ },
8581
+ vendor: "sqlite",
8582
+ connectionString: "data.db"
8583
+ }
8584
+ }).implement(async (input) => {
8585
+ const { connectionString, vendor, ...operation } = input;
8586
+ return withEphemeralRelationalMutationToolSet(
8587
+ { vendor, connectionString },
8588
+ async (toolSet) => replaceMutationConnectionFailure(
8589
+ await toolSet.insert.invoke(operation),
8590
+ INSERT_CONNECTION_FAILURE,
8591
+ "Failed to execute INSERT query. Please verify your input and database connection."
8592
+ ),
8593
+ toInsertErrorResponse
8594
+ );
8595
+ }).build();
8596
+ var relationalQuerySchema = z.object({
8597
+ sql: z.string().describe("SQL query string to execute"),
8598
+ params: z.union([
8599
+ z.array(z.unknown()).describe("Positional parameters (e.g., [value1, value2])"),
8600
+ z.record(z.string(), z.unknown()).describe('Named parameters (e.g., { name: "John", age: 30 })')
8601
+ ]).optional().describe("Query parameters for parameter binding (prevents SQL injection)"),
8602
+ vendor: z.enum(["postgresql", "mysql", "sqlite"]).describe("Database vendor"),
8603
+ connectionString: z.string().min(1, "Database connection string is required").describe("Database connection string used to create a new connection")
8604
+ });
8605
+ function toQueryErrorResponse(error) {
8606
+ return {
8607
+ success: false,
8608
+ error: error instanceof Error ? error.message : "Unknown error occurred",
8609
+ rows: [],
8610
+ rowCount: 0
8611
+ };
8612
+ }
8613
+ async function invokeRelationalQuery(execution, input) {
8614
+ try {
8615
+ const queryInput = {
8616
+ sql: input.sql,
8617
+ params: input.params,
8618
+ vendor: execution.vendor
8619
+ };
8620
+ const result = execution.transaction ? await executeQuery2(execution.executor, queryInput, { transaction: execution.transaction }) : await executeQuery2(execution.executor, queryInput);
8621
+ return {
8622
+ success: true,
8623
+ rows: result.rows,
8624
+ rowCount: result.rowCount,
8625
+ executionTime: result.executionTime
8626
+ };
8627
+ } catch (error) {
8628
+ return toQueryErrorResponse(error);
8629
+ }
8630
+ }
8631
+ var relationalQuery = toolBuilder().name("relational-query").displayName("Relational Query").description("Execute raw SQL queries against relational databases (PostgreSQL, MySQL, SQLite) with parameter binding to prevent SQL injection").category(ToolCategory.DATABASE).tags(["database", "sql", "query", "postgresql", "mysql", "sqlite"]).schema(relationalQuerySchema).example({
8632
+ description: "SELECT query with positional parameters",
8633
+ input: {
8634
+ sql: "SELECT * FROM users WHERE id = $1",
8635
+ params: [42],
8636
+ vendor: "postgresql",
8637
+ connectionString: "postgresql://user:pass@localhost:5432/mydb"
8638
+ }
8639
+ }).example({
8640
+ description: "INSERT query with named parameters",
8641
+ input: {
8642
+ sql: "INSERT INTO users (name, email) VALUES (:name, :email)",
8643
+ params: { name: "John Doe", email: "john@example.com" },
8644
+ vendor: "postgresql",
8645
+ connectionString: "postgresql://user:pass@localhost:5432/mydb"
8646
+ }
8647
+ }).example({
8648
+ description: "UPDATE query with positional parameters",
8649
+ input: {
8650
+ sql: "UPDATE users SET status = ? WHERE id = ?",
8651
+ params: ["active", 42],
8652
+ vendor: "mysql",
8653
+ connectionString: "mysql://user:pass@localhost:3306/mydb"
8654
+ }
8655
+ }).usageNotes("Always use parameter binding (params) instead of string concatenation to prevent SQL injection. Supports positional ($1, ?) and named (:name) parameters.").limitation("Requires database-specific driver as peer dependency (pg, mysql2, or better-sqlite3)").limitation("Connection string must be valid for the specified vendor").limitation("Large result sets may impact performance").limitation("Placeholder characters ($n, ?, :name) inside SQL string literals are not supported. Use parameter binding for all dynamic values instead of embedding them in string literals").implement(async (input) => {
8656
+ const { connectionString, vendor, ...operation } = input;
8657
+ return withEphemeralRelationalToolSet(
8658
+ { vendor, connectionString },
8659
+ async (toolSet) => replaceConnectionFailureMessage(
8660
+ await toolSet.query.invoke(operation),
8661
+ QUERY_CONNECTION_FAILURE,
8662
+ `Failed to initialize ${vendor} connection`
8663
+ )
8664
+ );
8665
+ }).build();
8666
+ var whereOperatorSchema = z.enum([
8667
+ "eq",
8668
+ // equals
8669
+ "ne",
8670
+ // not equals
8671
+ "gt",
8672
+ // greater than
8673
+ "lt",
8674
+ // less than
8675
+ "gte",
8676
+ // greater than or equal
8677
+ "lte",
8678
+ // less than or equal
8679
+ "like",
8680
+ // LIKE pattern matching
8681
+ "in",
8682
+ // IN array
8683
+ "notIn",
8684
+ // NOT IN array
8685
+ "isNull",
8686
+ // IS NULL
8687
+ "isNotNull"
8688
+ // IS NOT NULL
8689
+ ]);
8690
+ var whereConditionSchema = z.object({
8691
+ column: z.string().min(1, "Column name must not be empty").describe("Column name to filter on"),
8692
+ operator: whereOperatorSchema.describe("Comparison operator"),
8693
+ value: z.union([
8694
+ z.string(),
8695
+ z.number(),
8696
+ z.boolean(),
8697
+ z.array(z.union([z.string(), z.number()])),
8698
+ z.null()
8699
+ ]).optional().describe("Value to compare against (not required for isNull/isNotNull)")
8700
+ }).superRefine((val, ctx) => {
8701
+ const op = val.operator;
8702
+ if (op === "isNull" || op === "isNotNull") {
8703
+ if (val.value !== void 0) {
8704
+ ctx.addIssue({
8705
+ code: z.ZodIssueCode.custom,
8706
+ path: ["value"],
8707
+ message: "value must not be provided when using isNull or isNotNull operator"
8708
+ });
8709
+ }
8710
+ return;
8711
+ }
8712
+ if (val.value === void 0) {
8713
+ ctx.addIssue({
8714
+ code: z.ZodIssueCode.custom,
8715
+ path: ["value"],
8716
+ message: "value is required for this operator"
8717
+ });
8718
+ return;
8719
+ }
8720
+ if (op === "in" || op === "notIn") {
8721
+ if (!Array.isArray(val.value)) {
8722
+ ctx.addIssue({
8723
+ code: z.ZodIssueCode.custom,
8724
+ path: ["value"],
8725
+ message: `${op === "in" ? "IN" : "NOT IN"} operator requires an array value`
8726
+ });
8727
+ } else if (val.value.length === 0) {
8728
+ ctx.addIssue({
8729
+ code: z.ZodIssueCode.custom,
8730
+ path: ["value"],
8731
+ message: `${op === "in" ? "IN" : "NOT IN"} operator requires a non-empty array value`
8732
+ });
8733
+ }
8734
+ return;
8735
+ }
8736
+ if (val.value === null) {
8737
+ ctx.addIssue({
8738
+ code: z.ZodIssueCode.custom,
8739
+ path: ["value"],
8740
+ message: "null is only allowed with isNull/isNotNull operators"
8741
+ });
8742
+ return;
8743
+ }
8744
+ if (op === "like" && typeof val.value !== "string") {
8745
+ ctx.addIssue({
8746
+ code: z.ZodIssueCode.custom,
8747
+ path: ["value"],
8748
+ message: "LIKE operator requires a string value"
8749
+ });
8750
+ return;
8751
+ }
8752
+ if ((op === "gt" || op === "lt" || op === "gte" || op === "lte") && typeof val.value !== "string" && typeof val.value !== "number") {
8753
+ ctx.addIssue({
8754
+ code: z.ZodIssueCode.custom,
8755
+ path: ["value"],
8756
+ message: `${op.toUpperCase()} operator requires a string or number value`
8546
8757
  });
8758
+ return;
8547
8759
  }
8548
- return {
8549
- rowCount,
8550
- executionTime: batchResult.executionTime,
8551
- batch: {
8552
- enabled: true,
8553
- batchSize: options.batchSize,
8554
- totalItems: batchResult.totalItems,
8555
- processedItems: batchResult.processedItems,
8556
- successfulItems: batchResult.successfulItems,
8557
- failedItems: batchResult.failedItems,
8558
- totalBatches: batchResult.totalBatches,
8559
- retries: batchResult.retries,
8560
- partialSuccess: batchResult.partialSuccess,
8561
- failures: [...batchResult.failures, ...operationFailures],
8562
- benchmark
8563
- }
8760
+ if ((op === "eq" || op === "ne") && Array.isArray(val.value)) {
8761
+ ctx.addIssue({
8762
+ code: z.ZodIssueCode.custom,
8763
+ path: ["value"],
8764
+ message: `${op.toUpperCase()} operator requires a scalar value`
8765
+ });
8766
+ }
8767
+ });
8768
+ var orderDirectionSchema = z.enum(["asc", "desc"]);
8769
+ var orderBySchema = z.object({
8770
+ column: z.string().min(1, "Column name must not be empty").describe("Column name to order by"),
8771
+ direction: orderDirectionSchema.describe("Sort direction (ascending or descending)")
8772
+ });
8773
+ var streamingOptionsSchema = z.object({
8774
+ enabled: z.boolean().default(true).describe("Enable chunked streaming mode for large result sets"),
8775
+ chunkSize: z.number().int().min(1).max(5e3).optional().describe("Rows fetched per chunk (default: 100)"),
8776
+ maxRows: z.number().int().positive().optional().describe("Optional cap on streamed rows"),
8777
+ sampleSize: z.number().int().min(0).max(5e3).optional().describe("Number of rows to include in the response payload"),
8778
+ benchmark: z.boolean().optional().default(false).describe("Run memory benchmark comparing regular vs streaming execution (adds two extra query executions; use side-effect-free SELECT statements)")
8779
+ });
8780
+ var relationalSelectSchema = z.object({
8781
+ table: z.string().min(1, "Table name is required").regex(
8782
+ VALID_QUALIFIED_IDENTIFIER_PATTERN,
8783
+ "Table name contains invalid characters. Only alphanumeric characters, underscores, and dots (for schema qualification) are allowed."
8784
+ ).describe("Table name to select from (schema-qualified names supported, e.g. public.users)"),
8785
+ columns: z.array(z.string().min(1, "Column name must not be empty")).min(1, "Columns array must not be empty").optional().describe("Columns to select (omit for SELECT *)"),
8786
+ where: z.array(whereConditionSchema).optional().describe("WHERE conditions (combined with AND)"),
8787
+ orderBy: z.array(orderBySchema).optional().describe("ORDER BY clauses"),
8788
+ limit: z.number().int().positive().optional().describe("Maximum number of rows to return"),
8789
+ offset: z.number().int().nonnegative().optional().describe("Number of rows to skip"),
8790
+ streaming: streamingOptionsSchema.optional().describe("Optional streaming mode configuration"),
8791
+ vendor: z.enum(["postgresql", "mysql", "sqlite"]).describe("Database vendor"),
8792
+ connectionString: z.string().min(1, "Database connection string is required").describe("Database connection string")
8793
+ });
8794
+
8795
+ // src/data/relational/tools/relational-select/query-builder.ts
8796
+ function buildSelectQuery2(input) {
8797
+ const queryInput = {
8798
+ table: input.table,
8799
+ columns: input.columns,
8800
+ where: input.where,
8801
+ orderBy: input.orderBy,
8802
+ limit: input.limit,
8803
+ offset: input.offset,
8804
+ vendor: input.vendor
8564
8805
  };
8806
+ return buildSelectQuery(queryInput);
8565
8807
  }
8566
8808
 
8567
- // src/data/relational/tools/relational-update/executor.ts
8568
- async function executeUpdate(manager, input, context) {
8809
+ // src/data/relational/tools/relational-select/error-utils.ts
8810
+ var SAFE_VALIDATION_ERROR_PATTERNS2 = [
8811
+ "must not be empty",
8812
+ "contains invalid characters",
8813
+ "requires an array value",
8814
+ "requires a non-empty array value",
8815
+ "must be an integer between",
8816
+ "must be a positive integer",
8817
+ "null is only allowed with isNull/isNotNull operators",
8818
+ "LIKE operator requires a string value",
8819
+ "operator requires a string or number value",
8820
+ "operator requires a scalar value",
8821
+ "Stream cancelled by caller"
8822
+ ];
8823
+ function isSafeValidationError(error) {
8824
+ if (!(error instanceof Error)) {
8825
+ return false;
8826
+ }
8827
+ return SAFE_VALIDATION_ERROR_PATTERNS2.some(
8828
+ (pattern) => error.message.includes(pattern)
8829
+ );
8830
+ }
8831
+
8832
+ // src/data/relational/tools/relational-select/executor.ts
8833
+ var logger17 = createLogger("agentforge:tools:data:relational:select");
8834
+ function toSelectQueryInput(input) {
8835
+ return {
8836
+ table: input.table,
8837
+ columns: input.columns,
8838
+ where: input.where,
8839
+ orderBy: input.orderBy,
8840
+ limit: input.limit,
8841
+ offset: input.offset,
8842
+ vendor: input.vendor
8843
+ };
8844
+ }
8845
+ async function executeSelect(executor, input, context) {
8569
8846
  const startTime = Date.now();
8570
- updateExecutorLogger.debug("Building UPDATE query", {
8847
+ logger17.debug("Building SELECT query", {
8571
8848
  vendor: input.vendor,
8572
8849
  table: input.table,
8573
- hasWhere: !!input.where?.length,
8574
- ...input.allowFullTableUpdate !== void 0 ? { allowFullTableUpdate: input.allowFullTableUpdate } : {},
8575
- hasOptimisticLock: !!input.optimisticLock,
8576
- operationCount: input.operations?.length ?? 0,
8577
- batchModeEnabled: !!input.batch?.enabled
8850
+ hasWhere: !!input.where,
8851
+ hasOrderBy: !!input.orderBy,
8852
+ hasLimit: !!input.limit,
8853
+ streamingEnabled: !!input.streaming?.enabled
8578
8854
  });
8579
8855
  try {
8580
- const batchOptions = resolveBatchOptions2(input.batch);
8581
- const result = input.operations && batchOptions ? await executeUpdateInBatchMode(
8582
- manager,
8583
- input,
8584
- context,
8585
- batchOptions
8586
- ) : await executeSingleUpdate(manager, input, toSingleUpdateOperation(input), context);
8856
+ const session = context?.transaction ?? executor;
8857
+ if (input.streaming?.enabled) {
8858
+ const streamOptions = {
8859
+ chunkSize: input.streaming.chunkSize,
8860
+ maxRows: input.streaming.maxRows,
8861
+ sampleSize: input.streaming.sampleSize
8862
+ };
8863
+ const streamInput = toSelectQueryInput(input);
8864
+ if (input.streaming.benchmark) {
8865
+ logger17.warn("Streaming benchmark enabled; SELECT will execute up to three times (result + benchmark regular + benchmark streaming).", {
8866
+ vendor: input.vendor,
8867
+ table: input.table,
8868
+ chunkSize: streamOptions.chunkSize ?? DEFAULT_CHUNK_SIZE,
8869
+ ...streamOptions.maxRows !== void 0 ? { maxRows: streamOptions.maxRows } : {},
8870
+ ...streamOptions.sampleSize !== void 0 ? { sampleSize: streamOptions.sampleSize } : {}
8871
+ });
8872
+ }
8873
+ const streamResult = await executeStreamingSelect(session, streamInput, streamOptions);
8874
+ const benchmark = input.streaming.benchmark ? await benchmarkStreamingSelectMemory(session, streamInput, streamOptions) : void 0;
8875
+ const executionTime2 = Date.now() - startTime;
8876
+ logger17.debug("Streaming SELECT query executed successfully", {
8877
+ vendor: input.vendor,
8878
+ table: input.table,
8879
+ rowCount: streamResult.rowCount,
8880
+ chunkCount: streamResult.chunkCount,
8881
+ executionTime: executionTime2,
8882
+ cancelled: streamResult.cancelled
8883
+ });
8884
+ return {
8885
+ rows: streamResult.rows,
8886
+ rowCount: streamResult.rowCount,
8887
+ executionTime: executionTime2,
8888
+ streaming: {
8889
+ enabled: true,
8890
+ chunkSize: input.streaming.chunkSize ?? DEFAULT_CHUNK_SIZE,
8891
+ chunkCount: streamResult.chunkCount,
8892
+ sampledRowCount: streamResult.rows.length,
8893
+ streamedRowCount: streamResult.rowCount,
8894
+ cancelled: streamResult.cancelled,
8895
+ memoryUsage: streamResult.memoryUsage,
8896
+ benchmark
8897
+ }
8898
+ };
8899
+ }
8900
+ const query = buildSelectQuery2(input);
8901
+ const result = await session.execute(query);
8587
8902
  const executionTime = Date.now() - startTime;
8588
- updateExecutorLogger.debug("UPDATE query executed successfully", {
8903
+ const rows = Array.isArray(result) ? result : result.rows || [];
8904
+ const rowCount = rows.length;
8905
+ logger17.debug("SELECT query executed successfully", {
8589
8906
  vendor: input.vendor,
8590
8907
  table: input.table,
8591
- rowCount: result.rowCount,
8592
- executionTime,
8593
- batchMode: !!result.batch,
8594
- partialSuccess: result.batch?.partialSuccess ?? false
8908
+ rowCount,
8909
+ executionTime
8595
8910
  });
8596
8911
  return {
8597
- ...result,
8912
+ rows,
8913
+ rowCount,
8598
8914
  executionTime
8599
8915
  };
8600
8916
  } catch (error) {
8601
8917
  const executionTime = Date.now() - startTime;
8602
- updateExecutorLogger.error("UPDATE query execution failed", {
8918
+ logger17.error("SELECT query execution failed", {
8603
8919
  vendor: input.vendor,
8604
8920
  table: input.table,
8605
8921
  error: error instanceof Error ? error.message : String(error),
8606
8922
  executionTime
8607
8923
  });
8608
- const constraintMessage = getUpdateConstraintViolationMessage(error);
8609
- if (constraintMessage) {
8610
- throw new Error(constraintMessage, { cause: error });
8611
- }
8612
- if (isSafeUpdateValidationError(error)) {
8924
+ if (isSafeValidationError(error)) {
8613
8925
  throw error;
8614
8926
  }
8615
- throw new Error("UPDATE query failed. See logs for details.", { cause: error });
8927
+ throw new Error("SELECT query failed. See logs for details.", { cause: error });
8616
8928
  }
8617
8929
  }
8618
8930
 
8619
- // src/data/relational/tools/relational-update/index.ts
8620
- var relationalUpdate = toolBuilder().name("relational-update").displayName("Relational UPDATE").description("Execute type-safe UPDATE queries with single and batched operation support").category(ToolCategory.DATABASE).tags(["database", "sql", "update", "postgresql", "mysql", "sqlite"]).schema(relationalUpdateSchema).example({
8621
- description: "Update one row by condition",
8931
+ // src/data/relational/tools/relational-select/index.ts
8932
+ function toSelectErrorResponse(error) {
8933
+ const errorMessage = isSafeValidationError(error) ? error.message : "Failed to execute SELECT query. Please verify your input and database connection.";
8934
+ return {
8935
+ success: false,
8936
+ error: errorMessage,
8937
+ rows: [],
8938
+ rowCount: 0
8939
+ };
8940
+ }
8941
+ async function invokeRelationalSelect(execution, input) {
8942
+ try {
8943
+ const result = await executeSelect(
8944
+ execution.executor,
8945
+ { ...input, vendor: execution.vendor },
8946
+ execution.transaction ? { transaction: execution.transaction } : void 0
8947
+ );
8948
+ return {
8949
+ success: true,
8950
+ rows: result.rows,
8951
+ rowCount: result.rowCount,
8952
+ executionTime: result.executionTime,
8953
+ streaming: result.streaming
8954
+ };
8955
+ } catch (error) {
8956
+ return toSelectErrorResponse(error);
8957
+ }
8958
+ }
8959
+ var relationalSelect = toolBuilder().name("relational-select").displayName("Relational SELECT").description("Execute type-safe SELECT queries with WHERE conditions, ORDER BY, LIMIT, and OFFSET using Drizzle ORM query builder").category(ToolCategory.DATABASE).tags(["database", "sql", "select", "query", "postgresql", "mysql", "sqlite"]).schema(relationalSelectSchema).example({
8960
+ description: "SELECT with WHERE condition",
8622
8961
  input: {
8623
8962
  table: "users",
8624
- data: { status: "inactive" },
8625
- where: [{ column: "id", operator: "eq", value: 123 }],
8963
+ columns: ["id", "name", "email"],
8964
+ where: [{ column: "status", operator: "eq", value: "active" }],
8626
8965
  vendor: "postgresql",
8627
8966
  connectionString: "postgresql://localhost/mydb"
8628
8967
  }
8629
8968
  }).example({
8630
- description: "Optimistic lock update",
8969
+ description: "SELECT with pagination",
8631
8970
  input: {
8632
- table: "users",
8633
- data: { status: "active" },
8634
- where: [{ column: "id", operator: "eq", value: 123 }],
8635
- optimisticLock: { column: "version", expectedValue: 5 },
8636
- vendor: "sqlite",
8637
- connectionString: "data.db"
8971
+ table: "products",
8972
+ orderBy: [{ column: "created_at", direction: "desc" }],
8973
+ limit: 20,
8974
+ offset: 0,
8975
+ vendor: "mysql",
8976
+ connectionString: "mysql://localhost/shop"
8638
8977
  }
8639
8978
  }).example({
8640
- description: "Batch update operations",
8979
+ description: "SELECT with streaming mode",
8641
8980
  input: {
8642
- table: "users",
8643
- operations: [
8644
- {
8645
- data: { status: "inactive" },
8646
- where: [{ column: "id", operator: "eq", value: 1 }]
8647
- },
8648
- {
8649
- data: { status: "inactive" },
8650
- where: [{ column: "id", operator: "eq", value: 2 }]
8651
- }
8652
- ],
8653
- batch: {
8654
- batchSize: 100,
8655
- continueOnError: true
8981
+ table: "events",
8982
+ orderBy: [{ column: "id", direction: "asc" }],
8983
+ streaming: {
8984
+ enabled: true,
8985
+ chunkSize: 250,
8986
+ sampleSize: 25
8656
8987
  },
8657
8988
  vendor: "postgresql",
8658
- connectionString: "postgresql://localhost/mydb"
8989
+ connectionString: "postgresql://localhost/analytics"
8659
8990
  }
8660
8991
  }).implement(async (input) => {
8661
- const manager = new ConnectionManager({
8662
- vendor: input.vendor,
8663
- connection: input.connectionString
8664
- });
8665
- try {
8666
- await manager.connect();
8667
- const result = await executeUpdate(manager, input);
8668
- return {
8669
- success: true,
8670
- rowCount: result.rowCount,
8671
- executionTime: result.executionTime,
8672
- batch: result.batch
8673
- };
8674
- } catch (error) {
8675
- let errorMessage = "Failed to execute UPDATE query. Please verify your input and database connection.";
8676
- if (isSafeUpdateError(error)) {
8677
- errorMessage = error.message;
8678
- }
8679
- return {
8680
- success: false,
8681
- error: errorMessage,
8682
- rowCount: 0
8683
- };
8684
- } finally {
8685
- await manager.disconnect();
8686
- }
8992
+ const { connectionString, vendor, ...operation } = input;
8993
+ return withEphemeralRelationalToolSet(
8994
+ { vendor, connectionString },
8995
+ async (toolSet) => replaceConnectionFailureMessage(
8996
+ await toolSet.select.invoke(operation),
8997
+ SELECT_CONNECTION_FAILURE,
8998
+ "Failed to execute SELECT query. Please verify your input and database connection."
8999
+ )
9000
+ );
8687
9001
  }).build();
8688
- var deleteWhereOperatorSchema = z.enum([
9002
+ var updateValueSchema = z.union([
9003
+ z.string().describe("String value"),
9004
+ z.number().describe("Numeric value"),
9005
+ z.boolean().describe("Boolean value"),
9006
+ z.null().describe("Null value")
9007
+ ]).describe("Scalar value for an UPDATE column");
9008
+ var updateDataSchema = z.record(
9009
+ z.string().min(1, "Column name must not be empty").describe("Column name"),
9010
+ updateValueSchema
9011
+ ).superRefine((value, ctx) => {
9012
+ if (Object.keys(value).length === 0) {
9013
+ ctx.addIssue({
9014
+ code: z.ZodIssueCode.custom,
9015
+ message: "Update data must not be empty"
9016
+ });
9017
+ }
9018
+ }).describe("Object containing column-value pairs to update");
9019
+ var updateWhereOperatorSchema = z.enum([
8689
9020
  "eq",
8690
9021
  "ne",
8691
9022
  "gt",
@@ -8698,9 +9029,9 @@ var deleteWhereOperatorSchema = z.enum([
8698
9029
  "isNull",
8699
9030
  "isNotNull"
8700
9031
  ]);
8701
- var deleteWhereConditionSchema = z.object({
9032
+ var updateWhereConditionSchema = z.object({
8702
9033
  column: z.string().min(1, "Column name must not be empty").describe("Column name to filter on"),
8703
- operator: deleteWhereOperatorSchema.describe("Comparison operator"),
9034
+ operator: updateWhereOperatorSchema.describe("Comparison operator"),
8704
9035
  value: z.union([
8705
9036
  z.string(),
8706
9037
  z.number(),
@@ -8768,26 +9099,30 @@ var deleteWhereConditionSchema = z.object({
8768
9099
  });
8769
9100
  }
8770
9101
  });
8771
- var deleteSoftDeleteSchema = z.object({
8772
- column: z.string().min(1, "Soft delete column must not be empty").default("deleted_at").describe("Column to write deletion timestamp/value into"),
8773
- value: z.union([z.string(), z.number()]).optional().describe("Optional explicit value for the soft-delete column (defaults to ISO timestamp)")
8774
- }).describe("Enable soft delete by updating a column instead of physically deleting rows");
8775
- var deleteBatchOperationSchema = z.object({
8776
- where: z.array(deleteWhereConditionSchema).optional().describe("WHERE conditions (combined with AND)"),
8777
- allowFullTableDelete: z.boolean().default(false).describe("Explicitly allow DELETE without WHERE conditions for this operation"),
8778
- cascade: z.boolean().default(false).describe("Enable cascade-aware error messaging for this operation"),
8779
- softDelete: deleteSoftDeleteSchema.optional().describe("Use soft-delete semantics instead of hard DELETE for this operation")
9102
+ var updateOptimisticLockSchema = z.object({
9103
+ column: z.string().min(1, "Optimistic lock column must not be empty").describe("Version or lock column name"),
9104
+ expectedValue: z.union([
9105
+ z.string().describe("Expected string version value"),
9106
+ z.number().describe("Expected numeric version value")
9107
+ ]).describe("Expected current value for the lock column")
9108
+ }).describe("Optional optimistic lock condition");
9109
+ var updateBatchOperationSchema = z.object({
9110
+ data: updateDataSchema.describe("Columns and values to update"),
9111
+ where: z.array(updateWhereConditionSchema).optional().describe("WHERE conditions (combined with AND)"),
9112
+ allowFullTableUpdate: z.boolean().default(false).describe("Explicitly allow UPDATE without WHERE conditions for this operation"),
9113
+ optimisticLock: updateOptimisticLockSchema.optional().describe("Optional optimistic locking condition for this operation")
8780
9114
  }).superRefine((value, ctx) => {
8781
9115
  const hasWhere = (value.where?.length ?? 0) > 0;
8782
- if (!value.allowFullTableDelete && !hasWhere) {
9116
+ const hasOptimisticLock = !!value.optimisticLock;
9117
+ if (!value.allowFullTableUpdate && !hasWhere && !hasOptimisticLock) {
8783
9118
  ctx.addIssue({
8784
9119
  code: z.ZodIssueCode.custom,
8785
9120
  path: ["where"],
8786
- message: "WHERE conditions are required unless allowFullTableDelete is true"
9121
+ message: "WHERE conditions are required unless allowFullTableUpdate is true"
8787
9122
  });
8788
9123
  }
8789
9124
  });
8790
- var deleteBatchOptionsSchema = z.object({
9125
+ var updateBatchOptionsSchema = z.object({
8791
9126
  enabled: z.boolean().optional().default(true).describe("Enable batched processing for operations[] payloads"),
8792
9127
  batchSize: z.number().int().positive().max(5e3).optional().default(100).describe("Operations per batch chunk"),
8793
9128
  continueOnError: z.boolean().optional().default(true).describe("Continue processing remaining chunks when one chunk fails"),
@@ -8795,89 +9130,108 @@ var deleteBatchOptionsSchema = z.object({
8795
9130
  retryDelayMs: z.number().int().min(0).max(6e4).optional().default(0).describe("Delay between retry attempts in milliseconds"),
8796
9131
  benchmark: z.boolean().optional().default(false).describe("Run synthetic benchmark metadata comparing individual vs batched execution callbacks")
8797
9132
  });
8798
- var relationalDeleteSchema = z.object({
9133
+ var relationalUpdateSchema = z.object({
8799
9134
  table: z.string().min(1, "Table name is required").regex(
8800
9135
  VALID_QUALIFIED_IDENTIFIER_PATTERN,
8801
9136
  "Table name contains invalid characters. Only alphanumeric characters, underscores, and dots (for schema qualification) are allowed."
8802
- ).describe("Table name to delete from (schema-qualified names supported, e.g. public.users)"),
8803
- where: z.array(deleteWhereConditionSchema).optional().describe("WHERE conditions (combined with AND)"),
8804
- allowFullTableDelete: z.boolean().default(false).describe("Explicitly allow DELETE without WHERE conditions"),
8805
- cascade: z.boolean().default(false).describe("Enable cascade-aware error messaging for foreign key constraints"),
8806
- softDelete: deleteSoftDeleteSchema.optional().describe("Use soft-delete semantics instead of hard DELETE"),
8807
- operations: z.array(deleteBatchOperationSchema).min(1, "operations must contain at least one batch item").optional().describe("Batch delete operation list"),
8808
- batch: deleteBatchOptionsSchema.optional().describe("Optional batch execution controls for operations payloads"),
9137
+ ).describe("Table name to update (schema-qualified names supported, e.g. public.users)"),
9138
+ data: updateDataSchema.optional().describe("Columns and values to update"),
9139
+ where: z.array(updateWhereConditionSchema).optional().describe("WHERE conditions (combined with AND)"),
9140
+ allowFullTableUpdate: z.boolean().default(false).describe("Explicitly allow UPDATE without WHERE conditions"),
9141
+ optimisticLock: updateOptimisticLockSchema.optional().describe("Optional optimistic locking condition"),
9142
+ operations: z.array(updateBatchOperationSchema).min(1, "operations must contain at least one batch item").optional().describe("Batch update operation list"),
9143
+ batch: updateBatchOptionsSchema.optional().describe("Optional batch execution controls for operations payloads"),
8809
9144
  vendor: z.enum(["postgresql", "mysql", "sqlite"]).describe("Database vendor"),
8810
9145
  connectionString: z.string().min(1, "Database connection string is required").describe("Database connection string")
8811
9146
  }).superRefine((value, ctx) => {
8812
9147
  const hasOperations = (value.operations?.length ?? 0) > 0;
8813
- if (hasOperations && (value.where?.length ?? 0) > 0) {
9148
+ if (hasOperations && value.data) {
8814
9149
  ctx.addIssue({
8815
9150
  code: z.ZodIssueCode.custom,
8816
- path: ["where"],
8817
- message: "where cannot be provided when operations[] is used"
9151
+ path: ["data"],
9152
+ message: "data cannot be provided when operations[] is used"
8818
9153
  });
8819
9154
  }
8820
- if (hasOperations && value.allowFullTableDelete) {
9155
+ if (!hasOperations && !value.data) {
8821
9156
  ctx.addIssue({
8822
9157
  code: z.ZodIssueCode.custom,
8823
- path: ["allowFullTableDelete"],
8824
- message: "allowFullTableDelete cannot be provided when operations[] is used"
9158
+ path: ["data"],
9159
+ message: "data is required when operations[] is not provided"
8825
9160
  });
8826
9161
  }
8827
- if (hasOperations && value.cascade) {
9162
+ if (hasOperations && (value.where?.length ?? 0) > 0) {
8828
9163
  ctx.addIssue({
8829
9164
  code: z.ZodIssueCode.custom,
8830
- path: ["cascade"],
8831
- message: "cascade cannot be provided when operations[] is used"
9165
+ path: ["where"],
9166
+ message: "where cannot be provided when operations[] is used"
8832
9167
  });
8833
9168
  }
8834
- if (hasOperations && value.softDelete) {
9169
+ if (hasOperations && value.optimisticLock) {
8835
9170
  ctx.addIssue({
8836
9171
  code: z.ZodIssueCode.custom,
8837
- path: ["softDelete"],
8838
- message: "softDelete cannot be provided when operations[] is used"
9172
+ path: ["optimisticLock"],
9173
+ message: "optimisticLock cannot be provided when operations[] is used"
9174
+ });
9175
+ }
9176
+ if (hasOperations && value.allowFullTableUpdate) {
9177
+ ctx.addIssue({
9178
+ code: z.ZodIssueCode.custom,
9179
+ path: ["allowFullTableUpdate"],
9180
+ message: "allowFullTableUpdate cannot be provided when operations[] is used"
8839
9181
  });
8840
9182
  }
8841
9183
  if (hasOperations) {
8842
9184
  return;
8843
9185
  }
8844
9186
  const hasWhere = (value.where?.length ?? 0) > 0;
8845
- if (!value.allowFullTableDelete && !hasWhere) {
9187
+ const hasOptimisticLock = !!value.optimisticLock;
9188
+ if (!value.allowFullTableUpdate && !hasWhere && !hasOptimisticLock) {
8846
9189
  ctx.addIssue({
8847
9190
  code: z.ZodIssueCode.custom,
8848
9191
  path: ["where"],
8849
- message: "WHERE conditions are required unless allowFullTableDelete is true"
9192
+ message: "WHERE conditions are required unless allowFullTableUpdate is true"
8850
9193
  });
8851
9194
  }
8852
9195
  });
8853
9196
 
8854
- // src/data/relational/tools/relational-delete/error-utils.ts
8855
- var SAFE_DELETE_VALIDATION_PATTERNS = [
9197
+ // src/data/relational/tools/relational-update/error-utils.ts
9198
+ var SAFE_UPDATE_VALIDATION_PATTERNS = [
8856
9199
  "must not be empty",
8857
9200
  "contains invalid characters",
8858
- "WHERE conditions are required for DELETE queries",
8859
- "WHERE conditions are required unless allowFullTableDelete is true",
8860
- "value is required for this operator",
9201
+ "Update data must be an object",
9202
+ "Update data must not be empty",
9203
+ "must not be undefined",
9204
+ "WHERE conditions are required for UPDATE queries",
8861
9205
  "null is only allowed with isNull/isNotNull operators",
8862
- "operator requires a value",
8863
9206
  "operator requires a string or number value",
8864
- "operator requires a string value",
8865
9207
  "operator requires a non-empty array value",
8866
- "must not include value"
9208
+ "operator requires a string value",
9209
+ "must not include value",
9210
+ "Optimistic lock expectedValue must not be empty",
9211
+ "optimistic lock check failed",
9212
+ "WHERE conditions are required unless allowFullTableUpdate is true"
8867
9213
  ];
8868
9214
  var CONSTRAINT_VIOLATION_PATTERNS3 = [
8869
9215
  {
8870
- pattern: /(foreign key constraint|violates foreign key constraint|foreign key mismatch|a foreign key constraint fails)/i,
8871
- message: "Delete failed: foreign key constraint violation."
9216
+ pattern: /(unique constraint|duplicate key|duplicate entry)/i,
9217
+ message: "Update failed: unique constraint violation."
9218
+ },
9219
+ {
9220
+ pattern: /(foreign key constraint|violates foreign key constraint)/i,
9221
+ message: "Update failed: foreign key constraint violation."
9222
+ },
9223
+ {
9224
+ pattern: /(not null constraint|cannot be null)/i,
9225
+ message: "Update failed: NOT NULL constraint violation."
8872
9226
  }
8873
9227
  ];
8874
- function isSafeDeleteValidationError(error) {
9228
+ function isSafeUpdateValidationError(error) {
8875
9229
  if (!(error instanceof Error)) {
8876
9230
  return false;
8877
9231
  }
8878
- return SAFE_DELETE_VALIDATION_PATTERNS.some((pattern) => error.message.includes(pattern));
9232
+ return SAFE_UPDATE_VALIDATION_PATTERNS.some((pattern) => error.message.includes(pattern));
8879
9233
  }
8880
- function getDeleteConstraintViolationMessage(error, cascade) {
9234
+ function getUpdateConstraintViolationMessage(error) {
8881
9235
  if (!(error instanceof Error)) {
8882
9236
  return null;
8883
9237
  }
@@ -8887,21 +9241,18 @@ function getDeleteConstraintViolationMessage(error, cascade) {
8887
9241
  }
8888
9242
  for (const entry of CONSTRAINT_VIOLATION_PATTERNS3) {
8889
9243
  if (messages.some((msg) => entry.pattern.test(msg))) {
8890
- if (cascade) {
8891
- return `${entry.message} Verify database-level ON DELETE CASCADE is configured for related foreign keys.`;
8892
- }
8893
9244
  return entry.message;
8894
9245
  }
8895
9246
  }
8896
9247
  return null;
8897
9248
  }
8898
- function isSafeDeleteError(error) {
9249
+ function isSafeUpdateError(error) {
8899
9250
  if (!(error instanceof Error)) {
8900
9251
  return false;
8901
9252
  }
8902
- return SAFE_DELETE_VALIDATION_PATTERNS.some((pattern) => error.message.includes(pattern)) || error.message.startsWith("Delete failed:");
9253
+ return isSafeUpdateValidationError(error) || getUpdateConstraintViolationMessage(error) !== null;
8903
9254
  }
8904
- var deleteExecutorLogger = createLogger("agentforge:tools:data:relational:delete");
9255
+ var updateExecutorLogger = createLogger("agentforge:tools:data:relational:update");
8905
9256
  function toNumber3(value) {
8906
9257
  return typeof value === "number" && Number.isFinite(value) ? value : void 0;
8907
9258
  }
@@ -8938,71 +9289,73 @@ function resolveBatchOptions3(batch) {
8938
9289
  benchmark: batch.benchmark ?? false
8939
9290
  };
8940
9291
  }
8941
- function toSingleDeleteOperation(input) {
9292
+ function toSingleUpdateOperation(input) {
9293
+ if (!input.data) {
9294
+ throw new Error("UPDATE data is required when operations[] is not provided.");
9295
+ }
8942
9296
  return {
9297
+ data: input.data,
8943
9298
  where: input.where,
8944
- allowFullTableDelete: input.allowFullTableDelete,
8945
- cascade: input.cascade,
8946
- softDelete: input.softDelete
9299
+ allowFullTableUpdate: input.allowFullTableUpdate,
9300
+ optimisticLock: input.optimisticLock
8947
9301
  };
8948
9302
  }
8949
9303
 
8950
- // src/data/relational/tools/relational-delete/executor-single.ts
8951
- async function executeSingleDelete(manager, input, operation, context) {
8952
- const built = buildDeleteQuery({
9304
+ // src/data/relational/tools/relational-update/executor-single.ts
9305
+ async function executeSingleUpdate(executor, input, operation, context) {
9306
+ const built = buildUpdateQuery({
8953
9307
  table: input.table,
9308
+ data: operation.data,
8954
9309
  where: operation.where,
8955
- allowFullTableDelete: operation.allowFullTableDelete,
8956
- softDelete: operation.softDelete,
9310
+ allowFullTableUpdate: operation.allowFullTableUpdate,
9311
+ optimisticLock: operation.optimisticLock,
8957
9312
  vendor: input.vendor
8958
9313
  });
8959
- const executor = manager;
8960
- const rawResult = await executor.execute(built.query);
9314
+ const session = context?.transaction ?? executor;
9315
+ const rawResult = await session.execute(built.query);
8961
9316
  const rowCount = normalizeAffectedRows2(rawResult);
9317
+ if (built.usesOptimisticLock && rowCount === 0) {
9318
+ throw new Error("Update failed: optimistic lock check failed.");
9319
+ }
8962
9320
  return {
8963
9321
  rowCount,
8964
- executionTime: 0,
8965
- softDeleted: built.usesSoftDelete
9322
+ executionTime: 0
8966
9323
  };
8967
9324
  }
8968
9325
 
8969
- // src/data/relational/tools/relational-delete/executor-batch.ts
8970
- async function executeDeleteInBatchMode(manager, input, context, options) {
9326
+ // src/data/relational/tools/relational-update/executor-batch.ts
9327
+ async function executeUpdateInBatchMode(executor, input, context, options) {
8971
9328
  const batchResult = await executeBatchedTask(
8972
9329
  {
8973
- operation: "delete",
9330
+ operation: "update",
8974
9331
  items: input.operations,
8975
9332
  executeBatch: async (operations, batchIndex) => {
8976
9333
  let rowCount2 = 0;
8977
9334
  let successfulItems = 0;
8978
9335
  let failedItems = 0;
8979
- let softDeletedCount = 0;
8980
9336
  const failures = [];
8981
9337
  for (const operation of operations) {
8982
9338
  try {
8983
- const result = await executeSingleDelete(
8984
- manager,
9339
+ const result = await executeSingleUpdate(
9340
+ executor,
8985
9341
  input,
8986
9342
  {
9343
+ data: operation.data,
8987
9344
  where: operation.where,
8988
- allowFullTableDelete: operation.allowFullTableDelete,
8989
- cascade: operation.cascade,
8990
- softDelete: operation.softDelete
9345
+ allowFullTableUpdate: operation.allowFullTableUpdate,
9346
+ optimisticLock: operation.optimisticLock
8991
9347
  },
8992
9348
  context
8993
9349
  );
8994
9350
  rowCount2 += result.rowCount;
8995
9351
  successfulItems += 1;
8996
- if (result.softDeleted) {
8997
- softDeletedCount += 1;
8998
- }
8999
9352
  } catch (error) {
9000
9353
  if (!options.continueOnError) {
9001
9354
  throw error;
9002
9355
  }
9003
9356
  failedItems += 1;
9004
9357
  failures.push({
9005
- operation: "delete",
9358
+ operation: "update",
9006
9359
  batchIndex,
9007
9360
  batchSize: operations.length,
9008
9361
  attempts: 1,
@@ -9014,7 +9367,6 @@ async function executeDeleteInBatchMode(manager, input, context, options) {
9014
9367
  rowCount: rowCount2,
9015
9368
  successfulItems,
9016
9369
  failedItems,
9017
- softDeletedCount,
9018
9370
  failures
9019
9371
  };
9020
9372
  },
@@ -9026,7 +9378,7 @@ async function executeDeleteInBatchMode(manager, input, context, options) {
9026
9378
  maxRetries: options.maxRetries,
9027
9379
  retryDelayMs: options.retryDelayMs,
9028
9380
  onProgress: (progress) => {
9029
- deleteExecutorLogger.debug("DELETE batch progress", {
9381
+ updateExecutorLogger.debug("UPDATE batch progress", {
9030
9382
  vendor: input.vendor,
9031
9383
  table: input.table,
9032
9384
  ...progress
@@ -9035,19 +9387,15 @@ async function executeDeleteInBatchMode(manager, input, context, options) {
9035
9387
  }
9036
9388
  );
9037
9389
  const rowCount = batchResult.results.reduce((total, result) => total + result.rowCount, 0);
9038
- const softDeleted = batchResult.results.some((result) => result.softDeletedCount > 0);
9039
9390
  const operationFailures = batchResult.results.flatMap((result) => result.failures);
9040
9391
  let benchmark;
9041
9392
  if (options.benchmark) {
9042
- deleteExecutorLogger.warn(
9043
- "DELETE batch benchmark enabled. Synthetic benchmark callbacks are side-effect free and do not execute SQL.",
9044
- {
9045
- vendor: input.vendor,
9046
- table: input.table,
9047
- operationCount: input.operations.length,
9048
- batchSize: options.batchSize
9049
- }
9050
- );
9393
+ updateExecutorLogger.warn("UPDATE batch benchmark enabled. Synthetic benchmark callbacks are side-effect free and do not execute SQL.", {
9394
+ vendor: input.vendor,
9395
+ table: input.table,
9396
+ operationCount: input.operations.length,
9397
+ batchSize: options.batchSize
9398
+ });
9051
9399
  benchmark = await benchmarkBatchExecution({
9052
9400
  items: input.operations,
9053
9401
  batchSize: options.batchSize,
@@ -9060,7 +9408,6 @@ async function executeDeleteInBatchMode(manager, input, context, options) {
9060
9408
  return {
9061
9409
  rowCount,
9062
9410
  executionTime: batchResult.executionTime,
9063
- softDeleted,
9064
9411
  batch: {
9065
9412
  enabled: true,
9066
9413
  batchSize: options.batchSize,
@@ -9077,35 +9424,32 @@ async function executeDeleteInBatchMode(manager, input, context, options) {
9077
9424
  };
9078
9425
  }
9079
9426
 
9080
- // src/data/relational/tools/relational-delete/executor.ts
9081
- async function executeDelete(manager, input, context) {
9427
+ // src/data/relational/tools/relational-update/executor.ts
9428
+ async function executeUpdate(executor, input, context) {
9082
9429
  const startTime = Date.now();
9083
- deleteExecutorLogger.debug("Building DELETE query", {
9430
+ updateExecutorLogger.debug("Building UPDATE query", {
9084
9431
  vendor: input.vendor,
9085
9432
  table: input.table,
9086
9433
  hasWhere: !!input.where?.length,
9087
- ...input.allowFullTableDelete !== void 0 ? { allowFullTableDelete: input.allowFullTableDelete } : {},
9088
- ...input.cascade !== void 0 ? { cascade: input.cascade } : {},
9089
- softDelete: !!input.softDelete,
9434
+ ...input.allowFullTableUpdate !== void 0 ? { allowFullTableUpdate: input.allowFullTableUpdate } : {},
9435
+ hasOptimisticLock: !!input.optimisticLock,
9090
9436
  operationCount: input.operations?.length ?? 0,
9091
9437
  batchModeEnabled: !!input.batch?.enabled
9092
9438
  });
9093
9439
  try {
9094
9440
  const batchOptions = resolveBatchOptions3(input.batch);
9095
- const result = input.operations && batchOptions ? await executeDeleteInBatchMode(
9096
- manager,
9441
+ const result = input.operations && batchOptions ? await executeUpdateInBatchMode(
9442
+ executor,
9097
9443
  input,
9098
9444
  context,
9099
9445
  batchOptions
9100
- ) : await executeSingleDelete(manager, input, toSingleDeleteOperation(input), context);
9446
+ ) : await executeSingleUpdate(executor, input, toSingleUpdateOperation(input), context);
9101
9447
  const executionTime = Date.now() - startTime;
9102
- deleteExecutorLogger.debug("DELETE query executed successfully", {
9448
+ updateExecutorLogger.debug("UPDATE query executed successfully", {
9103
9449
  vendor: input.vendor,
9104
9450
  table: input.table,
9105
9451
  rowCount: result.rowCount,
9106
9452
  executionTime,
9107
- softDelete: result.softDeleted,
9108
- ...input.cascade !== void 0 ? { cascade: input.cascade } : {},
9109
9453
  batchMode: !!result.batch,
9110
9454
  partialSuccess: result.batch?.partialSuccess ?? false
9111
9455
  });
@@ -9115,55 +9459,80 @@ async function executeDelete(manager, input, context) {
9115
9459
  };
9116
9460
  } catch (error) {
9117
9461
  const executionTime = Date.now() - startTime;
9118
- deleteExecutorLogger.error("DELETE query execution failed", {
9462
+ updateExecutorLogger.error("UPDATE query execution failed", {
9119
9463
  vendor: input.vendor,
9120
9464
  table: input.table,
9121
9465
  error: error instanceof Error ? error.message : String(error),
9122
- executionTime,
9123
- ...input.cascade !== void 0 ? { cascade: input.cascade } : {},
9124
- softDelete: !!input.softDelete
9466
+ executionTime
9125
9467
  });
9126
- const constraintMessage = getDeleteConstraintViolationMessage(error, input.cascade ?? false);
9468
+ const constraintMessage = getUpdateConstraintViolationMessage(error);
9127
9469
  if (constraintMessage) {
9128
9470
  throw new Error(constraintMessage, { cause: error });
9129
9471
  }
9130
- if (isSafeDeleteValidationError(error)) {
9472
+ if (isSafeUpdateValidationError(error)) {
9131
9473
  throw error;
9132
9474
  }
9133
- throw new Error("DELETE query failed. See logs for details.", { cause: error });
9475
+ throw new Error("UPDATE query failed. See logs for details.", { cause: error });
9134
9476
  }
9135
9477
  }
9136
9478
 
9137
- // src/data/relational/tools/relational-delete/index.ts
9138
- var relationalDelete = toolBuilder().name("relational-delete").displayName("Relational DELETE").description("Execute type-safe DELETE queries with single and batched operation support").category(ToolCategory.DATABASE).tags(["database", "sql", "delete", "postgresql", "mysql", "sqlite"]).schema(relationalDeleteSchema).example({
9139
- description: "Delete one row by condition",
9479
+ // src/data/relational/tools/relational-update/index.ts
9480
+ function toUpdateErrorResponse(error) {
9481
+ const errorMessage = isSafeUpdateError(error) ? error.message : "Failed to execute UPDATE query. Please verify your input and database connection.";
9482
+ return {
9483
+ success: false,
9484
+ error: errorMessage,
9485
+ rowCount: 0
9486
+ };
9487
+ }
9488
+ async function invokeRelationalUpdate(execution, input) {
9489
+ try {
9490
+ const result = await executeUpdate(
9491
+ execution.executor,
9492
+ { ...input, vendor: execution.vendor },
9493
+ execution.transaction ? { transaction: execution.transaction } : void 0
9494
+ );
9495
+ return {
9496
+ success: true,
9497
+ rowCount: result.rowCount,
9498
+ executionTime: result.executionTime,
9499
+ batch: result.batch
9500
+ };
9501
+ } catch (error) {
9502
+ return toUpdateErrorResponse(error);
9503
+ }
9504
+ }
9505
+ var relationalUpdate = toolBuilder().name("relational-update").displayName("Relational UPDATE").description("Execute type-safe UPDATE queries with single and batched operation support").category(ToolCategory.DATABASE).tags(["database", "sql", "update", "postgresql", "mysql", "sqlite"]).schema(relationalUpdateSchema).example({
9506
+ description: "Update one row by condition",
9140
9507
  input: {
9141
9508
  table: "users",
9509
+ data: { status: "inactive" },
9142
9510
  where: [{ column: "id", operator: "eq", value: 123 }],
9143
9511
  vendor: "postgresql",
9144
9512
  connectionString: "postgresql://localhost/mydb"
9145
9513
  }
9146
9514
  }).example({
9147
- description: "Soft delete rows",
9515
+ description: "Optimistic lock update",
9148
9516
  input: {
9149
9517
  table: "users",
9150
- where: [{ column: "status", operator: "eq", value: "inactive" }],
9151
- softDelete: { column: "deleted_at" },
9518
+ data: { status: "active" },
9519
+ where: [{ column: "id", operator: "eq", value: 123 }],
9520
+ optimisticLock: { column: "version", expectedValue: 5 },
9152
9521
  vendor: "sqlite",
9153
9522
  connectionString: "data.db"
9154
9523
  }
9155
9524
  }).example({
9156
- description: "Batch delete operations",
9525
+ description: "Batch update operations",
9157
9526
  input: {
9158
9527
  table: "users",
9159
9528
  operations: [
9160
9529
  {
9161
- where: [{ column: "status", operator: "eq", value: "inactive" }],
9162
- softDelete: { column: "deleted_at" }
9530
+ data: { status: "inactive" },
9531
+ where: [{ column: "id", operator: "eq", value: 1 }]
9163
9532
  },
9164
9533
  {
9165
- where: [{ column: "status", operator: "eq", value: "pending_delete" }],
9166
- softDelete: { column: "deleted_at" }
9534
+ data: { status: "inactive" },
9535
+ where: [{ column: "id", operator: "eq", value: 2 }]
9167
9536
  }
9168
9537
  ],
9169
9538
  batch: {
@@ -9174,141 +9543,503 @@ var relationalDelete = toolBuilder().name("relational-delete").displayName("Rela
9174
9543
  connectionString: "postgresql://localhost/mydb"
9175
9544
  }
9176
9545
  }).implement(async (input) => {
9177
- const manager = new ConnectionManager({
9178
- vendor: input.vendor,
9179
- connection: input.connectionString
9180
- });
9181
- try {
9182
- await manager.connect();
9183
- const result = await executeDelete(manager, input);
9184
- return {
9185
- success: true,
9186
- rowCount: result.rowCount,
9187
- executionTime: result.executionTime,
9188
- softDeleted: result.softDeleted,
9189
- batch: result.batch
9190
- };
9191
- } catch (error) {
9192
- let errorMessage = "Failed to execute DELETE query. Please verify your input and database connection.";
9193
- if (isSafeDeleteError(error)) {
9194
- errorMessage = error.message;
9195
- }
9196
- return {
9197
- success: false,
9198
- error: errorMessage,
9199
- rowCount: 0,
9200
- softDeleted: false
9201
- };
9202
- } finally {
9203
- await manager.disconnect();
9204
- }
9546
+ const { connectionString, vendor, ...operation } = input;
9547
+ return withEphemeralRelationalMutationToolSet(
9548
+ { vendor, connectionString },
9549
+ async (toolSet) => replaceMutationConnectionFailure(
9550
+ await toolSet.update.invoke(operation),
9551
+ UPDATE_CONNECTION_FAILURE,
9552
+ "Failed to execute UPDATE query. Please verify your input and database connection."
9553
+ ),
9554
+ toUpdateErrorResponse
9555
+ );
9205
9556
  }).build();
9206
9557
 
9207
- // src/data/relational/tools/relational-get-schema-error-utils.ts
9208
- var SAFE_VALIDATION_ERROR_PATTERNS2 = [
9209
- "must not be empty",
9210
- "contains invalid characters",
9211
- "Invalid table filter"
9212
- ];
9213
- function isSafeGetSchemaValidationError(error) {
9214
- if (!(error instanceof Error)) {
9558
+ // src/data/relational/tool-set.ts
9559
+ var DEFAULT_SCHEMA_CACHE_TTL_MS = 6e4;
9560
+ var TOOL_SET_SCHEMA_CACHE_KEY = "relational-tool-set";
9561
+ var PREFIX_PATTERN = /^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$/;
9562
+ var ROLLBACK_ONLY_MESSAGE = "Transaction is rollback-only; no database work was executed.";
9563
+ var logger18 = createLogger("agentforge:tools:data:relational:tool-set");
9564
+ var RelationalToolSetConfigurationError = class extends Error {
9565
+ name = "RelationalToolSetConfigurationError";
9566
+ };
9567
+ var RelationalToolSetDisposedError = class extends Error {
9568
+ name = "RelationalToolSetDisposedError";
9569
+ constructor() {
9570
+ super("The Relational Tool Set is disposing or has been disposed.");
9571
+ }
9572
+ };
9573
+ var RelationalTransactionError = class extends Error {
9574
+ constructor(message, code) {
9575
+ super(message);
9576
+ this.code = code;
9577
+ }
9578
+ name = "RelationalTransactionError";
9579
+ };
9580
+ function isFailedOrPartialResult(result) {
9581
+ if (!result || typeof result !== "object") {
9215
9582
  return false;
9216
9583
  }
9217
- return SAFE_VALIDATION_ERROR_PATTERNS2.some(
9218
- (pattern) => error.message.includes(pattern)
9584
+ const response = result;
9585
+ return response.success === false || response.batch?.partialSuccess === true || (response.batch?.failedItems ?? 0) > 0;
9586
+ }
9587
+ var TransactionToolScope = class {
9588
+ constructor(execution) {
9589
+ this.execution = execution;
9590
+ }
9591
+ active = true;
9592
+ rollbackOnly = false;
9593
+ tail = Promise.resolve();
9594
+ run(operation, failure) {
9595
+ if (!this.active) {
9596
+ return Promise.reject(
9597
+ new RelationalTransactionError(
9598
+ "Transaction-scoped Tools can no longer be used because their callback has settled.",
9599
+ "SCOPE_EXPIRED"
9600
+ )
9601
+ );
9602
+ }
9603
+ const invocation = this.tail.then(async () => {
9604
+ if (this.rollbackOnly) {
9605
+ return failure(ROLLBACK_ONLY_MESSAGE);
9606
+ }
9607
+ const result = await operation(this.execution);
9608
+ if (isFailedOrPartialResult(result)) {
9609
+ this.rollbackOnly = true;
9610
+ }
9611
+ return result;
9612
+ });
9613
+ this.tail = invocation.then(
9614
+ () => void 0,
9615
+ () => void 0
9616
+ );
9617
+ return invocation;
9618
+ }
9619
+ async settle() {
9620
+ this.active = false;
9621
+ await this.tail;
9622
+ }
9623
+ isRollbackOnly() {
9624
+ return this.rollbackOnly;
9625
+ }
9626
+ };
9627
+ function createConfiguredTools(options, execution) {
9628
+ const query = createTool(
9629
+ configuredMetadata(relationalQuery, options.prefix),
9630
+ relationalQuerySchema.omit({ vendor: true, connectionString: true }),
9631
+ (input) => execution(
9632
+ (context) => invokeRelationalQuery(context, input),
9633
+ (error) => ({ success: false, error, rows: [], rowCount: 0 }),
9634
+ QUERY_CONNECTION_FAILURE
9635
+ )
9636
+ );
9637
+ const select = createTool(
9638
+ configuredMetadata(relationalSelect, options.prefix),
9639
+ relationalSelectSchema.omit({ vendor: true, connectionString: true }),
9640
+ (input) => execution(
9641
+ (context) => invokeRelationalSelect(context, input),
9642
+ (error) => ({ success: false, error, rows: [], rowCount: 0 }),
9643
+ SELECT_CONNECTION_FAILURE
9644
+ )
9645
+ );
9646
+ const insert = createTool(
9647
+ configuredMetadata(relationalInsert, options.prefix),
9648
+ relationalInsertSchema.omit({ vendor: true, connectionString: true }),
9649
+ (input) => execution(
9650
+ (context) => invokeRelationalInsert(context, input),
9651
+ (error) => ({
9652
+ success: false,
9653
+ error,
9654
+ rowCount: 0,
9655
+ insertedIds: [],
9656
+ rows: []
9657
+ }),
9658
+ INSERT_CONNECTION_FAILURE
9659
+ )
9660
+ );
9661
+ const update = createTool(
9662
+ configuredMetadata(relationalUpdate, options.prefix),
9663
+ relationalUpdateOperationSchema,
9664
+ (input) => execution(
9665
+ (context) => invokeRelationalUpdate(context, input),
9666
+ (error) => ({ success: false, error, rowCount: 0 }),
9667
+ UPDATE_CONNECTION_FAILURE
9668
+ )
9669
+ );
9670
+ const deleteTool = createTool(
9671
+ configuredMetadata(relationalDelete, options.prefix),
9672
+ relationalDeleteOperationSchema,
9673
+ (input) => execution(
9674
+ (context) => invokeRelationalDelete(context, input),
9675
+ (error) => ({ success: false, error, rowCount: 0, softDeleted: false }),
9676
+ DELETE_CONNECTION_FAILURE
9677
+ )
9678
+ );
9679
+ const getSchema = createTool(
9680
+ configuredMetadata(relationalGetSchema, options.prefix),
9681
+ relationalGetSchemaInputSchema.omit({
9682
+ vendor: true,
9683
+ connectionString: true,
9684
+ database: true,
9685
+ cacheTtlMs: true
9686
+ }),
9687
+ (input) => execution(
9688
+ (context) => invokeRelationalGetSchema(context, {
9689
+ ...input,
9690
+ cacheTtlMs: options.schemaCacheTtlMs ?? DEFAULT_SCHEMA_CACHE_TTL_MS
9691
+ }),
9692
+ (error) => ({ success: false, error, schema: null }),
9693
+ "Failed to inspect schema. See logs for details."
9694
+ )
9219
9695
  );
9696
+ const tools = [query, select, insert, update, deleteTool, getSchema];
9697
+ return Object.freeze({
9698
+ query,
9699
+ select,
9700
+ insert,
9701
+ update,
9702
+ delete: deleteTool,
9703
+ getSchema,
9704
+ [Symbol.iterator]: () => tools[Symbol.iterator]()
9705
+ });
9220
9706
  }
9221
-
9222
- // src/data/relational/tools/relational-get-schema.ts
9223
- var logger17 = createLogger("agentforge:tools:data:relational:get-schema");
9224
- function buildSchemaCacheKey(vendor, connectionString, database) {
9225
- const databaseScope = database ?? "default";
9226
- const connectionHash = createHash("sha256").update(connectionString).digest("hex");
9227
- return `${vendor}:${databaseScope}:${connectionHash}`;
9707
+ function validateConfiguration(config, options) {
9708
+ if (!config || !["postgresql", "mysql", "sqlite"].includes(config.vendor)) {
9709
+ throw new RelationalToolSetConfigurationError(
9710
+ "Database vendor must be postgresql, mysql, or sqlite."
9711
+ );
9712
+ }
9713
+ if (typeof config.connection === "string" && config.connection.trim().length === 0 || typeof config.connection !== "string" && (!config.connection || Array.isArray(config.connection))) {
9714
+ throw new RelationalToolSetConfigurationError(
9715
+ "Database connection must be a non-empty string or configuration object."
9716
+ );
9717
+ }
9718
+ if (config.vendor === "sqlite" && typeof config.connection !== "string" && (typeof config.connection.url !== "string" || config.connection.url.trim().length === 0)) {
9719
+ throw new RelationalToolSetConfigurationError(
9720
+ "SQLite object configuration requires a non-empty url."
9721
+ );
9722
+ }
9723
+ if (options.prefix !== void 0 && !PREFIX_PATTERN.test(options.prefix)) {
9724
+ throw new RelationalToolSetConfigurationError(
9725
+ "Tool name prefix must be non-empty kebab-case starting with a letter."
9726
+ );
9727
+ }
9728
+ if (options.schemaCacheTtlMs !== void 0 && (!Number.isInteger(options.schemaCacheTtlMs) || options.schemaCacheTtlMs < 0)) {
9729
+ throw new RelationalToolSetConfigurationError(
9730
+ "Schema cache TTL must be a non-negative integer."
9731
+ );
9732
+ }
9733
+ const longestName = configuredName("relational-get-schema", options.prefix);
9734
+ if (longestName.length > 50) {
9735
+ throw new RelationalToolSetConfigurationError(
9736
+ "Tool name prefix is too long; configured Tool names must not exceed 50 characters."
9737
+ );
9738
+ }
9228
9739
  }
9229
- var relationalGetSchemaInputSchema = z.object({
9230
- vendor: z.enum(["postgresql", "mysql", "sqlite"]).describe("Database vendor"),
9231
- connectionString: z.string().min(1, "Database connection string is required").describe("Database connection string used to create a new connection"),
9232
- database: z.string().min(1).optional().describe("Optional logical database name used for cache scoping"),
9233
- tables: z.array(
9234
- z.string().regex(
9235
- VALID_TABLE_FILTER_PATTERN,
9236
- "Table filter contains invalid characters. Use alphanumeric, underscore, and optional schema qualification."
9237
- )
9238
- ).optional().describe("Optional table filter list. When omitted, all tables are returned."),
9239
- cacheTtlMs: z.number().int().min(0).optional().describe("Schema cache TTL in milliseconds (0 disables caching)"),
9240
- refreshCache: z.boolean().optional().describe("Force cache invalidation before introspecting schema")
9241
- });
9242
- var relationalGetSchema = toolBuilder().name("relational-get-schema").displayName("Relational Get Schema").description("Inspect relational database schema (tables, columns, primary keys, foreign keys, indexes) across PostgreSQL, MySQL, and SQLite").category(ToolCategory.DATABASE).tags(["database", "schema", "introspection", "postgresql", "mysql", "sqlite"]).schema(relationalGetSchemaInputSchema).example({
9243
- description: "Inspect all SQLite tables",
9244
- input: {
9245
- vendor: "sqlite",
9246
- connectionString: ":memory:"
9740
+ function cloneImmutableValue(value) {
9741
+ if (Array.isArray(value)) {
9742
+ return Object.freeze(value.map(cloneImmutableValue));
9247
9743
  }
9248
- }).example({
9249
- description: "Inspect selected PostgreSQL tables with cache control",
9250
- input: {
9251
- vendor: "postgresql",
9252
- connectionString: "postgresql://localhost/app",
9253
- tables: ["public.users", "public.orders"],
9254
- cacheTtlMs: 3e4,
9255
- refreshCache: true
9744
+ if (value instanceof Date) {
9745
+ return new Date(value.getTime());
9256
9746
  }
9257
- }).implement(async (input) => {
9258
- const manager = new ConnectionManager({
9259
- vendor: input.vendor,
9260
- connection: input.connectionString
9261
- });
9262
- const cacheKey = buildSchemaCacheKey(
9263
- input.vendor,
9264
- input.connectionString,
9265
- input.database
9266
- );
9267
- const inspector = new SchemaInspector(manager, input.vendor, {
9268
- cacheTtlMs: input.cacheTtlMs,
9269
- cacheKey
9747
+ if (value instanceof ArrayBuffer) {
9748
+ return value.slice(0);
9749
+ }
9750
+ if (Buffer.isBuffer(value)) {
9751
+ return Buffer.from(value);
9752
+ }
9753
+ if (ArrayBuffer.isView(value)) {
9754
+ return structuredClone(value);
9755
+ }
9756
+ if (value && typeof value === "object" && Object.getPrototypeOf(value) === Object.prototype) {
9757
+ return Object.freeze(
9758
+ Object.fromEntries(
9759
+ Object.entries(value).map(([key, nestedValue]) => [key, cloneImmutableValue(nestedValue)])
9760
+ )
9761
+ );
9762
+ }
9763
+ if (value && typeof value === "object") {
9764
+ throw new RelationalToolSetConfigurationError(
9765
+ "Database configuration contains an unsupported mutable object."
9766
+ );
9767
+ }
9768
+ return value;
9769
+ }
9770
+ function snapshotConfiguration(config) {
9771
+ const connection = cloneImmutableValue(config.connection);
9772
+ return Object.freeze({ vendor: config.vendor, connection });
9773
+ }
9774
+ function configuredName(name, prefix) {
9775
+ return prefix ? `${prefix}-${name}` : name;
9776
+ }
9777
+ function configuredMetadata(tool, prefix) {
9778
+ return {
9779
+ ...tool.metadata,
9780
+ name: configuredName(tool.metadata.name, prefix),
9781
+ examples: void 0
9782
+ };
9783
+ }
9784
+ function preserveOperationValidation(legacySchema, input, context) {
9785
+ const result = legacySchema.safeParse({
9786
+ ...input,
9787
+ vendor: "sqlite",
9788
+ connectionString: "configured"
9270
9789
  });
9271
- try {
9272
- await manager.connect();
9273
- if (input.refreshCache) {
9274
- inspector.invalidateCache();
9790
+ if (!result.success) {
9791
+ for (const issue of result.error.issues) {
9792
+ context.addIssue(issue);
9793
+ }
9794
+ }
9795
+ }
9796
+ var relationalUpdateOperationSchema = relationalUpdateSchema.innerType().omit({ vendor: true, connectionString: true }).superRefine((input, context) => {
9797
+ preserveOperationValidation(relationalUpdateSchema, input, context);
9798
+ });
9799
+ var relationalDeleteOperationSchema = relationalDeleteSchema.innerType().omit({ vendor: true, connectionString: true }).superRefine((input, context) => {
9800
+ preserveOperationValidation(relationalDeleteSchema, input, context);
9801
+ });
9802
+ var RelationalToolSetImplementation = class {
9803
+ constructor(config, options, sharedSchemaCacheKey) {
9804
+ this.config = config;
9805
+ this.sharedSchemaCacheKey = sharedSchemaCacheKey;
9806
+ this.prefix = options.prefix;
9807
+ const configuredTools = createConfiguredTools(
9808
+ options,
9809
+ (operation, failure, failureMessage) => this.run(operation, () => failure(failureMessage))
9810
+ );
9811
+ this.query = configuredTools.query;
9812
+ this.select = configuredTools.select;
9813
+ this.insert = configuredTools.insert;
9814
+ this.update = configuredTools.update;
9815
+ this.delete = configuredTools.delete;
9816
+ this.getSchema = configuredTools.getSchema;
9817
+ this.tools = Object.freeze([
9818
+ this.query,
9819
+ this.select,
9820
+ this.insert,
9821
+ this.update,
9822
+ this.delete,
9823
+ this.getSchema
9824
+ ]);
9825
+ }
9826
+ query;
9827
+ select;
9828
+ insert;
9829
+ update;
9830
+ delete;
9831
+ getSchema;
9832
+ tools;
9833
+ schemaCache = new SchemaCache();
9834
+ prefix;
9835
+ transactionContext = new AsyncLocalStorage();
9836
+ sqliteWorkTail = Promise.resolve();
9837
+ manager;
9838
+ initialization;
9839
+ activeWork = 0;
9840
+ state = "open";
9841
+ drain;
9842
+ resolveDrain;
9843
+ disposal;
9844
+ [Symbol.iterator]() {
9845
+ return this.tools[Symbol.iterator]();
9846
+ }
9847
+ async transaction(operation, options) {
9848
+ if (this.transactionContext.getStore()?.active) {
9849
+ throw new RelationalTransactionError(
9850
+ "Nested Relational Tool Set transactions are not supported.",
9851
+ "NESTED_TRANSACTION"
9852
+ );
9275
9853
  }
9276
- const schema = await inspector.inspect({
9277
- tables: input.tables
9854
+ return this.runTransaction((manager) => {
9855
+ const transactionState = { active: true };
9856
+ return this.transactionContext.run(
9857
+ transactionState,
9858
+ () => withTransaction(
9859
+ manager,
9860
+ async (transaction) => {
9861
+ const scope = new TransactionToolScope(this.transactionExecutionFor(transaction));
9862
+ const scopedTools = createConfiguredTools(
9863
+ { prefix: this.prefix },
9864
+ (scopedOperation, failure) => scope.run(scopedOperation, failure)
9865
+ );
9866
+ let result;
9867
+ try {
9868
+ result = await operation(scopedTools);
9869
+ } finally {
9870
+ try {
9871
+ await scope.settle();
9872
+ } finally {
9873
+ transactionState.active = false;
9874
+ }
9875
+ }
9876
+ if (scope.isRollbackOnly()) {
9877
+ throw new RelationalTransactionError(
9878
+ "The transaction was rolled back because a scoped Tool failed.",
9879
+ "ROLLBACK_ONLY"
9880
+ );
9881
+ }
9882
+ return result;
9883
+ },
9884
+ options
9885
+ )
9886
+ );
9278
9887
  });
9279
- const summary = schema.tables.reduce(
9280
- (accumulator, table) => {
9281
- accumulator.tableCount += 1;
9282
- accumulator.columnCount += table.columns.length;
9283
- accumulator.foreignKeyCount += table.foreignKeys.length;
9284
- accumulator.indexCount += table.indexes.length;
9285
- return accumulator;
9286
- },
9287
- { tableCount: 0, columnCount: 0, foreignKeyCount: 0, indexCount: 0 }
9888
+ }
9889
+ refreshSchema() {
9890
+ if (this.state !== "open") {
9891
+ throw new RelationalToolSetDisposedError();
9892
+ }
9893
+ if (this.sharedSchemaCacheKey) {
9894
+ SchemaInspector.clearCache(this.sharedSchemaCacheKey);
9895
+ } else {
9896
+ this.schemaCache.delete(TOOL_SET_SCHEMA_CACHE_KEY);
9897
+ }
9898
+ }
9899
+ dispose() {
9900
+ if (!this.disposal) {
9901
+ this.state = "closing";
9902
+ this.disposal = this.finishDisposal();
9903
+ }
9904
+ return this.disposal;
9905
+ }
9906
+ async run(operation, connectionFailure) {
9907
+ if (this.transactionContext.getStore()?.active) {
9908
+ throw new RelationalTransactionError(
9909
+ "Use the scoped Tools supplied to the transaction callback.",
9910
+ "UNSCOPED_TOOL_IN_TRANSACTION"
9911
+ );
9912
+ }
9913
+ if (this.state !== "open") {
9914
+ throw new RelationalToolSetDisposedError();
9915
+ }
9916
+ this.activeWork += 1;
9917
+ try {
9918
+ let manager;
9919
+ try {
9920
+ manager = await this.ensureConnection();
9921
+ } catch (error) {
9922
+ if (error instanceof MissingPeerDependencyError) {
9923
+ throw error;
9924
+ }
9925
+ return connectionFailure();
9926
+ }
9927
+ const execute = () => operation(this.schemaExecutionFor(manager));
9928
+ return this.config.vendor === "sqlite" ? await this.serializeSqliteWork(execute) : await execute();
9929
+ } finally {
9930
+ this.activeWork -= 1;
9931
+ if (this.activeWork === 0) {
9932
+ this.resolveDrain?.();
9933
+ }
9934
+ }
9935
+ }
9936
+ async runTransaction(operation) {
9937
+ if (this.state !== "open") {
9938
+ throw new RelationalToolSetDisposedError();
9939
+ }
9940
+ this.activeWork += 1;
9941
+ try {
9942
+ let manager;
9943
+ try {
9944
+ manager = await this.ensureConnection();
9945
+ } catch (error) {
9946
+ if (error instanceof MissingPeerDependencyError) {
9947
+ throw error;
9948
+ }
9949
+ throw new RelationalTransactionError(
9950
+ "Failed to start the managed database transaction.",
9951
+ "START_FAILED"
9952
+ );
9953
+ }
9954
+ if (this.config.vendor !== "sqlite") {
9955
+ return await operation(manager);
9956
+ }
9957
+ return await this.serializeSqliteWork(() => operation(manager));
9958
+ } finally {
9959
+ this.activeWork -= 1;
9960
+ if (this.activeWork === 0) {
9961
+ this.resolveDrain?.();
9962
+ }
9963
+ }
9964
+ }
9965
+ async serializeSqliteWork(operation) {
9966
+ const work = this.sqliteWorkTail.then(operation);
9967
+ this.sqliteWorkTail = work.then(
9968
+ () => void 0,
9969
+ () => void 0
9288
9970
  );
9289
- return {
9290
- success: true,
9291
- schema,
9292
- summary
9971
+ return work;
9972
+ }
9973
+ ensureConnection() {
9974
+ if (this.manager?.isConnected()) {
9975
+ return Promise.resolve(this.manager);
9976
+ }
9977
+ if (!this.initialization) {
9978
+ const manager = new ConnectionManager(this.config);
9979
+ this.manager = manager;
9980
+ this.initialization = manager.connect().then(() => manager).catch(async (error) => {
9981
+ this.manager = void 0;
9982
+ this.initialization = void 0;
9983
+ if (!(error instanceof MissingPeerDependencyError)) {
9984
+ logger18.error("Relational Tool Set connection initialization failed", {
9985
+ vendor: this.config.vendor,
9986
+ errorType: error instanceof Error ? error.name : typeof error
9987
+ });
9988
+ }
9989
+ try {
9990
+ await manager.dispose();
9991
+ } catch {
9992
+ }
9993
+ throw error;
9994
+ });
9995
+ }
9996
+ return this.initialization;
9997
+ }
9998
+ schemaExecutionFor(manager) {
9999
+ const execution = { executor: manager, vendor: this.config.vendor };
10000
+ return this.sharedSchemaCacheKey ? { ...execution, schemaCacheKey: this.sharedSchemaCacheKey } : {
10001
+ ...execution,
10002
+ schemaCache: this.schemaCache,
10003
+ schemaCacheKey: TOOL_SET_SCHEMA_CACHE_KEY
9293
10004
  };
9294
- } catch (error) {
9295
- logger17.error("Schema introspection failed", {
9296
- vendor: input.vendor,
9297
- hasTablesFilter: Array.isArray(input.tables),
9298
- tablesFilterCount: input.tables?.length ?? 0,
9299
- refreshCache: input.refreshCache ?? false,
9300
- error: error instanceof Error ? error.message : String(error)
9301
- });
9302
- const errorMessage = isSafeGetSchemaValidationError(error) ? error.message : "Failed to inspect schema. See logs for details.";
10005
+ }
10006
+ transactionExecutionFor(transaction) {
9303
10007
  return {
9304
- success: false,
9305
- error: errorMessage,
9306
- schema: null
10008
+ executor: transaction,
10009
+ transaction,
10010
+ vendor: this.config.vendor
9307
10011
  };
9308
- } finally {
9309
- await manager.disconnect();
9310
10012
  }
9311
- }).build();
10013
+ async finishDisposal() {
10014
+ if (this.activeWork > 0) {
10015
+ this.drain = new Promise((resolve3) => {
10016
+ this.resolveDrain = resolve3;
10017
+ });
10018
+ await this.drain;
10019
+ }
10020
+ this.schemaCache.clear();
10021
+ try {
10022
+ await this.manager?.dispose();
10023
+ } finally {
10024
+ this.state = "closed";
10025
+ this.resolveDrain = void 0;
10026
+ }
10027
+ }
10028
+ };
10029
+ function createRelationalToolSet(config, options = {}) {
10030
+ validateConfiguration(config, options);
10031
+ return new RelationalToolSetImplementation(snapshotConfiguration(config), { ...options });
10032
+ }
10033
+ function createLegacyRelationalReadToolSet(config, options = {}, schemaCacheKey) {
10034
+ return new RelationalToolSetImplementation(
10035
+ snapshotConfiguration(config),
10036
+ { ...options },
10037
+ schemaCacheKey
10038
+ );
10039
+ }
10040
+ function createLegacyRelationalMutationToolSet(config) {
10041
+ return new RelationalToolSetImplementation(snapshotConfiguration(config), {});
10042
+ }
9312
10043
  var fileReaderSchema = z.object({
9313
10044
  path: z.string().describe("Path to the file to read"),
9314
10045
  encoding: z.enum(["utf8", "utf-8", "ascii", "base64", "hex", "binary"]).default("utf8").describe("File encoding")
@@ -10640,7 +11371,7 @@ var AskHumanInputSchema = z.object({
10640
11371
  suggestions: z.array(z.string()).optional().describe("Suggested responses for the human")
10641
11372
  });
10642
11373
  var logLevel3 = process.env.LOG_LEVEL?.toLowerCase() || LogLevel.INFO;
10643
- var logger18 = createLogger("agentforge:tools:agent:ask-human", { level: logLevel3 });
11374
+ var logger19 = createLogger("agentforge:tools:agent:ask-human", { level: logLevel3 });
10644
11375
  function isRecord3(value) {
10645
11376
  return typeof value === "object" && value !== null;
10646
11377
  }
@@ -10705,7 +11436,7 @@ function createAskHumanTool() {
10705
11436
  suggestions: validatedInput.suggestions,
10706
11437
  status: "pending"
10707
11438
  };
10708
- logger18.debug("About to call interrupt()", {
11439
+ logger19.debug("About to call interrupt()", {
10709
11440
  humanRequest: {
10710
11441
  id: humanRequest.id,
10711
11442
  question: humanRequest.question,
@@ -10721,13 +11452,13 @@ function createAskHumanTool() {
10721
11452
  let response;
10722
11453
  try {
10723
11454
  response = interrupt(humanRequest);
10724
- logger18.debug("interrupt() returned successfully", {
11455
+ logger19.debug("interrupt() returned successfully", {
10725
11456
  responseType: typeof response,
10726
11457
  hasResponse: response != null,
10727
11458
  ...typeof response === "string" ? { responseLength: response.length } : {}
10728
11459
  });
10729
11460
  } catch (error) {
10730
- logger18.debug("interrupt() threw error (expected for GraphInterrupt)", {
11461
+ logger19.debug("interrupt() threw error (expected for GraphInterrupt)", {
10731
11462
  ...error && typeof error === "object" && "constructor" in error ? { errorType: error.constructor?.name } : {},
10732
11463
  error: error instanceof Error ? error.message : String(error)
10733
11464
  });
@@ -10752,6 +11483,6 @@ function createAskHumanTool() {
10752
11483
  }
10753
11484
  var askHumanTool = createAskHumanTool();
10754
11485
 
10755
- export { AskHumanInputSchema, CalculatorSchema, ConnectionManager, ConnectionState, CreditCardValidatorSchema, CurrentDateTimeSchema, DEFAULT_BATCH_SIZE, DEFAULT_CHUNK_SIZE, DEFAULT_DESTINATION_POLICY, DEFAULT_FILE_SYSTEM_POLICY, DEFAULT_RETRY_CONFIG2 as DEFAULT_RETRY_CONFIG, DateArithmeticSchema, DateComparisonSchema, DateDifferenceSchema, DateFormatterSchema, DestinationPolicyError, DuckDuckGoProvider, EmailValidatorSchema, EmbeddingManager, FileSystemPolicy, FileSystemPolicyError, HttpMethod, IpValidatorSchema, MAX_BATCH_SIZE, MathFunctionsSchema, MissingPeerDependencyError, OpenAIEmbeddingProvider, PhoneValidatorSchema, RandomNumberSchema, SchemaInspector, SerperProvider, StatisticsSchema, StringCaseConverterSchema, StringJoinSchema, StringLengthSchema, StringReplaceSchema, StringSplitSchema, StringSubstringSchema, StringTrimSchema, UrlValidatorSimpleSchema, UuidValidatorSchema, archiveConfluencePage, arrayFilter, arrayFilterSchema, arrayGroupBy, arrayGroupBySchema, arrayMap, arrayMapSchema, arraySort, arraySortSchema, askHumanTool, assertDestinationAllowed, benchmarkBatchExecution, benchmarkStreamingSelectMemory, buildDeleteQuery, buildInsertQuery, buildSelectQuery, buildUpdateQuery, calculator, checkPeerDependency, 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, createFileSystemPolicy, createFileWriterTool, createHtmlParserTool, createHtmlParserTools, createHttpTools, createIpValidatorTool, createJsonMergeTool, createJsonParserTool, createJsonQueryTool, createJsonStringifyTool, createJsonToCsvTool, createJsonToXmlTool, createJsonTools, createJsonValidatorTool, createMathFunctionsTool, createMathOperationTools, createModelSafeToolPreset, createNeo4jCreateNodeWithEmbeddingTool, createNeo4jFindNodesTool, createNeo4jGetSchemaTool, createNeo4jQueryTool, createNeo4jTools, createNeo4jTraverseTool, createNeo4jVectorSearchTool, createNeo4jVectorSearchWithEmbeddingTool, createObjectOmitTool, createObjectPickTool, createPathBasenameTool, createPathDirnameTool, createPathExtensionTool, createPathJoinTool, createPathNormalizeTool, createPathParseTool, createPathRelativeTool, createPathResolveTool, createPathUtilityTools, createPhoneValidatorTool, createRandomNumberTool, createScraperTools, createSelectReadableStream, 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, diffSchemas, directoryCreate, directoryCreateSchema, directoryDelete, directoryDeleteSchema, directoryList, directoryListSchema, directoryOperationTools, emailValidator, embeddingManager, executeBatchedTask, executeQuery2 as executeQuery, executeStreamingSelect, exportSchemaToJson, extractImages, extractImagesSchema, extractLinks, extractLinksSchema, fileAppend, fileAppendSchema, fileDelete, fileDeleteSchema, fileExists, fileExistsSchema, fileOperationTools, fileReader, fileReaderSchema, fileSearch, fileSearchSchema, fileWriter, fileWriterSchema, generateBatchEmbeddings, generateEmbedding, getCohereApiKey, getConfluencePage, getEmbeddingModel, getEmbeddingProvider, getHuggingFaceApiKey, getInstallationInstructions, getOllamaBaseUrl, getOpenAIApiKey, getPeerDependencyName, getSlackChannels, getSlackMessages, getSpacePages, getVendorTypeMap, getVoyageApiKey, htmlParser, htmlParserSchema, htmlParserTools, httpClient, httpGet, httpGetSchema, httpPost, httpPostSchema, httpRequestSchema, httpTools, importSchemaFromJson, initializeEmbeddings, initializeEmbeddingsWithConfig, initializeFromEnv, initializeNeo4jTools, ipValidator, isRetryableError2 as isRetryableError, jsonMerge, jsonMergeSchema, jsonParser, jsonParserSchema, jsonQuery, jsonQuerySchema, jsonStringify, jsonStringifySchema, jsonToCsv, jsonToCsvSchema, jsonToXml, jsonToXmlSchema, jsonTools, jsonValidator, jsonValidatorSchema, listConfluenceSpaces, mapColumnType, mapSchemaTypes, 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, relationalDelete, relationalGetSchema, relationalInsert, relationalQuery, relationalSelect, relationalUpdate, requestWithDestinationPolicy, resolveFileSystemPolicy, retryWithBackoff2 as retryWithBackoff, scraperTools, searchConfluence, searchResultSchema, sendSlackMessage, slackTools, statistics, streamSelectChunks, stringCaseConverter, stringJoin, stringLength, stringReplace, stringSplit, stringSubstring, stringTrim, stringUtilityTools, transformerArraySchema, transformerObjectSchema, transformerTools, transformerValueSchema, updateConfluencePage, urlBuilder, urlBuilderSchema, urlQueryParser, urlQueryParserSchema, urlValidator, urlValidatorSchema, urlValidatorSimple, urlValidatorTools, uuidValidator, validateBatch, validateColumnTypes, validateColumnsExist, validateTableExists, validateText, validationTools, webScraper, webScraperSchema, webSearch, webSearchOutputSchema, webSearchSchema, withProviderErrorMetadata, withTransaction, xmlGenerator, xmlGeneratorSchema, xmlParser, xmlParserSchema, xmlToJson, xmlToJsonSchema, xmlTools };
11486
+ export { AskHumanInputSchema, CalculatorSchema, ConnectionManager, ConnectionState, CreditCardValidatorSchema, CurrentDateTimeSchema, DEFAULT_BATCH_SIZE, DEFAULT_CHUNK_SIZE, DEFAULT_DESTINATION_POLICY, DEFAULT_FILE_SYSTEM_POLICY, DEFAULT_RETRY_CONFIG2 as DEFAULT_RETRY_CONFIG, DateArithmeticSchema, DateComparisonSchema, DateDifferenceSchema, DateFormatterSchema, DestinationPolicyError, DuckDuckGoProvider, EmailValidatorSchema, EmbeddingManager, FileSystemPolicy, FileSystemPolicyError, HttpMethod, IpValidatorSchema, MAX_BATCH_SIZE, MathFunctionsSchema, MissingPeerDependencyError, OpenAIEmbeddingProvider, PhoneValidatorSchema, RandomNumberSchema, RelationalToolSetConfigurationError, RelationalToolSetDisposedError, RelationalTransactionError, SchemaCache, SchemaInspector, SerperProvider, StatisticsSchema, StringCaseConverterSchema, StringJoinSchema, StringLengthSchema, StringReplaceSchema, StringSplitSchema, StringSubstringSchema, StringTrimSchema, UrlValidatorSimpleSchema, UuidValidatorSchema, archiveConfluencePage, arrayFilter, arrayFilterSchema, arrayGroupBy, arrayGroupBySchema, arrayMap, arrayMapSchema, arraySort, arraySortSchema, askHumanTool, assertDestinationAllowed, benchmarkBatchExecution, benchmarkStreamingSelectMemory, buildDeleteQuery, buildInsertQuery, buildSelectQuery, buildUpdateQuery, calculator, checkPeerDependency, 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, createFileSystemPolicy, createFileWriterTool, createHtmlParserTool, createHtmlParserTools, createHttpTools, createIpValidatorTool, createJsonMergeTool, createJsonParserTool, createJsonQueryTool, createJsonStringifyTool, createJsonToCsvTool, createJsonToXmlTool, createJsonTools, createJsonValidatorTool, createMathFunctionsTool, createMathOperationTools, createModelSafeToolPreset, createNeo4jCreateNodeWithEmbeddingTool, createNeo4jFindNodesTool, createNeo4jGetSchemaTool, createNeo4jQueryTool, createNeo4jTools, createNeo4jTraverseTool, createNeo4jVectorSearchTool, createNeo4jVectorSearchWithEmbeddingTool, createObjectOmitTool, createObjectPickTool, createPathBasenameTool, createPathDirnameTool, createPathExtensionTool, createPathJoinTool, createPathNormalizeTool, createPathParseTool, createPathRelativeTool, createPathResolveTool, createPathUtilityTools, createPhoneValidatorTool, createRandomNumberTool, createRelationalToolSet, createScraperTools, createSelectReadableStream, 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, diffSchemas, directoryCreate, directoryCreateSchema, directoryDelete, directoryDeleteSchema, directoryList, directoryListSchema, directoryOperationTools, emailValidator, embeddingManager, executeBatchedTask, executeQuery2 as executeQuery, executeStreamingSelect, exportSchemaToJson, extractImages, extractImagesSchema, extractLinks, extractLinksSchema, fileAppend, fileAppendSchema, fileDelete, fileDeleteSchema, fileExists, fileExistsSchema, fileOperationTools, fileReader, fileReaderSchema, fileSearch, fileSearchSchema, fileWriter, fileWriterSchema, generateBatchEmbeddings, generateEmbedding, getCohereApiKey, getConfluencePage, getEmbeddingModel, getEmbeddingProvider, getHuggingFaceApiKey, getInstallationInstructions, getOllamaBaseUrl, getOpenAIApiKey, getPeerDependencyName, getSlackChannels, getSlackMessages, getSpacePages, getVendorTypeMap, getVoyageApiKey, htmlParser, htmlParserSchema, htmlParserTools, httpClient, httpGet, httpGetSchema, httpPost, httpPostSchema, httpRequestSchema, httpTools, importSchemaFromJson, initializeEmbeddings, initializeEmbeddingsWithConfig, initializeFromEnv, initializeNeo4jTools, ipValidator, isRetryableError2 as isRetryableError, jsonMerge, jsonMergeSchema, jsonParser, jsonParserSchema, jsonQuery, jsonQuerySchema, jsonStringify, jsonStringifySchema, jsonToCsv, jsonToCsvSchema, jsonToXml, jsonToXmlSchema, jsonTools, jsonValidator, jsonValidatorSchema, listConfluenceSpaces, mapColumnType, mapSchemaTypes, 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, relationalDelete, relationalGetSchema, relationalInsert, relationalQuery, relationalSelect, relationalUpdate, requestWithDestinationPolicy, resolveFileSystemPolicy, retryWithBackoff2 as retryWithBackoff, scraperTools, searchConfluence, searchResultSchema, sendSlackMessage, slackTools, statistics, streamSelectChunks, stringCaseConverter, stringJoin, stringLength, stringReplace, stringSplit, stringSubstring, stringTrim, stringUtilityTools, transformerArraySchema, transformerObjectSchema, transformerTools, transformerValueSchema, updateConfluencePage, urlBuilder, urlBuilderSchema, urlQueryParser, urlQueryParserSchema, urlValidator, urlValidatorSchema, urlValidatorSimple, urlValidatorTools, uuidValidator, validateBatch, validateColumnTypes, validateColumnsExist, validateTableExists, validateText, validationTools, webScraper, webScraperSchema, webSearch, webSearchOutputSchema, webSearchSchema, withProviderErrorMetadata, withTransaction, xmlGenerator, xmlGeneratorSchema, xmlParser, xmlParserSchema, xmlToJson, xmlToJsonSchema, xmlTools };
10756
11487
  //# sourceMappingURL=index.js.map
10757
11488
  //# sourceMappingURL=index.js.map