@easynet/agent-tool-hub 1.0.14 → 1.0.16

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/README.md CHANGED
@@ -37,7 +37,7 @@ npx agent-toolhub-react-stock GOOGL
37
37
 
38
38
  Pass the ticker symbol (e.g. `GOOGL`, `AAPL`, `MSFT`). **Configure your own LLM** — point the example to your OpenAI-compatible API (base URL and API key) in [examples/agent-toolhub-react-stock.mjs](examples/agent-toolhub-react-stock.mjs) or via env (e.g. `OPENAI_API_KEY`, `OPENAI_BASE_URL`). The bundled example uses placeholder values; replace them with your model endpoint.
39
39
 
40
- Output: console step-by-step progress + an HTML report (e.g. `GOOGL-research-report.html`) in the current directory. See a sample report: [docs/AAPL-research-report.html](docs/AAPL-research-report.html).
40
+ Output: console step-by-step progress + an HTML report (e.g. `GOOGL-research-report.html`) in the current directory. See a sample report: [easynet-world.github.io/agent-tool-hub/AAPL-research-report.html](https://easynet-world.github.io/agent-tool-hub/AAPL-research-report.html).
41
41
 
42
42
  **Agent Run Report** — The generated HTML report is a highlight: it shows system/user prompts, the rendered Markdown report, and a Debug tab with step-by-step execution and token usage.
43
43
 
@@ -31,11 +31,14 @@ function extractToolArgs(args) {
31
31
  }
32
32
  return {};
33
33
  }
