clodds 1.6.6 → 1.6.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.
@@ -49,6 +49,7 @@ const fs_1 = require("fs");
49
49
  const path_1 = require("path");
50
50
  const os_1 = require("os");
51
51
  const logger_1 = require("../utils/logger");
52
+ const tool_registry_js_1 = require("./tool-registry.js");
52
53
  const loader_1 = require("../skills/loader");
53
54
  // credentials module is private (gitignored) — lazy-load at runtime
54
55
  const _credPath = '../credentials/index.js';
@@ -7875,6 +7876,33 @@ function buildTools() {
7875
7876
  required: ['action'],
7876
7877
  },
7877
7878
  },
7879
+ // Tool search meta-tool (always included in core set)
7880
+ {
7881
+ name: 'tool_search',
7882
+ description: 'Search for specialized tools by platform, category, or keyword. Use this BEFORE attempting to use a tool that is not in your current tool set. Returns tool definitions you can use in follow-up requests. Available platforms include: polymarket, kalshi, manifold, metaculus, drift, opinion, predictfun, binance, bybit, mexc, hyperliquid, solana, pumpfun, bags, meteora, raydium, orca, coingecko, yahoo, acp, docker, git. Categories include: trading, market_data, portfolio, discovery, admin, infrastructure, defi, alerts.',
7883
+ input_schema: {
7884
+ type: 'object',
7885
+ properties: {
7886
+ platform: {
7887
+ type: 'string',
7888
+ description: 'Platform to search: polymarket, kalshi, manifold, metaculus, drift, opinion, predictfun, binance, bybit, mexc, hyperliquid, solana, pumpfun, bags, meteora, raydium, orca, coingecko, yahoo, acp, docker, git',
7889
+ },
7890
+ category: {
7891
+ type: 'string',
7892
+ description: 'Tool category: trading, market_data, portfolio, discovery, admin, infrastructure, defi, alerts',
7893
+ },
7894
+ query: {
7895
+ type: 'string',
7896
+ description: 'Keyword search: "buy order", "balance", "swap", "liquidity" etc.',
7897
+ },
7898
+ },
7899
+ },
7900
+ metadata: {
7901
+ category: 'admin',
7902
+ tags: ['meta', 'search', 'discovery', 'tools'],
7903
+ core: true,
7904
+ },
7905
+ },
7878
7906
  ];
7879
7907
  }
7880
7908
  function buildQmdEnv() {
@@ -15891,6 +15919,13 @@ async function executeTool(toolName, toolInput, context) {
15891
15919
  }
15892
15920
  return JSON.stringify({ result: 'Progress updated', id });
15893
15921
  }
15922
+ case 'tool_search': {
15923
+ // Fallback when TOOL_SEARCH_ENABLED is false or handler not intercepted.
15924
+ // Return a helpful message so Claude doesn't get a confusing error.
15925
+ return JSON.stringify({
15926
+ error: 'tool_search is not enabled. All tools are already available — use them directly.',
15927
+ });
15928
+ }
15894
15929
  default: {
15895
15930
  // Try modular handlers (Solana DEX, Bags.fm, Betfair, Smarkets, Opinion, Virtuals, etc.)
15896
15931
  if ((0, handlers_1.hasHandler)(toolName)) {
@@ -15983,7 +16018,34 @@ async function createAgentManager(config, feeds, db, sessionManager, sendMessage
15983
16018
  parseMode: 'Markdown',
15984
16019
  });
15985
16020
  });
15986
- const tools = buildTools();
16021
+ const allToolDefs = buildTools();
16022
+ // Build tool registry with inferred metadata
16023
+ const toolRegistry = new tool_registry_js_1.ToolRegistry();
16024
+ for (const tool of allToolDefs) {
16025
+ const inferred = (0, tool_registry_js_1.inferToolMetadata)(tool.name, tool.description);
16026
+ const isCore = tool_registry_js_1.CORE_TOOL_NAMES.has(tool.name);
16027
+ toolRegistry.register({
16028
+ ...tool,
16029
+ metadata: {
16030
+ ...inferred,
16031
+ ...tool.metadata, // explicit metadata overrides inferred
16032
+ core: tool.metadata?.core ?? isCore,
16033
+ },
16034
+ });
16035
+ }
16036
+ // Dynamic tool loading enabled by default. Set TOOL_SEARCH_ENABLED=false to disable.
16037
+ const TOOL_SEARCH_ENABLED = process.env.TOOL_SEARCH_ENABLED !== 'false';
16038
+ // Core tools (always sent) vs all tools (legacy mode)
16039
+ const coreTools = toolRegistry.getCoreTools();
16040
+ // When disabled, send all tools EXCEPT tool_search (no point confusing the LLM)
16041
+ const tools = TOOL_SEARCH_ENABLED
16042
+ ? coreTools
16043
+ : allToolDefs.filter(t => t.name !== 'tool_search');
16044
+ logger_1.logger.info({
16045
+ totalTools: allToolDefs.length,
16046
+ coreTools: coreTools.length,
16047
+ toolSearchEnabled: TOOL_SEARCH_ENABLED,
16048
+ }, 'Tool registry initialized');
15987
16049
  const getConfig = configProvider || (() => config);
15988
16050
  const getWebhooks = webhookToolProvider || (() => undefined);
15989
16051
  const summarizer = (0, memory_1.createClaudeSummarizer)();
