@agentforge/core 0.15.5 → 0.15.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -794,21 +794,42 @@ function toolBuilder() {
794
794
  // src/langchain/converter.ts
795
795
  var import_tools = require("@langchain/core/tools");
796
796
  var import_zod_to_json_schema = require("zod-to-json-schema");
797
+ function serializeToolResult(result) {
798
+ if (typeof result === "string") {
799
+ return result;
800
+ }
801
+ if (typeof result === "object" && result !== null) {
802
+ return JSON.stringify(result, null, 2);
803
+ }
804
+ return String(result);
805
+ }
806
+ function isJsonSchemaObject(value) {
807
+ return typeof value === "object" && value !== null && !Array.isArray(value);
808
+ }
809
+ function isJsonSchemaDefinitionMap(value) {
810
+ if (!isJsonSchemaObject(value)) {
811
+ return false;
812
+ }
813
+ return Object.values(value).every(isJsonSchemaObject);
814
+ }
815
+ function extractToolSchema(jsonSchema) {
816
+ if (!isJsonSchemaObject(jsonSchema)) {
817
+ return {};
818
+ }
819
+ const ref = jsonSchema.$ref;
820
+ const definitions = jsonSchema.definitions;
821
+ if (typeof ref !== "string" || !isJsonSchemaDefinitionMap(definitions)) {
822
+ return jsonSchema;
823
+ }
824
+ const refName = ref.replace("#/definitions/", "");
825
+ return definitions[refName] ?? jsonSchema;
826
+ }
797
827
  function toLangChainTool(tool) {
798
828
  return new import_tools.DynamicStructuredTool({
799
829
  name: tool.metadata.name,
800
830
  description: tool.metadata.description,
801
831
  schema: tool.schema,
802
- func: async (input) => {
803
- const result = await tool.invoke(input);
804
- if (typeof result === "string") {
805
- return result;
806
- }
807
- if (typeof result === "object" && result !== null) {
808
- return JSON.stringify(result, null, 2);
809
- }
810
- return String(result);
811
- }
832
+ func: async (input) => serializeToolResult(await tool.invoke(input))
812
833
  });
813
834
  }
814
835
  function toLangChainTools(tools) {
@@ -820,11 +841,7 @@ function getToolJsonSchema(tool) {
820
841
  $refStrategy: "none"
821
842
  // Don't use $ref for nested schemas
822
843
  });
823
- if (jsonSchema.$ref && jsonSchema.definitions) {
824
- const refName = jsonSchema.$ref.replace("#/definitions/", "");
825
- return jsonSchema.definitions[refName] || jsonSchema;
826
- }
827
- return jsonSchema;
844
+ return extractToolSchema(jsonSchema);
828
845
  }
829
846
  function getToolDescription(tool) {
830
847
  const { metadata } = tool;
@@ -1566,7 +1583,7 @@ function createToolExecutor(config = {}) {
1566
1583
  }
1567
1584
  return Math.min(delay, maxDelay);
1568
1585
  }