34
+ function toolNameForLLM(name) {
35
+ return name.replace(/[/.-]/g, "_");
36
+ }
34
37
  function toolHubToLangChainTools(hub) {
35
38
  const specs = hub.getRegistry().snapshot();
36
39
  return specs.map((spec) => {
37
40
  const opts = {
38
- name: spec.name,
41
+ name: toolNameForLLM(spec.name),
39
42
  description: spec.description ?? `Tool: ${spec.name}`,
40
43
  schema: TOOL_ARGS_SCHEMA
41
44
  };
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/langchain-tools.ts"],"names":["z","tool","createAgentToolHub"],"mappings":";;;;;;;;AA4BA,IAAM,gBAAA,GAAmBA,KAAA,CACtB,MAAA,CAAOA,KAAA,CAAE,MAAA,EAAO,EAAGA,KAAA,CAAE,OAAA,EAAS,CAAA,CAC9B,QAAA,CAAS,oCAAoC,CAAA;AAMhD,SAAS,gBAAgB,IAAA,EAAwC;AAC/D,EAAA,IAAI,IAAA,IAAQ,QAAQ,OAAO,IAAA,KAAS,YAAY,CAAC,KAAA,CAAM,OAAA,CAAQ,IAAI,CAAA,EAAG;AACpE,IAAA,OAAO,IAAA;AAAA,EACT;AACA,EAAA,MAAM,MAAM,OAAO,IAAA,KAAS,WAAW,IAAA,GAAO,MAAA,CAAO,QAAQ,EAAE,CAAA;AAC/D,EAAA,IAAI,CAAC,GAAA,CAAI,IAAA,EAAK,SAAU,EAAC;AAEzB,EAAA,IAAI;AACF,IAAA,MAAM,MAAA,GAAS,IAAA,CAAK,KAAA,CAAM,GAAG,CAAA;AAC7B,IAAA,IAAI,MAAA,IAAU,QAAQ,OAAO,MAAA,KAAW,YAAY,CAAC,KAAA,CAAM,OAAA,CAAQ,MAAM,CAAA,EAAG;AAC1E,MAAA,OAAO,MAAA;AAAA,IACT;AAAA,EACF,CAAA,CAAA,MAAQ;AAAA,EAER;AAEA,EAAA,MAAM,SAAA,GAAY,GAAA,CAAI,WAAA,CAAY,GAAG,CAAA;AACrC,EAAA,IAAI,SAAA,KAAc,EAAA,EAAI,OAAO,EAAC;AAC9B,EAAA,IAAI;AACF,IAAA,MAAM,SAAS,IAAA,CAAK,KAAA,CAAM,GAAA,CAAI,KAAA,CAAM,SAAS,CAAC,CAAA;AAC9C,IAAA,IAAI,MAAA,IAAU,QAAQ,OAAO,MAAA,KAAW,YAAY,CAAC,KAAA,CAAM,OAAA,CAAQ,MAAM,CAAA,EAAG;AAC1E,MAAA,OAAO,MAAA;AAAA,IACT;AAAA,EACF,CAAA,CAAA,MAAQ;AAAA,EAER;AACA,EAAA,OAAO,EAAC;AACV;AAWO,SAAS,wBAAwB,GAAA,EAA6B;AACnE,EAAA,MAAM,KAAA,GAAQ,GAAA,CAAI,WAAA,EAAY,CAAE,QAAA,EAAS;AACzC,EAAA,OAAO,KAAA,CAAM,GAAA,CAAI,CAAC,IAAA,KAAS;AACzB,IAAA,MAAM,IAAA,GAAO;AAAA,MACX,MAAM,IAAA,CAAK,IAAA;AAAA,MACX,WAAA,EAAa,IAAA,CAAK,WAAA,IAAe,CAAA,MAAA,EAAS,KAAK,IAAI,CAAA,CAAA;AAAA,MACnD,MAAA,EAAQ;AAAA,KACV;AAEA,IAAA,MAAM,CAAA,GAAIC,cAAA;AAAA,MACR,OAAO,IAAA,KAAkB;AACvB,QAAA,MAAM,UAAA,GAAa,gBAAgB,IAAI,CAAA;AACvC,QAAA,MAAM,SAAS,MAAM,GAAA,CAAI,UAAA,CAAW,IAAA,CAAK,MAAM,UAAU,CAAA;AACzD,QAAA,OAAO,MAAA,CAAO,KACV,MAAA,CAAO,MAAA,GACP,EAAE,KAAA,EAAO,MAAA,CAAO,KAAA,EAAO,OAAA,IAAW,aAAA,EAAc;AAAA,MACtD,CAAA;AAAA,MACA;AAAA,KACF;AACA,IAAA,OAAO,CAAA;AAAA,EACT,CAAC,CAAA;AACH;AASA,eAAsBC,oBAAmB,UAAA,EAAoB;AAC3D,EAAA,MAAM,GAAA,GAAM,MAAMA,oCAAA,CAA8B,UAAU,CAAA;AAC1D,EAAA,OAAO,MAAA,CAAO,OAAO,GAAA,EAAK;AAAA,IACxB,IAAI,KAAA,GAAmB;AACrB,MAAA,OAAO,wBAAwB,GAAG,CAAA;AAAA,IACpC;AAAA,GACD,CAAA;AACH","file":"langchain-tools.cjs","sourcesContent":["/**\n * Bridge: ToolHub registry → LangChain tools (for use with LangChain agents, e.g. DeepAgents).\n *\n * Requires peer dependencies: langchain, zod. Use this entry when integrating ToolHub\n * with LangChain 1.x agents. Main package entry does not depend on LangChain.\n *\n * @example\n * ```ts\n * import { createAgentToolHub, toolHubToLangChainTools } from \"@easynet/agent-tool-hub/langchain-tools\";\n * const hub = await createAgentToolHub(\"toolhub.yaml\");\n * // hub.tools is the LangChain tools array\n * const agent = createDeepAgent({ tools: hub.tools, ... });\n * // Or: const tools = toolHubToLangChainTools(hub);\n * ```\n */\n\nimport { createAgentToolHub as createAgentToolHubFromRuntime } from \"./toolhub-runtime.js\";\nimport { tool } from \"langchain\";\nimport { z } from \"zod\";\nimport type { ToolSpec } from \"./types/ToolSpec.js\";\nimport type { ToolResult } from \"./types/ToolResult.js\";\n\n/** Minimal interface for a hub that can list tools and invoke them (e.g. ToolHub, AgentToolHub). */\nexport interface ToolHubLike {\n getRegistry(): { snapshot(): ToolSpec[] };\n invokeTool(name: string, args: unknown): Promise<ToolResult>;\n}\n\nconst TOOL_ARGS_SCHEMA = z\n .record(z.string(), z.unknown())\n .describe(\"Tool arguments as key-value object\");\n\n/**\n * Extracts a JSON object from tool args. Handles models that send text + JSON\n * (e.g. \"Let's fetch.{\\\"symbol\\\":\\\"GOOGL\\\"}\") by finding the last {...} and parsing.\n */\nfunction extractToolArgs(args: unknown): Record<string, unknown> {\n if (args != null && typeof args === \"object\" && !Array.isArray(args)) {\n return args as Record<string, unknown>;\n }\n const str = typeof args === \"string\" ? args : String(args ?? \"\");\n if (!str.trim()) return {};\n // Try parse whole string first\n try {\n const parsed = JSON.parse(str) as unknown;\n if (parsed != null && typeof parsed === \"object\" && !Array.isArray(parsed)) {\n return parsed as Record<string, unknown>;\n }\n } catch {\n // ignore\n }\n // Find last { ... } and parse\n const lastBrace = str.lastIndexOf(\"{\");\n if (lastBrace === -1) return {};\n try {\n const parsed = JSON.parse(str.slice(lastBrace)) as unknown;\n if (parsed != null && typeof parsed === \"object\" && !Array.isArray(parsed)) {\n return parsed as Record<string, unknown>;\n }\n } catch {\n // ignore\n }\n return {};\n}\n\n/**\n * Converts a ToolHub (or AgentToolHub) registry into an array of LangChain tools.\n * Each tool delegates to hub.invokeTool(spec.name, args). Use with LangChain 1.x agents\n * (e.g. createDeepAgent from deepagents). Tool args are normalized: if the model\n * sends text + JSON (e.g. \"reasoning{\\\"symbol\\\":\\\"AAPL\\\"}\"), the JSON is extracted.\n *\n * @param hub - Instance with getRegistry() and invokeTool(name, args)\n * @returns Array of LangChain tool instances (compatible with agent tools array)\n */\nexport function toolHubToLangChainTools(hub: ToolHubLike): unknown[] {\n const specs = hub.getRegistry().snapshot();\n return specs.map((spec) => {\n const opts = {\n name: spec.name,\n description: spec.description ?? `Tool: ${spec.name}`,\n schema: TOOL_ARGS_SCHEMA,\n };\n // Cast opts to avoid TS2589 (excessively deep type instantiation) from langchain tool()\n const t = tool(\n async (args: unknown) => {\n const normalized = extractToolArgs(args);\n const result = await hub.invokeTool(spec.name, normalized);\n return result.ok\n ? result.result\n : { error: result.error?.message ?? \"Tool failed\" };\n },\n opts as Parameters<typeof tool>[1],\n );\n return t as unknown;\n });\n}\n\n/**\n * Create an AgentToolHub from a config path and return it with a `.tools` getter\n * that returns LangChain tools. Use with LangChain/DeepAgents agents.\n *\n * @param configPath - Path to toolhub.yaml (or other config file)\n * @returns AgentToolHub instance with `.tools` (array of LangChain tools)\n */\nexport async function createAgentToolHub(configPath: string) {\n const hub = await createAgentToolHubFromRuntime(configPath);\n return Object.assign(hub, {\n get tools(): unknown[] {\n return toolHubToLangChainTools(hub);\n },\n });\n}\n"]}
1
+ {"version":3,"sources":["../src/langchain-tools.ts"],"names":["z","tool","createAgentToolHub"],"mappings":";;;;;;;;AA4BA,IAAM,gBAAA,GAAmBA,KAAA,CACtB,MAAA,CAAOA,KAAA,CAAE,MAAA,EAAO,EAAGA,KAAA,CAAE,OAAA,EAAS,CAAA,CAC9B,QAAA,CAAS,oCAAoC,CAAA;AAMhD,SAAS,gBAAgB,IAAA,EAAwC;AAC/D,EAAA,IAAI,IAAA,IAAQ,QAAQ,OAAO,IAAA,KAAS,YAAY,CAAC,KAAA,CAAM,OAAA,CAAQ,IAAI,CAAA,EAAG;AACpE,IAAA,OAAO,IAAA;AAAA,EACT;AACA,EAAA,MAAM,MAAM,OAAO,IAAA,KAAS,WAAW,IAAA,GAAO,MAAA,CAAO,QAAQ,EAAE,CAAA;AAC/D,EAAA,IAAI,CAAC,GAAA,CAAI,IAAA,EAAK,SAAU,EAAC;AAEzB,EAAA,IAAI;AACF,IAAA,MAAM,MAAA,GAAS,IAAA,CAAK,KAAA,CAAM,GAAG,CAAA;AAC7B,IAAA,IAAI,MAAA,IAAU,QAAQ,OAAO,MAAA,KAAW,YAAY,CAAC,KAAA,CAAM,OAAA,CAAQ,MAAM,CAAA,EAAG;AAC1E,MAAA,OAAO,MAAA;AAAA,IACT;AAAA,EACF,CAAA,CAAA,MAAQ;AAAA,EAER;AAEA,EAAA,MAAM,SAAA,GAAY,GAAA,CAAI,WAAA,CAAY,GAAG,CAAA;AACrC,EAAA,IAAI,SAAA,KAAc,EAAA,EAAI,OAAO,EAAC;AAC9B,EAAA,IAAI;AACF,IAAA,MAAM,SAAS,IAAA,CAAK,KAAA,CAAM,GAAA,CAAI,KAAA,CAAM,SAAS,CAAC,CAAA;AAC9C,IAAA,IAAI,MAAA,IAAU,QAAQ,OAAO,MAAA,KAAW,YAAY,CAAC,KAAA,CAAM,OAAA,CAAQ,MAAM,CAAA,EAAG;AAC1E,MAAA,OAAO,MAAA;AAAA,IACT;AAAA,EACF,CAAA,CAAA,MAAQ;AAAA,EAER;AACA,EAAA,OAAO,EAAC;AACV;AAMA,SAAS,eAAe,IAAA,EAAsB;AAC5C,EAAA,OAAO,IAAA,CAAK,OAAA,CAAQ,QAAA,EAAU,GAAG,CAAA;AACnC;AAaO,SAAS,wBAAwB,GAAA,EAA6B;AACnE,EAAA,MAAM,KAAA,GAAQ,GAAA,CAAI,WAAA,EAAY,CAAE,QAAA,EAAS;AACzC,EAAA,OAAO,KAAA,CAAM,GAAA,CAAI,CAAC,IAAA,KAAS;AACzB,IAAA,MAAM,IAAA,GAAO;AAAA,MACX,IAAA,EAAM,cAAA,CAAe,IAAA,CAAK,IAAI,CAAA;AAAA,MAC9B,WAAA,EAAa,IAAA,CAAK,WAAA,IAAe,CAAA,MAAA,EAAS,KAAK,IAAI,CAAA,CAAA;AAAA,MACnD,MAAA,EAAQ;AAAA,KACV;AAEA,IAAA,MAAM,CAAA,GAAIC,cAAA;AAAA,MACR,OAAO,IAAA,KAAkB;AACvB,QAAA,MAAM,UAAA,GAAa,gBAAgB,IAAI,CAAA;AACvC,QAAA,MAAM,SAAS,MAAM,GAAA,CAAI,UAAA,CAAW,IAAA,CAAK,MAAM,UAAU,CAAA;AACzD,QAAA,OAAO,MAAA,CAAO,KACV,MAAA,CAAO,MAAA,GACP,EAAE,KAAA,EAAO,MAAA,CAAO,KAAA,EAAO,OAAA,IAAW,aAAA,EAAc;AAAA,MACtD,CAAA;AAAA,MACA;AAAA,KACF;AACA,IAAA,OAAO,CAAA;AAAA,EACT,CAAC,CAAA;AACH;AASA,eAAsBC,oBAAmB,UAAA,EAAoB;AAC3D,EAAA,MAAM,GAAA,GAAM,MAAMA,oCAAA,CAA8B,UAAU,CAAA;AAC1D,EAAA,OAAO,MAAA,CAAO,OAAO,GAAA,EAAK;AAAA,IACxB,IAAI,KAAA,GAAmB;AACrB,MAAA,OAAO,wBAAwB,GAAG,CAAA;AAAA,IACpC;AAAA,GACD,CAAA;AACH","file":"langchain-tools.cjs","sourcesContent":["/**\n * Bridge: ToolHub registry → LangChain tools (for use with LangChain agents, e.g. DeepAgents).\n *\n * Requires peer dependencies: langchain, zod. Use this entry when integrating ToolHub\n * with LangChain 1.x agents. Main package entry does not depend on LangChain.\n *\n * @example\n * ```ts\n * import { createAgentToolHub, toolHubToLangChainTools } from \"@easynet/agent-tool-hub/langchain-tools\";\n * const hub = await createAgentToolHub(\"toolhub.yaml\");\n * // hub.tools is the LangChain tools array\n * const agent = createDeepAgent({ tools: hub.tools, ... });\n * // Or: const tools = toolHubToLangChainTools(hub);\n * ```\n */\n\nimport { createAgentToolHub as createAgentToolHubFromRuntime } from \"./toolhub-runtime.js\";\nimport { tool } from \"langchain\";\nimport { z } from \"zod\";\nimport type { ToolSpec } from \"./types/ToolSpec.js\";\nimport type { ToolResult } from \"./types/ToolResult.js\";\n\n/** Minimal interface for a hub that can list tools and invoke them (e.g. ToolHub, AgentToolHub). */\nexport interface ToolHubLike {\n getRegistry(): { snapshot(): ToolSpec[] };\n invokeTool(name: string, args: unknown): Promise<ToolResult>;\n}\n\nconst TOOL_ARGS_SCHEMA = z\n .record(z.string(), z.unknown())\n .describe(\"Tool arguments as key-value object\");\n\n/**\n * Extracts a JSON object from tool args. Handles models that send text + JSON\n * (e.g. \"Let's fetch.{\\\"symbol\\\":\\\"GOOGL\\\"}\") by finding the last {...} and parsing.\n */\nfunction extractToolArgs(args: unknown): Record<string, unknown> {\n if (args != null && typeof args === \"object\" && !Array.isArray(args)) {\n return args as Record<string, unknown>;\n }\n const str = typeof args === \"string\" ? args : String(args ?? \"\");\n if (!str.trim()) return {};\n // Try parse whole string first\n try {\n const parsed = JSON.parse(str) as unknown;\n if (parsed != null && typeof parsed === \"object\" && !Array.isArray(parsed)) {\n return parsed as Record<string, unknown>;\n }\n } catch {\n // ignore\n }\n // Find last { ... } and parse\n const lastBrace = str.lastIndexOf(\"{\");\n if (lastBrace === -1) return {};\n try {\n const parsed = JSON.parse(str.slice(lastBrace)) as unknown;\n if (parsed != null && typeof parsed === \"object\" && !Array.isArray(parsed)) {\n return parsed as Record<string, unknown>;\n }\n } catch {\n // ignore\n }\n return {};\n}\n\n/**\n * Sanitize tool name for LLM/API: slashes and chars that some backends mangle become underscores.\n * Invoke still uses the original spec.name.\n */\nfunction toolNameForLLM(name: string): string {\n return name.replace(/[/.-]/g, \"_\");\n}\n\n/**\n * Converts a ToolHub (or AgentToolHub) registry into an array of LangChain tools.\n * Each tool delegates to hub.invokeTool(spec.name, args). Use with LangChain 1.x agents\n * (e.g. createDeepAgent from deepagents). Tool args are normalized: if the model\n * sends text + JSON (e.g. \"reasoning{\\\"symbol\\\":\\\"AAPL\\\"}\"), the JSON is extracted.\n * Tool names are sanitized for the LLM (e.g. tools/yahoo-finance → tools_yahoo_finance)\n * so OpenAI-compatible backends that mangle slashes still get a stable, matchable name.\n *\n * @param hub - Instance with getRegistry() and invokeTool(name, args)\n * @returns Array of LangChain tool instances (compatible with agent tools array)\n */\nexport function toolHubToLangChainTools(hub: ToolHubLike): unknown[] {\n const specs = hub.getRegistry().snapshot();\n return specs.map((spec) => {\n const opts = {\n name: toolNameForLLM(spec.name),\n description: spec.description ?? `Tool: ${spec.name}`,\n schema: TOOL_ARGS_SCHEMA,\n };\n // Cast opts to avoid TS2589 (excessively deep type instantiation) from langchain tool()\n const t = tool(\n async (args: unknown) => {\n const normalized = extractToolArgs(args);\n const result = await hub.invokeTool(spec.name, normalized);\n return result.ok\n ? result.result\n : { error: result.error?.message ?? \"Tool failed\" };\n },\n opts as Parameters<typeof tool>[1],\n );\n return t as unknown;\n });\n}\n\n/**\n * Create an AgentToolHub from a config path and return it with a `.tools` getter\n * that returns LangChain tools. Use with LangChain/DeepAgents agents.\n *\n * @param configPath - Path to toolhub.yaml (or other config file)\n * @returns AgentToolHub instance with `.tools` (array of LangChain tools)\n */\nexport async function createAgentToolHub(configPath: string) {\n const hub = await createAgentToolHubFromRuntime(configPath);\n return Object.assign(hub, {\n get tools(): unknown[] {\n return toolHubToLangChainTools(hub);\n },\n });\n}\n"]}
@@ -16,6 +16,8 @@ interface ToolHubLike {
16
16
  * Each tool delegates to hub.invokeTool(spec.name, args). Use with LangChain 1.x agents
17
17
  * (e.g. createDeepAgent from deepagents). Tool args are normalized: if the model
18
18
  * sends text + JSON (e.g. "reasoning{\"symbol\":\"AAPL\"}"), the JSON is extracted.
19
+ * Tool names are sanitized for the LLM (e.g. tools/yahoo-finance → tools_yahoo_finance)
20
+ * so OpenAI-compatible backends that mangle slashes still get a stable, matchable name.
19
21
  *
20
22
  * @param hub - Instance with getRegistry() and invokeTool(name, args)
21
23
  * @returns Array of LangChain tool instances (compatible with agent tools array)
@@ -16,6 +16,8 @@ interface ToolHubLike {
16
16
  * Each tool delegates to hub.invokeTool(spec.name, args). Use with LangChain 1.x agents
17
17
  * (e.g. createDeepAgent from deepagents). Tool args are normalized: if the model
18
18
  * sends text + JSON (e.g. "reasoning{\"symbol\":\"AAPL\"}"), the JSON is extracted.
19
+ * Tool names are sanitized for the LLM (e.g. tools/yahoo-finance → tools_yahoo_finance)
20
+ * so OpenAI-compatible backends that mangle slashes still get a stable, matchable name.
19
21
  *
20
22
  * @param hub - Instance with getRegistry() and invokeTool(name, args)
21
23
  * @returns Array of LangChain tool instances (compatible with agent tools array)
@@ -29,11 +29,14 @@ function extractToolArgs(args) {
29
29
  }
30
30
  return {};
31
31
  }
32
+ function toolNameForLLM(name) {
33
+ return name.replace(/[/.-]/g, "_");
34
+ }
32
35
  function toolHubToLangChainTools(hub) {
33
36
  const specs = hub.getRegistry().snapshot();
34
37
  return specs.map((spec) => {
35
38
  const opts = {
36
- name: spec.name,
39
+ name: toolNameForLLM(spec.name),
37
40
  description: spec.description ?? `Tool: ${spec.name}`,
38
41
  schema: TOOL_ARGS_SCHEMA
39
42
  };
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/langchain-tools.ts"],"names":["createAgentToolHub"],"mappings":";;;;;;AA4BA,IAAM,gBAAA,GAAmB,CAAA,CACtB,MAAA,CAAO,CAAA,CAAE,MAAA,EAAO,EAAG,CAAA,CAAE,OAAA,EAAS,CAAA,CAC9B,QAAA,CAAS,oCAAoC,CAAA;AAMhD,SAAS,gBAAgB,IAAA,EAAwC;AAC/D,EAAA,IAAI,IAAA,IAAQ,QAAQ,OAAO,IAAA,KAAS,YAAY,CAAC,KAAA,CAAM,OAAA,CAAQ,IAAI,CAAA,EAAG;AACpE,IAAA,OAAO,IAAA;AAAA,EACT;AACA,EAAA,MAAM,MAAM,OAAO,IAAA,KAAS,WAAW,IAAA,GAAO,MAAA,CAAO,QAAQ,EAAE,CAAA;AAC/D,EAAA,IAAI,CAAC,GAAA,CAAI,IAAA,EAAK,SAAU,EAAC;AAEzB,EAAA,IAAI;AACF,IAAA,MAAM,MAAA,GAAS,IAAA,CAAK,KAAA,CAAM,GAAG,CAAA;AAC7B,IAAA,IAAI,MAAA,IAAU,QAAQ,OAAO,MAAA,KAAW,YAAY,CAAC,KAAA,CAAM,OAAA,CAAQ,MAAM,CAAA,EAAG;AAC1E,MAAA,OAAO,MAAA;AAAA,IACT;AAAA,EACF,CAAA,CAAA,MAAQ;AAAA,EAER;AAEA,EAAA,MAAM,SAAA,GAAY,GAAA,CAAI,WAAA,CAAY,GAAG,CAAA;AACrC,EAAA,IAAI,SAAA,KAAc,EAAA,EAAI,OAAO,EAAC;AAC9B,EAAA,IAAI;AACF,IAAA,MAAM,SAAS,IAAA,CAAK,KAAA,CAAM,GAAA,CAAI,KAAA,CAAM,SAAS,CAAC,CAAA;AAC9C,IAAA,IAAI,MAAA,IAAU,QAAQ,OAAO,MAAA,KAAW,YAAY,CAAC,KAAA,CAAM,OAAA,CAAQ,MAAM,CAAA,EAAG;AAC1E,MAAA,OAAO,MAAA;AAAA,IACT;AAAA,EACF,CAAA,CAAA,MAAQ;AAAA,EAER;AACA,EAAA,OAAO,EAAC;AACV;AAWO,SAAS,wBAAwB,GAAA,EAA6B;AACnE,EAAA,MAAM,KAAA,GAAQ,GAAA,CAAI,WAAA,EAAY,CAAE,QAAA,EAAS;AACzC,EAAA,OAAO,KAAA,CAAM,GAAA,CAAI,CAAC,IAAA,KAAS;AACzB,IAAA,MAAM,IAAA,GAAO;AAAA,MACX,MAAM,IAAA,CAAK,IAAA;AAAA,MACX,WAAA,EAAa,IAAA,CAAK,WAAA,IAAe,CAAA,MAAA,EAAS,KAAK,IAAI,CAAA,CAAA;AAAA,MACnD,MAAA,EAAQ;AAAA,KACV;AAEA,IAAA,MAAM,CAAA,GAAI,IAAA;AAAA,MACR,OAAO,IAAA,KAAkB;AACvB,QAAA,MAAM,UAAA,GAAa,gBAAgB,IAAI,CAAA;AACvC,QAAA,MAAM,SAAS,MAAM,GAAA,CAAI,UAAA,CAAW,IAAA,CAAK,MAAM,UAAU,CAAA;AACzD,QAAA,OAAO,MAAA,CAAO,KACV,MAAA,CAAO,MAAA,GACP,EAAE,KAAA,EAAO,MAAA,CAAO,KAAA,EAAO,OAAA,IAAW,aAAA,EAAc;AAAA,MACtD,CAAA;AAAA,MACA;AAAA,KACF;AACA,IAAA,OAAO,CAAA;AAAA,EACT,CAAC,CAAA;AACH;AASA,eAAsBA,oBAAmB,UAAA,EAAoB;AAC3D,EAAA,MAAM,GAAA,GAAM,MAAM,kBAAA,CAA8B,UAAU,CAAA;AAC1D,EAAA,OAAO,MAAA,CAAO,OAAO,GAAA,EAAK;AAAA,IACxB,IAAI,KAAA,GAAmB;AACrB,MAAA,OAAO,wBAAwB,GAAG,CAAA;AAAA,IACpC;AAAA,GACD,CAAA;AACH","file":"langchain-tools.js","sourcesContent":["/**\n * Bridge: ToolHub registry → LangChain tools (for use with LangChain agents, e.g. DeepAgents).\n *\n * Requires peer dependencies: langchain, zod. Use this entry when integrating ToolHub\n * with LangChain 1.x agents. Main package entry does not depend on LangChain.\n *\n * @example\n * ```ts\n * import { createAgentToolHub, toolHubToLangChainTools } from \"@easynet/agent-tool-hub/langchain-tools\";\n * const hub = await createAgentToolHub(\"toolhub.yaml\");\n * // hub.tools is the LangChain tools array\n * const agent = createDeepAgent({ tools: hub.tools, ... });\n * // Or: const tools = toolHubToLangChainTools(hub);\n * ```\n */\n\nimport { createAgentToolHub as createAgentToolHubFromRuntime } from \"./toolhub-runtime.js\";\nimport { tool } from \"langchain\";\nimport { z } from \"zod\";\nimport type { ToolSpec } from \"./types/ToolSpec.js\";\nimport type { ToolResult } from \"./types/ToolResult.js\";\n\n/** Minimal interface for a hub that can list tools and invoke them (e.g. ToolHub, AgentToolHub). */\nexport interface ToolHubLike {\n getRegistry(): { snapshot(): ToolSpec[] };\n invokeTool(name: string, args: unknown): Promise<ToolResult>;\n}\n\nconst TOOL_ARGS_SCHEMA = z\n .record(z.string(), z.unknown())\n .describe(\"Tool arguments as key-value object\");\n\n/**\n * Extracts a JSON object from tool args. Handles models that send text + JSON\n * (e.g. \"Let's fetch.{\\\"symbol\\\":\\\"GOOGL\\\"}\") by finding the last {...} and parsing.\n */\nfunction extractToolArgs(args: unknown): Record<string, unknown> {\n if (args != null && typeof args === \"object\" && !Array.isArray(args)) {\n return args as Record<string, unknown>;\n }\n const str = typeof args === \"string\" ? args : String(args ?? \"\");\n if (!str.trim()) return {};\n // Try parse whole string first\n try {\n const parsed = JSON.parse(str) as unknown;\n if (parsed != null && typeof parsed === \"object\" && !Array.isArray(parsed)) {\n return parsed as Record<string, unknown>;\n }\n } catch {\n // ignore\n }\n // Find last { ... } and parse\n const lastBrace = str.lastIndexOf(\"{\");\n if (lastBrace === -1) return {};\n try {\n const parsed = JSON.parse(str.slice(lastBrace)) as unknown;\n if (parsed != null && typeof parsed === \"object\" && !Array.isArray(parsed)) {\n return parsed as Record<string, unknown>;\n }\n } catch {\n // ignore\n }\n return {};\n}\n\n/**\n * Converts a ToolHub (or AgentToolHub) registry into an array of LangChain tools.\n * Each tool delegates to hub.invokeTool(spec.name, args). Use with LangChain 1.x agents\n * (e.g. createDeepAgent from deepagents). Tool args are normalized: if the model\n * sends text + JSON (e.g. \"reasoning{\\\"symbol\\\":\\\"AAPL\\\"}\"), the JSON is extracted.\n *\n * @param hub - Instance with getRegistry() and invokeTool(name, args)\n * @returns Array of LangChain tool instances (compatible with agent tools array)\n */\nexport function toolHubToLangChainTools(hub: ToolHubLike): unknown[] {\n const specs = hub.getRegistry().snapshot();\n return specs.map((spec) => {\n const opts = {\n name: spec.name,\n description: spec.description ?? `Tool: ${spec.name}`,\n schema: TOOL_ARGS_SCHEMA,\n };\n // Cast opts to avoid TS2589 (excessively deep type instantiation) from langchain tool()\n const t = tool(\n async (args: unknown) => {\n const normalized = extractToolArgs(args);\n const result = await hub.invokeTool(spec.name, normalized);\n return result.ok\n ? result.result\n : { error: result.error?.message ?? \"Tool failed\" };\n },\n opts as Parameters<typeof tool>[1],\n );\n return t as unknown;\n });\n}\n\n/**\n * Create an AgentToolHub from a config path and return it with a `.tools` getter\n * that returns LangChain tools. Use with LangChain/DeepAgents agents.\n *\n * @param configPath - Path to toolhub.yaml (or other config file)\n * @returns AgentToolHub instance with `.tools` (array of LangChain tools)\n */\nexport async function createAgentToolHub(configPath: string) {\n const hub = await createAgentToolHubFromRuntime(configPath);\n return Object.assign(hub, {\n get tools(): unknown[] {\n return toolHubToLangChainTools(hub);\n },\n });\n}\n"]}
1
+ {"version":3,"sources":["../src/langchain-tools.ts"],"names":["createAgentToolHub"],"mappings":";;;;;;AA4BA,IAAM,gBAAA,GAAmB,CAAA,CACtB,MAAA,CAAO,CAAA,CAAE,MAAA,EAAO,EAAG,CAAA,CAAE,OAAA,EAAS,CAAA,CAC9B,QAAA,CAAS,oCAAoC,CAAA;AAMhD,SAAS,gBAAgB,IAAA,EAAwC;AAC/D,EAAA,IAAI,IAAA,IAAQ,QAAQ,OAAO,IAAA,KAAS,YAAY,CAAC,KAAA,CAAM,OAAA,CAAQ,IAAI,CAAA,EAAG;AACpE,IAAA,OAAO,IAAA;AAAA,EACT;AACA,EAAA,MAAM,MAAM,OAAO,IAAA,KAAS,WAAW,IAAA,GAAO,MAAA,CAAO,QAAQ,EAAE,CAAA;AAC/D,EAAA,IAAI,CAAC,GAAA,CAAI,IAAA,EAAK,SAAU,EAAC;AAEzB,EAAA,IAAI;AACF,IAAA,MAAM,MAAA,GAAS,IAAA,CAAK,KAAA,CAAM,GAAG,CAAA;AAC7B,IAAA,IAAI,MAAA,IAAU,QAAQ,OAAO,MAAA,KAAW,YAAY,CAAC,KAAA,CAAM,OAAA,CAAQ,MAAM,CAAA,EAAG;AAC1E,MAAA,OAAO,MAAA;AAAA,IACT;AAAA,EACF,CAAA,CAAA,MAAQ;AAAA,EAER;AAEA,EAAA,MAAM,SAAA,GAAY,GAAA,CAAI,WAAA,CAAY,GAAG,CAAA;AACrC,EAAA,IAAI,SAAA,KAAc,EAAA,EAAI,OAAO,EAAC;AAC9B,EAAA,IAAI;AACF,IAAA,MAAM,SAAS,IAAA,CAAK,KAAA,CAAM,GAAA,CAAI,KAAA,CAAM,SAAS,CAAC,CAAA;AAC9C,IAAA,IAAI,MAAA,IAAU,QAAQ,OAAO,MAAA,KAAW,YAAY,CAAC,KAAA,CAAM,OAAA,CAAQ,MAAM,CAAA,EAAG;AAC1E,MAAA,OAAO,MAAA;AAAA,IACT;AAAA,EACF,CAAA,CAAA,MAAQ;AAAA,EAER;AACA,EAAA,OAAO,EAAC;AACV;AAMA,SAAS,eAAe,IAAA,EAAsB;AAC5C,EAAA,OAAO,IAAA,CAAK,OAAA,CAAQ,QAAA,EAAU,GAAG,CAAA;AACnC;AAaO,SAAS,wBAAwB,GAAA,EAA6B;AACnE,EAAA,MAAM,KAAA,GAAQ,GAAA,CAAI,WAAA,EAAY,CAAE,QAAA,EAAS;AACzC,EAAA,OAAO,KAAA,CAAM,GAAA,CAAI,CAAC,IAAA,KAAS;AACzB,IAAA,MAAM,IAAA,GAAO;AAAA,MACX,IAAA,EAAM,cAAA,CAAe,IAAA,CAAK,IAAI,CAAA;AAAA,MAC9B,WAAA,EAAa,IAAA,CAAK,WAAA,IAAe,CAAA,MAAA,EAAS,KAAK,IAAI,CAAA,CAAA;AAAA,MACnD,MAAA,EAAQ;AAAA,KACV;AAEA,IAAA,MAAM,CAAA,GAAI,IAAA;AAAA,MACR,OAAO,IAAA,KAAkB;AACvB,QAAA,MAAM,UAAA,GAAa,gBAAgB,IAAI,CAAA;AACvC,QAAA,MAAM,SAAS,MAAM,GAAA,CAAI,UAAA,CAAW,IAAA,CAAK,MAAM,UAAU,CAAA;AACzD,QAAA,OAAO,MAAA,CAAO,KACV,MAAA,CAAO,MAAA,GACP,EAAE,KAAA,EAAO,MAAA,CAAO,KAAA,EAAO,OAAA,IAAW,aAAA,EAAc;AAAA,MACtD,CAAA;AAAA,MACA;AAAA,KACF;AACA,IAAA,OAAO,CAAA;AAAA,EACT,CAAC,CAAA;AACH;AASA,eAAsBA,oBAAmB,UAAA,EAAoB;AAC3D,EAAA,MAAM,GAAA,GAAM,MAAM,kBAAA,CAA8B,UAAU,CAAA;AAC1D,EAAA,OAAO,MAAA,CAAO,OAAO,GAAA,EAAK;AAAA,IACxB,IAAI,KAAA,GAAmB;AACrB,MAAA,OAAO,wBAAwB,GAAG,CAAA;AAAA,IACpC;AAAA,GACD,CAAA;AACH","file":"langchain-tools.js","sourcesContent":["/**\n * Bridge: ToolHub registry → LangChain tools (for use with LangChain agents, e.g. DeepAgents).\n *\n * Requires peer dependencies: langchain, zod. Use this entry when integrating ToolHub\n * with LangChain 1.x agents. Main package entry does not depend on LangChain.\n *\n * @example\n * ```ts\n * import { createAgentToolHub, toolHubToLangChainTools } from \"@easynet/agent-tool-hub/langchain-tools\";\n * const hub = await createAgentToolHub(\"toolhub.yaml\");\n * // hub.tools is the LangChain tools array\n * const agent = createDeepAgent({ tools: hub.tools, ... });\n * // Or: const tools = toolHubToLangChainTools(hub);\n * ```\n */\n\nimport { createAgentToolHub as createAgentToolHubFromRuntime } from \"./toolhub-runtime.js\";\nimport { tool } from \"langchain\";\nimport { z } from \"zod\";\nimport type { ToolSpec } from \"./types/ToolSpec.js\";\nimport type { ToolResult } from \"./types/ToolResult.js\";\n\n/** Minimal interface for a hub that can list tools and invoke them (e.g. ToolHub, AgentToolHub). */\nexport interface ToolHubLike {\n getRegistry(): { snapshot(): ToolSpec[] };\n invokeTool(name: string, args: unknown): Promise<ToolResult>;\n}\n\nconst TOOL_ARGS_SCHEMA = z\n .record(z.string(), z.unknown())\n .describe(\"Tool arguments as key-value object\");\n\n/**\n * Extracts a JSON object from tool args. Handles models that send text + JSON\n * (e.g. \"Let's fetch.{\\\"symbol\\\":\\\"GOOGL\\\"}\") by finding the last {...} and parsing.\n */\nfunction extractToolArgs(args: unknown): Record<string, unknown> {\n if (args != null && typeof args === \"object\" && !Array.isArray(args)) {\n return args as Record<string, unknown>;\n }\n const str = typeof args === \"string\" ? args : String(args ?? \"\");\n if (!str.trim()) return {};\n // Try parse whole string first\n try {\n const parsed = JSON.parse(str) as unknown;\n if (parsed != null && typeof parsed === \"object\" && !Array.isArray(parsed)) {\n return parsed as Record<string, unknown>;\n }\n } catch {\n // ignore\n }\n // Find last { ... } and parse\n const lastBrace = str.lastIndexOf(\"{\");\n if (lastBrace === -1) return {};\n try {\n const parsed = JSON.parse(str.slice(lastBrace)) as unknown;\n if (parsed != null && typeof parsed === \"object\" && !Array.isArray(parsed)) {\n return parsed as Record<string, unknown>;\n }\n } catch {\n // ignore\n }\n return {};\n}\n\n/**\n * Sanitize tool name for LLM/API: slashes and chars that some backends mangle become underscores.\n * Invoke still uses the original spec.name.\n */\nfunction toolNameForLLM(name: string): string {\n return name.replace(/[/.-]/g, \"_\");\n}\n\n/**\n * Converts a ToolHub (or AgentToolHub) registry into an array of LangChain tools.\n * Each tool delegates to hub.invokeTool(spec.name, args). Use with LangChain 1.x agents\n * (e.g. createDeepAgent from deepagents). Tool args are normalized: if the model\n * sends text + JSON (e.g. \"reasoning{\\\"symbol\\\":\\\"AAPL\\\"}\"), the JSON is extracted.\n * Tool names are sanitized for the LLM (e.g. tools/yahoo-finance → tools_yahoo_finance)\n * so OpenAI-compatible backends that mangle slashes still get a stable, matchable name.\n *\n * @param hub - Instance with getRegistry() and invokeTool(name, args)\n * @returns Array of LangChain tool instances (compatible with agent tools array)\n */\nexport function toolHubToLangChainTools(hub: ToolHubLike): unknown[] {\n const specs = hub.getRegistry().snapshot();\n return specs.map((spec) => {\n const opts = {\n name: toolNameForLLM(spec.name),\n description: spec.description ?? `Tool: ${spec.name}`,\n schema: TOOL_ARGS_SCHEMA,\n };\n // Cast opts to avoid TS2589 (excessively deep type instantiation) from langchain tool()\n const t = tool(\n async (args: unknown) => {\n const normalized = extractToolArgs(args);\n const result = await hub.invokeTool(spec.name, normalized);\n return result.ok\n ? result.result\n : { error: result.error?.message ?? \"Tool failed\" };\n },\n opts as Parameters<typeof tool>[1],\n );\n return t as unknown;\n });\n}\n\n/**\n * Create an AgentToolHub from a config path and return it with a `.tools` getter\n * that returns LangChain tools. Use with LangChain/DeepAgents agents.\n *\n * @param configPath - Path to toolhub.yaml (or other config file)\n * @returns AgentToolHub instance with `.tools` (array of LangChain tools)\n */\nexport async function createAgentToolHub(configPath: string) {\n const hub = await createAgentToolHubFromRuntime(configPath);\n return Object.assign(hub, {\n get tools(): unknown[] {\n return toolHubToLangChainTools(hub);\n },\n });\n}\n"]}
@@ -0,0 +1,21 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ const { spawnSync } = require("child_process");
4
+ const path = require("path");
5
+
6
+ const scriptDir = __dirname;
7
+ const mjsPath = path.join(scriptDir, "agent-toolhub-react-stock.mjs");
8
+ const packageRoot = path.join(scriptDir, "..");
9
+
10
+ // Use tsx so dependencies (e.g. @easynet/n8n-local) that ship .ts can be loaded
11
+ let nodeArgs = [mjsPath];
12
+ try {
13
+ require.resolve("tsx", { paths: [packageRoot] });
14
+ nodeArgs = ["--import", "tsx", mjsPath];
15
+ } catch (_) {
16
+ // tsx not available; run without it (may fail if a dep ships .ts)
17
+ }
18
+ const result = spawnSync(process.execPath, [...nodeArgs, ...process.argv.slice(2)], {
19
+ stdio: "inherit",
20
+ });
21
+ process.exitCode = result.status !== null ? result.status : 1;
@@ -1,3 +1,4 @@
1
+ #!/usr/bin/env node
1
2
  import { resolve, join, dirname } from "node:path";
2
3
  import { fileURLToPath } from "node:url";
3
4
  import { createAgent } from "langchain";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@easynet/agent-tool-hub",
3
- "version": "1.0.14",
3
+ "version": "1.0.16",
4
4
  "description": "Agent Tool Hub: multi-protocol tool registry, PTC runtime, and adapter layer for MCP/LangChain/n8n/SKILL",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",
@@ -8,7 +8,7 @@
8
8
  "types": "./dist/index.d.ts",
9
9
  "bin": {
10
10
  "agent-tool-hub": "dist/cli.js",
11
- "agent-toolhub-react-stock": "examples/agent-toolhub-react-stock.mjs"
11
+ "agent-toolhub-react-stock": "examples/agent-toolhub-react-stock-bin.cjs"
12
12
  },
13
13
  "exports": {
14
14
  ".": {
@@ -30,8 +30,8 @@
30
30
  "access": "public"
31
31
  },
32
32
  "scripts": {
33
- "build": "tsup",
34
- "dev": "tsup --watch",
33
+ "build": "npx tsup",
34
+ "dev": "npx tsup --watch",
35
35
  "example:agent-toolhub": "node examples/agent-toolhub-example.mjs",
36
36
  "example:agent-toolhub-react-stock": "node --import tsx examples/agent-toolhub-react-stock.mjs",
37
37
  "test": "vitest run",
@@ -53,6 +53,7 @@
53
53
  "mustache": "^4.2.0",
54
54
  "p-retry": "^6.2.1",
55
55
  "p-timeout": "^6.1.4",
56
+ "tsx": "^4.21.0",
56
57
  "uuid": "^11.0.5"
57
58
  },
58
59
  "devDependencies": {
@@ -67,7 +68,6 @@
67
68
  "langchain": "^1.0.0",
68
69
  "semantic-release": "^24.2.0",
69
70
  "tsup": "^8.3.5",
70
- "tsx": "^4.21.0",
71
71
  "typescript": "^5.7.2",
72
72
  "vitest": "^2.1.8",
73
73
  "zod": "^3.23.0"