@agentforge/tools 0.8.2 → 0.9.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
@@ -2,6 +2,7 @@ import { toolBuilder, ToolCategory, LogLevel, createLogger } from '@agentforge/c
2
2
  import { z } from 'zod';
3
3
  import axios from 'axios';
4
4
  import * as cheerio2 from 'cheerio';
5
+ import { WebClient } from '@slack/web-api';
5
6
  import { parse } from 'csv-parse/sync';
6
7
  import { stringify } from 'csv-stringify/sync';
7
8
  import { XMLParser, XMLBuilder } from 'fast-xml-parser';
@@ -656,6 +657,381 @@ var webSearch = toolBuilder().name("web-search").description(
656
657
  };
657
658
  }
658
659
  }).build();
660
+ var logLevel = process.env.LOG_LEVEL?.toLowerCase() || LogLevel.INFO;
661
+ var logger = createLogger("tools:slack", { level: logLevel });
662
+ var defaultSlackClient = null;
663
+ function getDefaultSlackClient() {
664
+ if (!defaultSlackClient) {
665
+ const token = process.env.SLACK_USER_TOKEN || process.env.SLACK_BOT_TOKEN;
666
+ if (!token) {
667
+ throw new Error(
668
+ "Slack token not configured. Please set SLACK_USER_TOKEN or SLACK_BOT_TOKEN environment variable."
669
+ );
670
+ }
671
+ defaultSlackClient = new WebClient(token);
672
+ }
673
+ return defaultSlackClient;
674
+ }
675
+ var sendSlackMessage = 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(
676
+ "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."
677
+ ).suggests(["get-slack-channels"]).schema(
678
+ z.object({
679
+ channel: z.string().describe("Channel name (e.g., 'general') or ID (e.g., 'C123456')"),
680
+ message: z.string().describe("Message content to send")
681
+ })
682
+ ).implementSafe(async ({ channel, message }) => {
683
+ logger.info("send-slack-message called", { channel, messageLength: message.length });
684
+ try {
685
+ const slack = getDefaultSlackClient();
686
+ const result = await slack.chat.postMessage({
687
+ channel,
688
+ text: message,
689
+ username: "AgentForge Bot",
690
+ icon_emoji: ":robot_face:"
691
+ });
692
+ logger.info("send-slack-message result", {
693
+ channel: result.channel,
694
+ timestamp: result.ts,
695
+ messageLength: message.length,
696
+ success: true
697
+ });
698
+ return {
699
+ channel: result.channel,
700
+ message,
701
+ timestamp: result.ts,
702
+ message_id: result.ts
703
+ };
704
+ } catch (error) {
705
+ logger.error("send-slack-message failed", {
706
+ channel,
707
+ error: error.message,
708
+ data: error.data
709
+ });
710
+ throw error;
711
+ }
712
+ }).build();
713
+ var notifySlack = 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(
714
+ "Use this for urgent notifications that require @mentions. For general messages without mentions, use send-slack-message instead."
715
+ ).suggests(["get-slack-channels"]).schema(
716
+ z.object({
717
+ channel: z.string().describe("Channel name or ID"),
718
+ message: z.string().describe("Notification message"),
719
+ mentions: z.array(z.string()).optional().describe("List of usernames to mention (without @)")
720
+ })
721
+ ).implementSafe(async ({ channel, message, mentions = [] }) => {
722
+ logger.info("notify-slack called", {
723
+ channel,
724
+ messageLength: message.length,
725
+ mentionCount: mentions.length
726
+ });
727
+ const slack = getDefaultSlackClient();
728
+ const mentionText = mentions.length > 0 ? mentions.map((m) => `<@${m}>`).join(" ") + " " : "";
729
+ const fullMessage = `${mentionText}${message}`;
730
+ const result = await slack.chat.postMessage({
731
+ channel,
732
+ text: fullMessage,
733
+ username: "AgentForge Bot",
734
+ icon_emoji: ":robot_face:"
735
+ });
736
+ logger.info("notify-slack result", {
737
+ channel: result.channel,
738
+ timestamp: result.ts,
739
+ mentions: mentions.length
740
+ });
741
+ return {
742
+ channel: result.channel,
743
+ message: fullMessage,
744
+ mentions,
745
+ timestamp: result.ts,
746
+ notification_id: result.ts
747
+ };
748
+ }).build();
749
+ var getSlackChannels = 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(
750
+ "Use this first to discover available channels before sending messages. Helps ensure you are sending to the correct channel."
751
+ ).follows(["send-slack-message", "notify-slack"]).schema(
752
+ z.object({
753
+ include_private: z.boolean().optional().describe("Include private channels (default: false)")
754
+ })
755
+ ).implementSafe(async ({ include_private = false }) => {
756
+ logger.info("get-slack-channels called", { include_private });
757
+ const slack = getDefaultSlackClient();
758
+ const publicChannels = await slack.conversations.list({
759
+ types: "public_channel",
760
+ exclude_archived: true
761
+ });
762
+ let allChannels = publicChannels.channels || [];
763
+ if (include_private) {
764
+ const privateChannels = await slack.conversations.list({
765
+ types: "private_channel",
766
+ exclude_archived: true
767
+ });
768
+ allChannels = [...allChannels, ...privateChannels.channels || []];
769
+ }
770
+ logger.info("get-slack-channels result", {
771
+ channelCount: allChannels.length,
772
+ includePrivate: include_private
773
+ });
774
+ return {
775
+ count: allChannels.length,
776
+ channels: allChannels.map((c) => ({
777
+ id: c.id,
778
+ name: c.name,
779
+ is_private: c.is_private || false,
780
+ num_members: c.num_members || 0
781
+ }))
782
+ };
783
+ }).build();
784
+ var getSlackMessages = 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(
785
+ "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)."
786
+ ).suggests(["get-slack-channels"]).schema(
787
+ z.object({
788
+ channel: z.string().describe("Channel name (e.g., 'general') or ID (e.g., 'C123456')"),
789
+ limit: z.number().int().min(1).max(100).optional().describe("Number of messages to retrieve (default: 20, max: 100)")
790
+ })
791
+ ).implementSafe(async ({ channel, limit = 20 }) => {
792
+ logger.info("get-slack-messages called", { channel, limit });
793
+ try {
794
+ const slack = getDefaultSlackClient();
795
+ let channelId = channel;
796
+ if (!channel.startsWith("C") && !channel.startsWith("D")) {
797
+ const channels = await slack.conversations.list({
798
+ types: "public_channel,private_channel",
799
+ exclude_archived: true
800
+ });
801
+ const found = channels.channels?.find((c) => c.name === channel);
802
+ if (!found) {
803
+ logger.error("get-slack-messages: channel not found", { channel });
804
+ throw new Error(
805
+ `Channel '${channel}' not found. Use get-slack-channels to see available channels.`
806
+ );
807
+ }
808
+ channelId = found.id;
809
+ }
810
+ const result = await slack.conversations.history({
811
+ channel: channelId,
812
+ limit: Math.min(limit, 100)
813
+ // Cap at 100 for performance
814
+ });
815
+ logger.info("get-slack-messages result", {
816
+ channel: channelId,
817
+ messageCount: result.messages?.length || 0,
818
+ limit
819
+ });
820
+ return {
821
+ channel: channelId,
822
+ count: result.messages?.length || 0,
823
+ messages: result.messages?.map((m) => ({
824
+ user: m.user || "unknown",
825
+ text: m.text || "",
826
+ timestamp: m.ts,
827
+ thread_ts: m.thread_ts,
828
+ type: m.type,
829
+ subtype: m.subtype
830
+ })) || []
831
+ };
832
+ } catch (error) {
833
+ logger.error("get-slack-messages failed", {
834
+ channel,
835
+ error: error.message,
836
+ data: error.data
837
+ });
838
+ throw error;
839
+ }
840
+ }).build();
841
+ function createSlackTools(config = {}) {
842
+ const {
843
+ token,
844
+ botName = "AgentForge Bot",
845
+ botIcon = ":robot_face:",
846
+ logLevel: customLogLevel
847
+ } = config;
848
+ let configuredClient = null;
849
+ function getConfiguredSlackClient() {
850
+ if (!configuredClient) {
851
+ const slackToken = token || process.env.SLACK_USER_TOKEN || process.env.SLACK_BOT_TOKEN;
852
+ if (!slackToken) {
853
+ throw new Error(
854
+ "Slack token not configured. Please provide a token in config or set SLACK_USER_TOKEN or SLACK_BOT_TOKEN environment variable."
855
+ );
856
+ }
857
+ configuredClient = new WebClient(slackToken);
858
+ }
859
+ return configuredClient;
860
+ }
861
+ const toolLogger = customLogLevel ? createLogger("tools:slack", { level: customLogLevel }) : logger;
862
+ const sendMessage = 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(
863
+ "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."
864
+ ).suggests(["get-slack-channels"]).schema(
865
+ z.object({
866
+ channel: z.string().describe("Channel name (e.g., 'general') or ID (e.g., 'C123456')"),
867
+ message: z.string().describe("Message content to send")
868
+ })
869
+ ).implementSafe(async ({ channel, message }) => {
870
+ toolLogger.info("send-slack-message called", { channel, messageLength: message.length });
871
+ try {
872
+ const slack = getConfiguredSlackClient();
873
+ const result = await slack.chat.postMessage({
874
+ channel,
875
+ text: message,
876
+ username: botName,
877
+ icon_emoji: botIcon
878
+ });
879
+ toolLogger.info("send-slack-message result", {
880
+ channel: result.channel,
881
+ timestamp: result.ts,
882
+ messageLength: message.length,
883
+ success: true
884
+ });
885
+ return {
886
+ channel: result.channel,
887
+ message,
888
+ timestamp: result.ts,
889
+ message_id: result.ts
890
+ };
891
+ } catch (error) {
892
+ toolLogger.error("send-slack-message failed", {
893
+ channel,
894
+ error: error.message,
895
+ data: error.data
896
+ });
897
+ throw error;
898
+ }
899
+ }).build();
900
+ const notify = 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(
901
+ "Use this for urgent notifications that require @mentions. For general messages without mentions, use send-slack-message instead."
902
+ ).suggests(["get-slack-channels"]).schema(
903
+ z.object({
904
+ channel: z.string().describe("Channel name or ID"),
905
+ message: z.string().describe("Notification message"),
906
+ mentions: z.array(z.string()).optional().describe("List of usernames to mention (without @)")
907
+ })
908
+ ).implementSafe(async ({ channel, message, mentions = [] }) => {
909
+ toolLogger.info("notify-slack called", {
910
+ channel,
911
+ messageLength: message.length,
912
+ mentionCount: mentions.length
913
+ });
914
+ const slack = getConfiguredSlackClient();
915
+ const mentionText = mentions.length > 0 ? mentions.map((m) => `<@${m}>`).join(" ") + " " : "";
916
+ const fullMessage = `${mentionText}${message}`;
917
+ const result = await slack.chat.postMessage({
918
+ channel,
919
+ text: fullMessage,
920
+ username: botName,
921
+ icon_emoji: botIcon
922
+ });
923
+ toolLogger.info("notify-slack result", {
924
+ channel: result.channel,
925
+ timestamp: result.ts,
926
+ mentions: mentions.length
927
+ });
928
+ return {
929
+ channel: result.channel,
930
+ message: fullMessage,
931
+ mentions,
932
+ timestamp: result.ts,
933
+ notification_id: result.ts
934
+ };
935
+ }).build();
936
+ const getChannels = 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(
937
+ "Use this first to discover available channels before sending messages. Helps ensure you are sending to the correct channel."
938
+ ).follows(["send-slack-message", "notify-slack"]).schema(
939
+ z.object({
940
+ include_private: z.boolean().optional().describe("Include private channels (default: false)")
941
+ })
942
+ ).implementSafe(async ({ include_private = false }) => {
943
+ toolLogger.info("get-slack-channels called", { include_private });
944
+ const slack = getConfiguredSlackClient();
945
+ const publicChannels = await slack.conversations.list({
946
+ types: "public_channel",
947
+ exclude_archived: true
948
+ });
949
+ let allChannels = publicChannels.channels || [];
950
+ if (include_private) {
951
+ const privateChannels = await slack.conversations.list({
952
+ types: "private_channel",
953
+ exclude_archived: true
954
+ });
955
+ allChannels = [...allChannels, ...privateChannels.channels || []];
956
+ }
957
+ toolLogger.info("get-slack-channels result", {
958
+ channelCount: allChannels.length,
959
+ includePrivate: include_private
960
+ });
961
+ return {
962
+ count: allChannels.length,
963
+ channels: allChannels.map((c) => ({
964
+ id: c.id,
965
+ name: c.name,
966
+ is_private: c.is_private || false,
967
+ num_members: c.num_members || 0
968
+ }))
969
+ };
970
+ }).build();
971
+ const getMessages = 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(
972
+ "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)."
973
+ ).suggests(["get-slack-channels"]).schema(
974
+ z.object({
975
+ channel: z.string().describe("Channel name (e.g., 'general') or ID (e.g., 'C123456')"),
976
+ limit: z.number().int().min(1).max(100).optional().describe("Number of messages to retrieve (default: 20, max: 100)")
977
+ })
978
+ ).implementSafe(async ({ channel, limit = 20 }) => {
979
+ toolLogger.info("get-slack-messages called", { channel, limit });
980
+ try {
981
+ const slack = getConfiguredSlackClient();
982
+ let channelId = channel;
983
+ if (!channel.startsWith("C") && !channel.startsWith("D")) {
984
+ const channels = await slack.conversations.list({
985
+ types: "public_channel,private_channel",
986
+ exclude_archived: true
987
+ });
988
+ const found = channels.channels?.find((c) => c.name === channel);
989
+ if (!found) {
990
+ toolLogger.error("get-slack-messages: channel not found", { channel });
991
+ throw new Error(
992
+ `Channel '${channel}' not found. Use get-slack-channels to see available channels.`
993
+ );
994
+ }
995
+ channelId = found.id;
996
+ }
997
+ const result = await slack.conversations.history({
998
+ channel: channelId,
999
+ limit: Math.min(limit, 100)
1000
+ });
1001
+ toolLogger.info("get-slack-messages result", {
1002
+ channel: channelId,
1003
+ messageCount: result.messages?.length || 0,
1004
+ limit
1005
+ });
1006
+ return {
1007
+ channel: channelId,
1008
+ count: result.messages?.length || 0,
1009
+ messages: result.messages?.map((m) => ({
1010
+ user: m.user || "unknown",
1011
+ text: m.text || "",
1012
+ timestamp: m.ts,
1013
+ thread_ts: m.thread_ts,
1014
+ type: m.type,
1015
+ subtype: m.subtype
1016
+ })) || []
1017
+ };
1018
+ } catch (error) {
1019
+ toolLogger.error("get-slack-messages failed", {
1020
+ channel,
1021
+ error: error.message,
1022
+ data: error.data
1023
+ });
1024
+ throw error;
1025
+ }
1026
+ }).build();
1027
+ return {
1028
+ sendMessage,
1029
+ notify,
1030
+ getChannels,
1031
+ getMessages
1032
+ };
1033
+ }
1034
+ var slackTools = [sendSlackMessage, notifySlack, getSlackChannels, getSlackMessages];
659
1035
  var jsonParser = toolBuilder().name("json-parser").description("Parse JSON string into an object. Validates JSON syntax and returns parsed data or error details.").category(ToolCategory.UTILITY).tags(["json", "parse", "data"]).schema(z.object({
660
1036
  json: z.string().describe("JSON string to parse"),
661
1037
  strict: z.boolean().default(true).describe("Use strict JSON parsing (no trailing commas, etc.)")
@@ -1880,8 +2256,8 @@ var AskHumanInputSchema = z.object({
1880
2256
  */
1881
2257
  suggestions: z.array(z.string()).optional().describe("Suggested responses for the human")
1882
2258
  });
1883
- var logLevel = process.env.LOG_LEVEL?.toLowerCase() || LogLevel.INFO;
1884
- var logger = createLogger("askHuman", { level: logLevel });
2259
+ var logLevel2 = process.env.LOG_LEVEL?.toLowerCase() || LogLevel.INFO;
2260
+ var logger2 = createLogger("askHuman", { level: logLevel2 });
1885
2261
  function createAskHumanTool() {
1886
2262
  return toolBuilder().name("ask-human").description(
1887
2263
  "Ask a human for input or approval. Use this when you need human guidance, approval for a critical action, or clarification on ambiguous requirements. The agent execution will pause until the human responds."
@@ -1914,13 +2290,13 @@ function createAskHumanTool() {
1914
2290
  suggestions: validatedInput.suggestions,
1915
2291
  status: "pending"
1916
2292
  };
1917
- logger.debug("About to call interrupt()", { humanRequest });
2293
+ logger2.debug("About to call interrupt()", { humanRequest });
1918
2294
  let response;
1919
2295
  try {
1920
2296
  response = interrupt(humanRequest);
1921
- logger.debug("interrupt() returned successfully", { response, responseType: typeof response });
2297
+ logger2.debug("interrupt() returned successfully", { response, responseType: typeof response });
1922
2298
  } catch (error) {
1923
- logger.debug("interrupt() threw error (expected for GraphInterrupt)", {
2299
+ logger2.debug("interrupt() threw error (expected for GraphInterrupt)", {
1924
2300
  errorType: error?.constructor?.name,
1925
2301
  error: error instanceof Error ? error.message : String(error)
1926
2302
  });
@@ -1945,6 +2321,6 @@ function createAskHumanTool() {
1945
2321
  }
1946
2322
  var askHumanTool = createAskHumanTool();
1947
2323
 
1948
- export { AskHumanInputSchema, DuckDuckGoProvider, SerperProvider, arrayFilter, arrayGroupBy, arrayMap, arraySort, askHumanTool, calculator, createAskHumanTool, createDuckDuckGoProvider, createSerperProvider, creditCardValidator, csvGenerator, csvParser, csvToJson, currentDateTime, dateArithmetic, dateComparison, dateDifference, dateFormatter, directoryCreate, directoryDelete, directoryList, emailValidator, extractImages, extractLinks, fileAppend, fileDelete, fileExists, fileReader, fileSearch, fileWriter, htmlParser, httpClient, httpGet, httpPost, ipValidator, jsonMerge, jsonParser, jsonQuery, jsonStringify, jsonToCsv, jsonToXml, jsonValidator, mathFunctions, objectOmit, objectPick, pathBasename, pathDirname, pathExtension, pathJoin, pathNormalize, pathParse, pathRelative, pathResolve, phoneValidator, randomNumber, searchResultSchema, statistics, stringCaseConverter, stringJoin, stringLength, stringReplace, stringSplit, stringSubstring, stringTrim, urlBuilder, urlQueryParser, urlValidator, urlValidatorSimple, uuidValidator, webScraper, webSearch, webSearchOutputSchema, webSearchSchema, xmlGenerator, xmlParser, xmlToJson };
2324
+ export { AskHumanInputSchema, DuckDuckGoProvider, SerperProvider, arrayFilter, arrayGroupBy, arrayMap, arraySort, askHumanTool, calculator, createAskHumanTool, createDuckDuckGoProvider, createSerperProvider, createSlackTools, creditCardValidator, csvGenerator, csvParser, csvToJson, currentDateTime, dateArithmetic, dateComparison, dateDifference, dateFormatter, directoryCreate, directoryDelete, directoryList, emailValidator, extractImages, extractLinks, fileAppend, fileDelete, fileExists, fileReader, fileSearch, fileWriter, getSlackChannels, getSlackMessages, htmlParser, httpClient, httpGet, httpPost, ipValidator, jsonMerge, jsonParser, jsonQuery, jsonStringify, jsonToCsv, jsonToXml, jsonValidator, mathFunctions, notifySlack, objectOmit, objectPick, pathBasename, pathDirname, pathExtension, pathJoin, pathNormalize, pathParse, pathRelative, pathResolve, phoneValidator, randomNumber, searchResultSchema, sendSlackMessage, slackTools, statistics, stringCaseConverter, stringJoin, stringLength, stringReplace, stringSplit, stringSubstring, stringTrim, urlBuilder, urlQueryParser, urlValidator, urlValidatorSimple, uuidValidator, webScraper, webSearch, webSearchOutputSchema, webSearchSchema, xmlGenerator, xmlParser, xmlToJson };
1949
2325
  //# sourceMappingURL=index.js.map
1950
2326
  //# sourceMappingURL=index.js.map