1569
- function toError(error) {
1586
+ function toError2(error) {
1570
1587
  if (error instanceof Error) {
1571
1588
  return error;
1572
1589
  }
@@ -1597,7 +1614,7 @@ function createToolExecutor(config = {}) {
1597
1614
  try {
1598
1615
  return await executeFn.call(tool, input);
1599
1616
  } catch (error) {
1600
- const currentError = toError(error);
1617
+ const currentError = toError2(error);
1601
1618
  lastError = currentError;
1602
1619
  if (policy.retryableErrors && policy.retryableErrors.length > 0) {
1603
1620
  const isRetryable = policy.retryableErrors.some(
@@ -1635,7 +1652,7 @@ function createToolExecutor(config = {}) {
1635
1652
  return result;
1636
1653
  } catch (error) {
1637
1654
  const duration = Date.now() - startTime;
1638
- const normalizedError = toError(error);
1655
+ const normalizedError = toError2(error);
1639
1656
  metrics.totalExecutions++;
1640
1657
  metrics.failedExecutions++;
1641
1658
  metrics.totalDuration += duration;
@@ -1902,6 +1919,15 @@ function createManagedTool(config) {
1902
1919
  }
1903
1920
 
1904
1921
  // src/tools/composition.ts
1922
+ function isConditionalStep(step) {
1923
+ return !Array.isArray(step) && "condition" in step;
1924
+ }
1925
+ function calculateRetryDelay(attempt, delay, backoff) {
1926
+ return backoff === "exponential" ? delay * Math.pow(2, attempt - 1) : delay * attempt;
1927
+ }
1928
+ function toError(error) {
1929
+ return error instanceof Error ? error : new Error(String(error));
1930
+ }
1905
1931
  function sequential(tools) {
1906
1932
  return {
1907
1933
  name: `sequential(${tools.map((t) => t.name).join(" -> ")})`,
@@ -1945,7 +1971,7 @@ function composeTool(config) {
1945
1971
  for (const step of config.steps) {
1946
1972
  if (Array.isArray(step)) {
1947
1973
  result = await parallel(step).invoke(result);
1948
- } else if ("condition" in step) {
1974
+ } else if (isConditionalStep(step)) {
1949
1975
  result = await conditional(step).invoke(result);
1950
1976
  } else {
1951
1977
  result = await step.invoke(result);
@@ -1960,18 +1986,21 @@ function composeTool(config) {
1960
1986
  }
1961
1987
  function retry(tool, options = {}) {
1962
1988
  const { maxAttempts = 3, delay = 1e3, backoff = "exponential" } = options;
1989
+ if (!Number.isInteger(maxAttempts) || maxAttempts < 1) {
1990
+ throw new Error("Invalid retry options: maxAttempts must be an integer >= 1");
1991
+ }
1963
1992
  return {
1964
1993
  name: `retry(${tool.name})`,
1965
1994
  description: `${tool.description} (with retry)`,
1966
1995
  invoke: async (input) => {
1967
- let lastError;
1996
+ let lastError = new Error(`Tool ${tool.name} failed without an explicit error`);
1968
1997
  for (let attempt = 1; attempt <= maxAttempts; attempt++) {
1969
1998
  try {
1970
1999
  return await tool.invoke(input);
1971
2000
  } catch (error) {
1972
- lastError = error;
2001
+ lastError = toError(error);
1973
2002
  if (attempt < maxAttempts) {
1974
- const waitTime = backoff === "exponential" ? delay * Math.pow(2, attempt - 1) : delay * attempt;
2003
+ const waitTime = calculateRetryDelay(attempt, delay, backoff);
1975
2004
  await new Promise((resolve) => setTimeout(resolve, waitTime));
1976
2005
  }
1977
2006
  }
@@ -1985,12 +2014,19 @@ function timeout(tool, ms) {
1985
2014
  name: `timeout(${tool.name})`,
1986
2015
  description: `${tool.description} (with ${ms}ms timeout)`,
1987
2016
  invoke: async (input) => {
1988
- return Promise.race([
1989
- tool.invoke(input),
1990
- new Promise(
1991
- (_, reject) => setTimeout(() => reject(new Error(`Tool ${tool.name} timed out after ${ms}ms`)), ms)
1992
- )
1993
- ]);
2017
+ let timer;
2018
+ const timeoutPromise = new Promise((_, reject) => {
2019
+ timer = setTimeout(() => {
2020
+ reject(new Error(`Tool ${tool.name} timed out after ${ms}ms`));
2021
+ }, ms);
2022
+ });
2023
+ try {
2024
+ return await Promise.race([tool.invoke(input), timeoutPromise]);
2025
+ } finally {
2026
+ if (timer !== void 0) {
2027
+ clearTimeout(timer);
2028
+ }
2029
+ }
1994
2030
  }
1995
2031
  };
1996
2032
  }
package/dist/index.d.cts CHANGED
@@ -1670,54 +1670,57 @@ declare function createManagedTool<TContext = any, TInput = any, TOutput = any>(
1670
1670
  * Tool Composition - Compose tools into higher-level operations
1671
1671
  * @module tools/composition
1672
1672
  */
1673
- interface ComposedTool {
1673
+ type RetryBackoffStrategy = 'linear' | 'exponential';
1674
+ interface RetryOptions$1 {
1675
+ maxAttempts?: number;
1676
+ delay?: number;
1677
+ backoff?: RetryBackoffStrategy;
1678
+ }
1679
+ interface ComposedTool<TInput = unknown, TOutput = unknown> {
1674
1680
  name: string;
1675
1681
  description: string;
1676
- invoke: (input: any) => Promise<any>;
1682
+ invoke(input: TInput): Promise<TOutput>;
1677
1683
  }
1678
- interface ConditionalConfig {
1679
- condition: (input: any) => boolean | Promise<boolean>;
1680
- onTrue: ComposedTool;
1681
- onFalse: ComposedTool;
1684
+ interface ConditionalConfig<TInput = unknown, TTrueOutput = unknown, TFalseOutput = unknown> {
1685
+ condition(input: TInput): boolean | Promise<boolean>;
1686
+ onTrue: ComposedTool<TInput, TTrueOutput>;
1687
+ onFalse: ComposedTool<TInput, TFalseOutput>;
1682
1688
  }
1683
- interface ComposeToolConfig {
1689
+ type ComposedStep = ComposedTool<unknown, unknown> | ComposedTool<unknown, unknown>[] | ConditionalConfig<unknown, unknown, unknown>;
1690
+ interface ComposeToolConfig<TOutput = unknown> {
1684
1691
  name: string;
1685
1692
  description?: string;
1686
- steps: Array<ComposedTool | ComposedTool[] | ConditionalConfig>;
1687
- transformResult?: (result: any) => any;
1693
+ steps: ComposedStep[];
1694
+ transformResult?: (result: unknown) => TOutput;
1688
1695
  }
1689
1696
  /**
1690
1697
  * Execute tools sequentially
1691
1698
  */
1692
- declare function sequential(tools: ComposedTool[]): ComposedTool;
1699
+ declare function sequential<TInput = unknown>(tools: ComposedTool<unknown, unknown>[]): ComposedTool<TInput, unknown>;
1693
1700
  /**
1694
1701
  * Execute tools in parallel
1695
1702
  */
1696
- declare function parallel(tools: ComposedTool[]): ComposedTool;
1703
+ declare function parallel<TInput = unknown>(tools: ComposedTool<TInput, unknown>[]): ComposedTool<TInput, unknown[]>;
1697
1704
  /**
1698
1705
  * Execute tool conditionally
1699
1706
  */
1700
- declare function conditional(config: ConditionalConfig): ComposedTool;
1707
+ declare function conditional<TInput = unknown, TTrueOutput = unknown, TFalseOutput = unknown>(config: ConditionalConfig<TInput, TTrueOutput, TFalseOutput>): ComposedTool<TInput, TTrueOutput | TFalseOutput>;
1701
1708
  /**
1702
1709
  * Compose tools into a complex workflow
1703
1710
  */
1704
- declare function composeTool(config: ComposeToolConfig): ComposedTool;
1711
+ declare function composeTool<TInput = unknown, TOutput = unknown>(config: ComposeToolConfig<TOutput>): ComposedTool<TInput, TOutput>;
1705
1712
  /**
1706
1713
  * Create a tool that retries on failure
1707
1714
  */
1708
- declare function retry(tool: ComposedTool, options?: {
1709
- maxAttempts?: number;
1710
- delay?: number;
1711
- backoff?: 'linear' | 'exponential';
1712
- }): ComposedTool;
1715
+ declare function retry<TInput = unknown, TOutput = unknown>(tool: ComposedTool<TInput, TOutput>, options?: RetryOptions$1): ComposedTool<TInput, TOutput>;
1713
1716
  /**
1714
1717
  * Create a tool that times out
1715
1718
  */
1716
- declare function timeout(tool: ComposedTool, ms: number): ComposedTool;
1719
+ declare function timeout<TInput = unknown, TOutput = unknown>(tool: ComposedTool<TInput, TOutput>, ms: number): ComposedTool<TInput, TOutput>;
1717
1720
  /**
1718
1721
  * Create a tool that caches results
1719
1722
  */
1720
- declare function cache(tool: ComposedTool, ttl?: number): ComposedTool;
1723
+ declare function cache<TInput = unknown, TOutput = unknown>(tool: ComposedTool<TInput, TOutput>, ttl?: number): ComposedTool<TInput, TOutput>;
1721
1724
 
1722
1725
  /**
1723
1726
  * Tool Mocking & Testing - Mock tools for testing
@@ -1791,6 +1794,13 @@ declare function createToolSimulator(config: ToolSimulatorConfig): {
1791
1794
  * ```
1792
1795
  */
1793
1796
 
1797
+ type RuntimeSchema<TInput = unknown> = z.ZodSchema<TInput>;
1798
+ type JsonSchemaObject = Record<string, unknown>;
1799
+ interface LangChainConvertibleTool<TInput = unknown, TOutput = unknown> {
1800
+ metadata: ToolMetadata;
1801
+ schema: RuntimeSchema<TInput>;
1802
+ invoke(input: TInput): Promise<TOutput>;
1803
+ }
1794
1804
  /**
1795
1805
  * Convert an AgentForge tool to a LangChain DynamicStructuredTool
1796
1806
  *
@@ -1823,7 +1833,7 @@ declare function createToolSimulator(config: ToolSimulatorConfig): {
1823
1833
  * });
1824
1834
  * ```
1825
1835
  */
1826
- declare function toLangChainTool(tool: Tool<any, any>): DynamicStructuredTool<any>;
1836
+ declare function toLangChainTool<TInput, TOutput>(tool: Tool<TInput, TOutput>): DynamicStructuredTool<RuntimeSchema<TInput>, TInput, TInput, string>;
1827
1837
  /**
1828
1838
  * Convert multiple AgentForge tools to LangChain tools
1829
1839
  *
@@ -1841,7 +1851,7 @@ declare function toLangChainTool(tool: Tool<any, any>): DynamicStructuredTool<an
1841
1851
  * });
1842
1852
  * ```
1843
1853
  */
1844
- declare function toLangChainTools(tools: Tool<any, any>[]): DynamicStructuredTool<any>[];
1854
+ declare function toLangChainTools(tools: readonly LangChainConvertibleTool[]): DynamicStructuredTool[];
1845
1855
  /**
1846
1856
  * Get the JSON Schema representation of a tool's input schema
1847
1857
  *
@@ -1856,7 +1866,7 @@ declare function toLangChainTools(tools: Tool<any, any>[]): DynamicStructuredToo
1856
1866
  * console.log(JSON.stringify(schema, null, 2));
1857
1867
  * ```
1858
1868
  */
1859
- declare function getToolJsonSchema(tool: Tool<any, any>): Record<string, any>;
1869
+ declare function getToolJsonSchema<TInput, TOutput>(tool: Tool<TInput, TOutput>): JsonSchemaObject;
1860
1870
  /**
1861
1871
  * Get tool metadata in a format suitable for LLM prompts
1862
1872
  *
@@ -1877,7 +1887,7 @@ declare function getToolJsonSchema(tool: Tool<any, any>): Record<string, any>;
1877
1887
  * // ...
1878
1888
  * ```
1879
1889
  */
1880
- declare function getToolDescription(tool: Tool<any, any>): string;
1890
+ declare function getToolDescription<TInput, TOutput>(tool: Tool<TInput, TOutput>): string;
1881
1891
 
1882
1892
  /**
1883
1893
  * LangGraph State Utilities
package/dist/index.d.ts CHANGED
@@ -1670,54 +1670,57 @@ declare function createManagedTool<TContext = any, TInput = any, TOutput = any>(
1670
1670
  * Tool Composition - Compose tools into higher-level operations
1671
1671
  * @module tools/composition
1672
1672
  */
1673
- interface ComposedTool {
1673
+ type RetryBackoffStrategy = 'linear' | 'exponential';
1674
+ interface RetryOptions$1 {
1675
+ maxAttempts?: number;
1676
+ delay?: number;
1677
+ backoff?: RetryBackoffStrategy;
1678
+ }
1679
+ interface ComposedTool<TInput = unknown, TOutput = unknown> {
1674
1680
  name: string;
1675
1681
  description: string;
1676
- invoke: (input: any) => Promise<any>;
1682
+ invoke(input: TInput): Promise<TOutput>;
1677
1683
  }
1678
- interface ConditionalConfig {
1679
- condition: (input: any) => boolean | Promise<boolean>;
1680
- onTrue: ComposedTool;
1681
- onFalse: ComposedTool;
1684
+ interface ConditionalConfig<TInput = unknown, TTrueOutput = unknown, TFalseOutput = unknown> {
1685
+ condition(input: TInput): boolean | Promise<boolean>;
1686
+ onTrue: ComposedTool<TInput, TTrueOutput>;
1687
+ onFalse: ComposedTool<TInput, TFalseOutput>;
1682
1688
  }
1683
- interface ComposeToolConfig {
1689
+ type ComposedStep = ComposedTool<unknown, unknown> | ComposedTool<unknown, unknown>[] | ConditionalConfig<unknown, unknown, unknown>;
1690
+ interface ComposeToolConfig<TOutput = unknown> {
1684
1691
  name: string;
1685
1692
  description?: string;
1686
- steps: Array<ComposedTool | ComposedTool[] | ConditionalConfig>;
1687
- transformResult?: (result: any) => any;
1693
+ steps: ComposedStep[];
1694
+ transformResult?: (result: unknown) => TOutput;
1688
1695
  }
1689
1696
  /**
1690
1697
  * Execute tools sequentially
1691
1698
  */
1692
- declare function sequential(tools: ComposedTool[]): ComposedTool;
1699
+ declare function sequential<TInput = unknown>(tools: ComposedTool<unknown, unknown>[]): ComposedTool<TInput, unknown>;
1693
1700
  /**
1694
1701
  * Execute tools in parallel
1695
1702
  */
1696
- declare function parallel(tools: ComposedTool[]): ComposedTool;
1703
+ declare function parallel<TInput = unknown>(tools: ComposedTool<TInput, unknown>[]): ComposedTool<TInput, unknown[]>;
1697
1704
  /**
1698
1705
  * Execute tool conditionally
1699
1706
  */
1700
- declare function conditional(config: ConditionalConfig): ComposedTool;
1707
+ declare function conditional<TInput = unknown, TTrueOutput = unknown, TFalseOutput = unknown>(config: ConditionalConfig<TInput, TTrueOutput, TFalseOutput>): ComposedTool<TInput, TTrueOutput | TFalseOutput>;
1701
1708
  /**
1702
1709
  * Compose tools into a complex workflow
1703
1710
  */
1704
- declare function composeTool(config: ComposeToolConfig): ComposedTool;
1711
+ declare function composeTool<TInput = unknown, TOutput = unknown>(config: ComposeToolConfig<TOutput>): ComposedTool<TInput, TOutput>;
1705
1712
  /**
1706
1713
  * Create a tool that retries on failure
1707
1714
  */
1708
- declare function retry(tool: ComposedTool, options?: {
1709
- maxAttempts?: number;
1710
- delay?: number;
1711
- backoff?: 'linear' | 'exponential';
1712
- }): ComposedTool;
1715
+ declare function retry<TInput = unknown, TOutput = unknown>(tool: ComposedTool<TInput, TOutput>, options?: RetryOptions$1): ComposedTool<TInput, TOutput>;
1713
1716
  /**
1714
1717
  * Create a tool that times out
1715
1718
  */
1716
- declare function timeout(tool: ComposedTool, ms: number): ComposedTool;
1719
+ declare function timeout<TInput = unknown, TOutput = unknown>(tool: ComposedTool<TInput, TOutput>, ms: number): ComposedTool<TInput, TOutput>;
1717
1720
  /**
1718
1721
  * Create a tool that caches results
1719
1722
  */
1720
- declare function cache(tool: ComposedTool, ttl?: number): ComposedTool;
1723
+ declare function cache<TInput = unknown, TOutput = unknown>(tool: ComposedTool<TInput, TOutput>, ttl?: number): ComposedTool<TInput, TOutput>;
1721
1724
 
1722
1725
  /**
1723
1726
  * Tool Mocking & Testing - Mock tools for testing
@@ -1791,6 +1794,13 @@ declare function createToolSimulator(config: ToolSimulatorConfig): {
1791
1794
  * ```
1792
1795
  */
1793
1796
 
1797
+ type RuntimeSchema<TInput = unknown> = z.ZodSchema<TInput>;
1798
+ type JsonSchemaObject = Record<string, unknown>;
1799
+ interface LangChainConvertibleTool<TInput = unknown, TOutput = unknown> {
1800
+ metadata: ToolMetadata;
1801
+ schema: RuntimeSchema<TInput>;
1802
+ invoke(input: TInput): Promise<TOutput>;
1803
+ }
1794
1804
  /**
1795
1805
  * Convert an AgentForge tool to a LangChain DynamicStructuredTool
1796
1806
  *
@@ -1823,7 +1833,7 @@ declare function createToolSimulator(config: ToolSimulatorConfig): {
1823
1833
  * });
1824
1834
  * ```
1825
1835
  */
1826
- declare function toLangChainTool(tool: Tool<any, any>): DynamicStructuredTool<any>;
1836
+ declare function toLangChainTool<TInput, TOutput>(tool: Tool<TInput, TOutput>): DynamicStructuredTool<RuntimeSchema<TInput>, TInput, TInput, string>;
1827
1837
  /**
1828
1838
  * Convert multiple AgentForge tools to LangChain tools
1829
1839
  *
@@ -1841,7 +1851,7 @@ declare function toLangChainTool(tool: Tool<any, any>): DynamicStructuredTool<an
1841
1851
  * });
1842
1852
  * ```
1843
1853
  */
1844
- declare function toLangChainTools(tools: Tool<any, any>[]): DynamicStructuredTool<any>[];
1854
+ declare function toLangChainTools(tools: readonly LangChainConvertibleTool[]): DynamicStructuredTool[];
1845
1855
  /**
1846
1856
  * Get the JSON Schema representation of a tool's input schema
1847
1857
  *
@@ -1856,7 +1866,7 @@ declare function toLangChainTools(tools: Tool<any, any>[]): DynamicStructuredToo
1856
1866
  * console.log(JSON.stringify(schema, null, 2));
1857
1867
  * ```
1858
1868
  */
1859
- declare function getToolJsonSchema(tool: Tool<any, any>): Record<string, any>;
1869
+ declare function getToolJsonSchema<TInput, TOutput>(tool: Tool<TInput, TOutput>): JsonSchemaObject;
1860
1870
  /**
1861
1871
  * Get tool metadata in a format suitable for LLM prompts
1862
1872
  *
@@ -1877,7 +1887,7 @@ declare function getToolJsonSchema(tool: Tool<any, any>): Record<string, any>;
1877
1887
  * // ...
1878
1888
  * ```
1879
1889
  */
1880
- declare function getToolDescription(tool: Tool<any, any>): string;
1890
+ declare function getToolDescription<TInput, TOutput>(tool: Tool<TInput, TOutput>): string;
1881
1891
 
1882
1892
  /**
1883
1893
  * LangGraph State Utilities
package/dist/index.js CHANGED
@@ -619,21 +619,42 @@ function toolBuilder() {
619
619
  // src/langchain/converter.ts
620
620
  import { DynamicStructuredTool } from "@langchain/core/tools";
621
621
  import { zodToJsonSchema } from "zod-to-json-schema";
622
+ function serializeToolResult(result) {
623
+ if (typeof result === "string") {
624
+ return result;
625
+ }
626
+ if (typeof result === "object" && result !== null) {
627
+ return JSON.stringify(result, null, 2);
628
+ }
629
+ return String(result);
630
+ }
631
+ function isJsonSchemaObject(value) {
632
+ return typeof value === "object" && value !== null && !Array.isArray(value);
633
+ }
634
+ function isJsonSchemaDefinitionMap(value) {
635
+ if (!isJsonSchemaObject(value)) {
636
+ return false;
637
+ }
638
+ return Object.values(value).every(isJsonSchemaObject);
639
+ }
640
+ function extractToolSchema(jsonSchema) {
641
+ if (!isJsonSchemaObject(jsonSchema)) {
642
+ return {};
643
+ }
644
+ const ref = jsonSchema.$ref;
645
+ const definitions = jsonSchema.definitions;
646
+ if (typeof ref !== "string" || !isJsonSchemaDefinitionMap(definitions)) {
647
+ return jsonSchema;
648
+ }
649
+ const refName = ref.replace("#/definitions/", "");
650
+ return definitions[refName] ?? jsonSchema;
651
+ }
622
652
  function toLangChainTool(tool) {
623
653
  return new DynamicStructuredTool({
624
654
  name: tool.metadata.name,
625
655
  description: tool.metadata.description,
626
656
  schema: tool.schema,
627
- func: async (input) => {
628
- const result = await tool.invoke(input);
629
- if (typeof result === "string") {
630
- return result;
631
- }
632
- if (typeof result === "object" && result !== null) {
633
- return JSON.stringify(result, null, 2);
634
- }
635
- return String(result);
636
- }
657
+ func: async (input) => serializeToolResult(await tool.invoke(input))
637
658
  });
638
659
  }
639
660
  function toLangChainTools(tools) {
@@ -645,11 +666,7 @@ function getToolJsonSchema(tool) {
645
666
  $refStrategy: "none"
646
667
  // Don't use $ref for nested schemas
647
668
  });
648
- if (jsonSchema.$ref && jsonSchema.definitions) {
649
- const refName = jsonSchema.$ref.replace("#/definitions/", "");
650
- return jsonSchema.definitions[refName] || jsonSchema;
651
- }
652
- return jsonSchema;
669
+ return extractToolSchema(jsonSchema);
653
670
  }
654
671
  function getToolDescription(tool) {
655
672
  const { metadata } = tool;
@@ -1391,7 +1408,7 @@ function createToolExecutor(config = {}) {
1391
1408
  }
1392
1409
  return Math.min(delay, maxDelay);
1393
1410
  }
1394
- function toError(error) {
1411
+ function toError2(error) {
1395
1412
  if (error instanceof Error) {
1396
1413
  return error;
1397
1414
  }
@@ -1422,7 +1439,7 @@ function createToolExecutor(config = {}) {
1422
1439
  try {
1423
1440
  return await executeFn.call(tool, input);
1424
1441
  } catch (error) {
1425
- const currentError = toError(error);
1442
+ const currentError = toError2(error);
1426
1443
  lastError = currentError;
1427
1444
  if (policy.retryableErrors && policy.retryableErrors.length > 0) {
1428
1445
  const isRetryable = policy.retryableErrors.some(
@@ -1460,7 +1477,7 @@ function createToolExecutor(config = {}) {
1460
1477
  return result;
1461
1478
  } catch (error) {
1462
1479
  const duration = Date.now() - startTime;
1463
- const normalizedError = toError(error);
1480
+ const normalizedError = toError2(error);
1464
1481
  metrics.totalExecutions++;
1465
1482
  metrics.failedExecutions++;
1466
1483
  metrics.totalDuration += duration;
@@ -1727,6 +1744,15 @@ function createManagedTool(config) {
1727
1744
  }
1728
1745
 
1729
1746
  // src/tools/composition.ts
1747
+ function isConditionalStep(step) {
1748
+ return !Array.isArray(step) && "condition" in step;
1749
+ }
1750
+ function calculateRetryDelay(attempt, delay, backoff) {
1751
+ return backoff === "exponential" ? delay * Math.pow(2, attempt - 1) : delay * attempt;
1752
+ }
1753
+ function toError(error) {
1754
+ return error instanceof Error ? error : new Error(String(error));
1755
+ }
1730
1756
  function sequential(tools) {
1731
1757
  return {
1732
1758
  name: `sequential(${tools.map((t) => t.name).join(" -> ")})`,
@@ -1770,7 +1796,7 @@ function composeTool(config) {
1770
1796
  for (const step of config.steps) {
1771
1797
  if (Array.isArray(step)) {
1772
1798
  result = await parallel(step).invoke(result);
1773
- } else if ("condition" in step) {
1799
+ } else if (isConditionalStep(step)) {
1774
1800
  result = await conditional(step).invoke(result);
1775
1801
  } else {
1776
1802
  result = await step.invoke(result);
@@ -1785,18 +1811,21 @@ function composeTool(config) {
1785
1811
  }
1786
1812
  function retry(tool, options = {}) {
1787
1813
  const { maxAttempts = 3, delay = 1e3, backoff = "exponential" } = options;
1814
+ if (!Number.isInteger(maxAttempts) || maxAttempts < 1) {
1815
+ throw new Error("Invalid retry options: maxAttempts must be an integer >= 1");
1816
+ }
1788
1817
  return {
1789
1818
  name: `retry(${tool.name})`,
1790
1819
  description: `${tool.description} (with retry)`,
1791
1820
  invoke: async (input) => {
1792
- let lastError;
1821
+ let lastError = new Error(`Tool ${tool.name} failed without an explicit error`);
1793
1822
  for (let attempt = 1; attempt <= maxAttempts; attempt++) {
1794
1823
  try {
1795
1824
  return await tool.invoke(input);
1796
1825
  } catch (error) {
1797
- lastError = error;
1826
+ lastError = toError(error);
1798
1827
  if (attempt < maxAttempts) {
1799
- const waitTime = backoff === "exponential" ? delay * Math.pow(2, attempt - 1) : delay * attempt;
1828
+ const waitTime = calculateRetryDelay(attempt, delay, backoff);
1800
1829
  await new Promise((resolve) => setTimeout(resolve, waitTime));
1801
1830
  }
1802
1831
  }
@@ -1810,12 +1839,19 @@ function timeout(tool, ms) {
1810
1839
  name: `timeout(${tool.name})`,
1811
1840
  description: `${tool.description} (with ${ms}ms timeout)`,
1812
1841
  invoke: async (input) => {
1813
- return Promise.race([
1814
- tool.invoke(input),
1815
- new Promise(
1816
- (_, reject) => setTimeout(() => reject(new Error(`Tool ${tool.name} timed out after ${ms}ms`)), ms)
1817
- )
1818
- ]);
1842
+ let timer;
1843
+ const timeoutPromise = new Promise((_, reject) => {
1844
+ timer = setTimeout(() => {
1845
+ reject(new Error(`Tool ${tool.name} timed out after ${ms}ms`));
1846
+ }, ms);
1847
+ });
1848
+ try {
1849
+ return await Promise.race([tool.invoke(input), timeoutPromise]);
1850
+ } finally {
1851
+ if (timer !== void 0) {
1852
+ clearTimeout(timer);
1853
+ }
1854
+ }
1819
1855
  }
1820
1856
  };
1821
1857
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agentforge/core",
3
- "version": "0.15.5",
3
+ "version": "0.15.7",
4
4
  "description": "Production-ready TypeScript agent framework built on LangGraph with orchestration, middleware, and typed abstractions.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",