clodds 1.6.7 → 1.6.9

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.
@@ -16024,14 +16024,16 @@ async function createAgentManager(config, feeds, db, sessionManager, sendMessage
16024
16024
  for (const tool of allToolDefs) {
16025
16025
  const inferred = (0, tool_registry_js_1.inferToolMetadata)(tool.name, tool.description);
16026
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
- });
16027
+ const merged = {
16028
+ ...inferred,
16029
+ ...tool.metadata, // explicit metadata overrides inferred
16030
+ core: tool.metadata?.core ?? isCore,
16031
+ };
16032
+ // Sync categories with explicit category override to prevent divergence
16033
+ if (tool.metadata?.category && !tool.metadata?.categories) {
16034
+ merged.categories = [tool.metadata.category, ...(inferred.categories ?? []).filter(c => c !== tool.metadata.category)];
16035
+ }
16036
+ toolRegistry.register({ ...tool, metadata: merged });
16035
16037
  }
16036
16038
  // Dynamic tool loading enabled by default. Set TOOL_SEARCH_ENABLED=false to disable.
16037
16039
  const TOOL_SEARCH_ENABLED = process.env.TOOL_SEARCH_ENABLED !== 'false';
@@ -16423,28 +16425,103 @@ async function createAgentManager(config, feeds, db, sessionManager, sendMessage
16423
16425
  let lastKnownInputTokens = 0;
16424
16426
  // Dynamic tool loading: tools discovered via tool_search during this request
16425
16427
  const discoveredTools = [];
16426
- // Preload platform/category tools based on user message keywords
16428
+ // Preload platform/category tools based on user message keywords.
16429
+ // Uses intersection mode when both platform AND intent are detected
16430
+ // to avoid loading all tools from multiple platforms (~150+).
16431
+ // Also checks conversation context for platform hints in multi-turn chats.
16427
16432
  if (TOOL_SEARCH_ENABLED && processedMessage.text) {
16428
16433
  const hints = (0, tool_registry_js_1.detectToolHints)(processedMessage.text);
16434
+ // CONVERSATION CONTEXT: If no platform in current message, borrow from recent history.
16435
+ // "buy YES at 40 cents" after discussing polymarket → still loads polymarket tools.
16436
+ if (hints.platforms.length === 0 && messages.length > 1) {
16437
+ const userMsgs = messages.filter(m => m.role === 'user');
16438
+ // Exclude current message (last one) — we already parsed it above
16439
+ const recentUserMsgs = userMsgs.slice(0, -1).slice(-4)
16440
+ .map(m => typeof m.content === 'string' ? m.content : '')
16441
+ .join(' ');
16442
+ if (recentUserMsgs) {
16443
+ const contextHints = (0, tool_registry_js_1.detectToolHints)(recentUserMsgs);
16444
+ // Only borrow platforms from context, not categories (current intent is authoritative)
16445
+ for (const p of contextHints.platforms)
16446
+ hints.platforms.push(p);
16447
+ }
16448
+ }
16429
16449
  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);
16450
+ const GLOBAL_PRELOAD_CAP = 35;
16451
+ const MAX_TOOLS_PER_PLATFORM = 10;
16452
+ let preloadMode = 'none';
16453
+ // Helper: add a tool if not already preloaded and under global cap
16454
+ const addTool = (t) => {
16455
+ if (preloaded.size >= GLOBAL_PRELOAD_CAP)
16456
+ return false;
16457
+ if (preloaded.has(t.name))
16458
+ return false;
16459
+ discoveredTools.push(t);
16460
+ preloaded.add(t.name);
16461
+ return true;
16462
+ };
16463
+ // Helper: load top tools per platform, sorted by category priority
16464
+ const loadPlatformFallback = () => {
16465
+ const priorityRank = {
16466
+ trading: 4, market_data: 3, portfolio: 2, defi: 1,
16467
+ };
16468
+ for (const platform of hints.platforms) {
16469
+ const sorted = [...toolRegistry.searchByPlatform(platform)].sort((a, b) => {
16470
+ const aRank = priorityRank[a.metadata?.category ?? ''] ?? 0;
16471
+ const bRank = priorityRank[b.metadata?.category ?? ''] ?? 0;
16472
+ return bRank - aRank;
16473
+ });
16474
+ for (const t of sorted.slice(0, MAX_TOOLS_PER_PLATFORM)) {
16475
+ addTool(t);
16476
+ }
16477
+ }
16478
+ };
16479
+ if (hints.hasIntent && hints.platforms.length > 0) {
16480
+ // INTERSECTION MODE: Both platform AND intent detected
16481
+ // Only load tools matching BOTH criteria (e.g. polymarket + trading)
16482
+ preloadMode = 'intersection';
16483
+ for (const platform of hints.platforms) {
16484
+ for (const category of hints.categories) {
16485
+ for (const t of toolRegistry.searchByPlatformAndCategory(platform, category)) {
16486
+ if (!addTool(t))
16487
+ break; // hit global cap
16488
+ }
16435
16489
  }
16436
16490
  }
16491
+ // If intersection found nothing, supplement with platform fallback.
16492
+ // With multi-category assignment this should be rare.
16493
+ if (preloaded.size === 0) {
16494
+ preloadMode = 'intersection+platform_fallback';
16495
+ loadPlatformFallback();
16496
+ }
16437
16497
  }
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);
16498
+ else if (hints.platforms.length > 0) {
16499
+ // FALLBACK 1: Platform only, no clear intent
16500
+ // Load top tools per platform, prioritize trading/market_data/portfolio
16501
+ preloadMode = 'platform_fallback';
16502
+ loadPlatformFallback();
16503
+ }
16504
+ else if (hints.categories.length > 0) {
16505
+ // FALLBACK 2: Intent only, no platform
16506
+ // Distribute tools across platforms to avoid single-platform bias
16507
+ preloadMode = 'intent_fallback';
16508
+ const MAX_PER_PLATFORM_INTENT = 3;
16509
+ for (const category of hints.categories) {
16510
+ const perPlatformCount = new Map();
16511
+ for (const t of toolRegistry.searchByCategory(category)) {
16512
+ const plat = t.metadata?.platform ?? 'unknown';
16513
+ const count = perPlatformCount.get(plat) ?? 0;
16514
+ if (count >= MAX_PER_PLATFORM_INTENT)
16515
+ continue;
16516
+ if (!addTool(t))
16517
+ break; // hit global cap
16518
+ perPlatformCount.set(plat, count + 1);
16443
16519
  }
16444
16520
  }
16445
16521
  }