@@ -16359,6 +16421,36 @@ async function createAgentManager(config, feeds, db, sessionManager, sendMessage
16359
16421
  const effectiveMaxTokens = modelContextWindow - reserveTokens;
16360
16422
  // Track actual API token usage for accurate compaction decisions
16361
16423
  let lastKnownInputTokens = 0;
16424
+ // Dynamic tool loading: tools discovered via tool_search during this request
16425
+ const discoveredTools = [];
16426
+ // Preload platform/category tools based on user message keywords
16427
+ if (TOOL_SEARCH_ENABLED && processedMessage.text) {
16428
+ const hints = (0, tool_registry_js_1.detectToolHints)(processedMessage.text);
16429
+ const preloaded = new Set();
16430
+ for (const platform of hints.platforms) {
16431
+ for (const t of toolRegistry.searchByPlatform(platform)) {
16432
+ if (!preloaded.has(t.name)) {
16433
+ discoveredTools.push(t);
16434
+ preloaded.add(t.name);
16435
+ }
16436
+ }
16437
+ }
16438
+ for (const category of hints.categories) {
16439
+ for (const t of toolRegistry.searchByCategory(category)) {
16440
+ if (!preloaded.has(t.name)) {
16441
+ discoveredTools.push(t);
16442
+ preloaded.add(t.name);
16443
+ }
16444
+ }
16445
+ }
16446
+ if (discoveredTools.length > 0) {
16447
+ logger_1.logger.info({
16448
+ platforms: hints.platforms,
16449
+ categories: hints.categories,
16450
+ preloaded: discoveredTools.length,
16451
+ }, 'Preloaded tools from message keywords');
16452
+ }
16453
+ }
16362
16454
  // Add all messages to context manager for tracking
16363
16455
  for (const msg of messages) {
16364
16456
  const content = typeof msg.content === 'string' ? msg.content : JSON.stringify(msg.content);
@@ -16409,13 +16501,23 @@ async function createAgentManager(config, feeds, db, sessionManager, sendMessage
16409
16501
  }, 'Context compacted successfully');
16410
16502
  }
16411
16503
  }
16504
+ // Build dynamic tool set: core tools + any discovered tools
16505
+ const getActiveTools = () => {
16506
+ if (!TOOL_SEARCH_ENABLED || discoveredTools.length === 0)
16507
+ return tools;
16508
+ // Dedupe by name (core tools + discovered)
16509
+ const seen = new Set(tools.map(t => t.name));
16510
+ const extra = discoveredTools.filter(t => !seen.has(t.name));
16511
+ return [...tools, ...extra];
16512
+ };
16412
16513
  let response;
16413
16514
  try {
16515
+ const activeTools = getActiveTools();
16414
16516
  response = await createMessage({
16415
16517
  model: modelId,
16416
16518
  max_tokens: 1024,
16417
16519
  system: finalSystemPrompt,
16418
- tools: tools,
16520
+ tools: activeTools,
16419
16521
  messages,
16420
16522
  });
16421
16523
  }
@@ -16488,7 +16590,48 @@ async function createAgentManager(config, feeds, db, sessionManager, sendMessage
16488
16590
  void notifyToolStatus(`Running tool: ${block.name}...`);
16489
16591
  }, TOOL_STREAM_DELAY_MS);
16490
16592
  }
16491
- const result = await executeTool(block.name, finalParams, context);
16593
+ let result;
16594
+ // Handle tool_search in-scope (needs access to toolRegistry)
16595
+ if (block.name === 'tool_search' && TOOL_SEARCH_ENABLED) {
16596
+ const { platform, category, query } = finalParams;
16597
+ let searchResults;
16598
+ if (platform) {
16599
+ searchResults = toolRegistry.searchByPlatform(platform);
16600
+ }
16601
+ else if (category) {
16602
+ searchResults = toolRegistry.searchByCategory(category);
16603
+ }
16604
+ else if (query) {
16605
+ searchResults = toolRegistry.searchByText(query);
16606
+ }
16607
+ else {
16608
+ searchResults = [];
16609
+ }
16610
+ // Take top 25 results
16611
+ const topResults = searchResults.slice(0, 25);
16612
+ // Store discovered tools for next API call (dedupe)
16613
+ const alreadyDiscovered = new Set(discoveredTools.map(t => t.name));
16614
+ for (const t of topResults) {
16615
+ if (!alreadyDiscovered.has(t.name)) {
16616
+ discoveredTools.push(t);
16617
+ }
16618
+ }
16619
+ result = JSON.stringify({
16620
+ found: topResults.length,
16621
+ total_available: searchResults.length,
16622
+ tools: topResults.map(t => ({
16623
+ name: t.name,
16624
+ description: t.description,
16625
+ })),
16626
+ hint: topResults.length > 0
16627
+ ? 'These tools are now available for you to use. Call them directly.'
16628
+ : 'No tools found. Try a different search query or platform.',
16629
+ });
16630
+ logger_1.logger.info({ platform, category, query, found: topResults.length }, 'tool_search executed');
16631
+ }
16632
+ else {
16633
+ result = await executeTool(block.name, finalParams, context);
16634
+ }
16492
16635
  if (announceTimer) {
16493
16636
  clearTimeout(announceTimer);
16494
16637
  announceTimer = null;
@@ -16547,11 +16690,12 @@ async function createAgentManager(config, feeds, db, sessionManager, sendMessage
16547
16690
  }
16548
16691
  }
16549
16692
  try {
16693
+ const activeTools = getActiveTools();
16550
16694
  response = await createMessage({
16551
16695
  model: modelId,
16552
16696
  max_tokens: 1024,
16553
16697
  system: finalSystemPrompt,
16554
- tools: tools,
16698
+ tools: activeTools,
16555
16699
  messages,
16556
16700
  });
16557
16701
  }