clementine-agent 1.18.31 → 1.18.33

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.
@@ -2436,23 +2436,71 @@ You have a cost budget per message — not a hard turn limit. Work until the tas
2436
2436
  whitelist.add(mcpTool('goal_work'));
2437
2437
  allowedTools = allowedTools.filter(t => whitelist.has(t));
2438
2438
  }
2439
- if (!toolRoute.fullSurface
2440
- && !adminNeeded
2441
- && !autonomousToolRun
2442
- && allowedTools.length > TOOL_SURFACE_HARD_LIMIT) {
2439
+ // Tool-surface cap. Applies to chat AND to autonomous runs (cron,
2440
+ // unleashed, heartbeat). Without this cap on cron, a single job got
2441
+ // 300+ MCP tool schemas in the system prompt — leaving Sonnet's SDK
2442
+ // autocompact no room to actually compact when tool responses came
2443
+ // back. That manifested as `rapid_refill_breaker` ("context refilled
2444
+ // to the limit within 3 turns"). The SDK's autocompact still works;
2445
+ // we just have to give it room.
2446
+ if (!adminNeeded && allowedTools.length > TOOL_SURFACE_HARD_LIMIT) {
2443
2447
  const beforeAllowedToolCount = allowedTools.length;
2444
2448
  const coreSdkTools = new Set(['Read', 'Write', 'Edit', 'Bash', 'Glob', 'Grep', 'WebSearch', 'WebFetch']);
2445
2449
  const clementineToolPrefixForCap = `mcp__${TOOLS_SERVER}__`;
2446
- allowedTools = allowedTools.filter(tool => coreSdkTools.has(tool) || tool.startsWith(clementineToolPrefixForCap));
2447
- externalMcpServers = {};
2448
- composioMcpServers = {};
2449
- logger.warn({
2450
- sessionKey,
2451
- beforeAllowedToolCount,
2452
- afterAllowedToolCount: allowedTools.length,
2453
- hardLimit: TOOL_SURFACE_HARD_LIMIT,
2454
- bundles: toolRoute.bundles,
2455
- }, 'SDK allowed tool surface exceeded hard limit; falling back to core Clementine tools for this interactive turn');
2450
+ // Smart fallback: if the route matched specific bundles, keep
2451
+ // those bundles' explicit servers/toolkits and drop everything
2452
+ // else (including the fullSurface=true expansion to "all
2453
+ // connected MCP servers"). Only fall all the way down to
2454
+ // core+Clementine tools when there are no matched bundles to
2455
+ // restrict to.
2456
+ const matchedExternal = Array.isArray(toolRoute.externalMcpServers)
2457
+ ? new Set(toolRoute.externalMcpServers)
2458
+ : null;
2459
+ const matchedComposio = Array.isArray(toolRoute.composioToolkits)
2460
+ ? new Set(toolRoute.composioToolkits)
2461
+ : null;
2462
+ const hasMatchedBundles = !!matchedExternal && !!matchedComposio
2463
+ && (matchedExternal.size > 0 || matchedComposio.size > 0);
2464
+ if (hasMatchedBundles) {
2465
+ const keepServers = new Set([
2466
+ TOOLS_SERVER,
2467
+ ...(matchedExternal ?? []),
2468
+ ...(matchedComposio ?? []),
2469
+ ]);
2470
+ allowedTools = allowedTools.filter(tool => {
2471
+ if (coreSdkTools.has(tool))
2472
+ return true;
2473
+ if (!tool.startsWith('mcp__'))
2474
+ return true;
2475
+ const serverName = tool.slice('mcp__'.length).split('__')[0];
2476
+ return keepServers.has(serverName);
2477
+ });
2478
+ externalMcpServers = Object.fromEntries(Object.entries(externalMcpServers).filter(([name]) => matchedExternal.has(name)));
2479
+ composioMcpServers = Object.fromEntries(Object.entries(composioMcpServers).filter(([name]) => matchedComposio.has(name)));
2480
+ logger.warn({
2481
+ sessionKey,
2482
+ beforeAllowedToolCount,
2483
+ afterAllowedToolCount: allowedTools.length,
2484
+ hardLimit: TOOL_SURFACE_HARD_LIMIT,
2485
+ bundles: toolRoute.bundles,
2486
+ keptExternal: [...(matchedExternal ?? [])],
2487
+ keptComposio: [...(matchedComposio ?? [])],
2488
+ autonomous: autonomousToolRun,
2489
+ }, 'Tool surface exceeded hard limit; trimmed to matched bundles');
2490
+ }
2491
+ else {
2492
+ allowedTools = allowedTools.filter(tool => coreSdkTools.has(tool) || tool.startsWith(clementineToolPrefixForCap));
2493
+ externalMcpServers = {};
2494
+ composioMcpServers = {};
2495
+ logger.warn({
2496
+ sessionKey,
2497
+ beforeAllowedToolCount,
2498
+ afterAllowedToolCount: allowedTools.length,
2499
+ hardLimit: TOOL_SURFACE_HARD_LIMIT,
2500
+ bundles: toolRoute.bundles,
2501
+ autonomous: autonomousToolRun,
2502
+ }, 'Tool surface exceeded hard limit with no matched bundles; falling back to core Clementine tools');
2503
+ }
2456
2504
  }
2457
2505
  }
2458
2506
  // Permission mode: always 'bypassPermissions' — this is a daemon/harness with no interactive