16446
16522
  if (discoveredTools.length > 0) {
16447
16523
  logger_1.logger.info({
16524
+ mode: preloadMode,
16448
16525
  platforms: hints.platforms,
16449
16526
  categories: hints.categories,
16450
16527
  preloaded: discoveredTools.length,
@@ -16595,11 +16672,15 @@ async function createAgentManager(config, feeds, db, sessionManager, sendMessage
16595
16672
  if (block.name === 'tool_search' && TOOL_SEARCH_ENABLED) {
16596
16673
  const { platform, category, query } = finalParams;
16597
16674
  let searchResults;
16598
- if (platform) {
16599
- searchResults = toolRegistry.searchByPlatform(platform);
16600
- }
16601
- else if (category) {
16602
- searchResults = toolRegistry.searchByCategory(category);
16675
+ // Uses intersection when both platform and category provided.
16676
+ // When platform/category AND query are both given, use structured search
16677
+ // (query is just a hint the LLM adds — platform/category are authoritative).
16678
+ if (platform || category) {
16679
+ searchResults = toolRegistry.search({ platform, category });
16680
+ // If structured search found nothing and a text query was also given, try text search
16681
+ if (searchResults.length === 0 && query) {
16682
+ searchResults = toolRegistry.searchByText(query);
16683
+ }
16603
16684
  }
16604
16685
  else if (query) {
16605
16686
  searchResults = toolRegistry.searchByText(query);