Skip to content

Deferred (lazy) tool loading: tool_search + auto threshold for large MCP tool sets#148

Merged
gnanam1990 merged 3 commits into
mainfrom
deferred-tool-loading
Jun 9, 2026
Merged

Deferred (lazy) tool loading: tool_search + auto threshold for large MCP tool sets#148
gnanam1990 merged 3 commits into
mainfrom
deferred-tool-loading

Conversation

@gnanam1990

@gnanam1990 gnanam1990 commented Jun 9, 2026

Copy link
Copy Markdown
Collaborator

Summary

Adds deferred (lazy) tool loading so a large MCP tool set no longer sends every tool's full JSON schema on every turn. When the number of visible MCP tools crosses a threshold, their schemas are withheld, advertised compactly in a per-turn <system-reminder>, and the model pulls a tool's full schema on demand via a tool_search tool. Strictly additive — below the threshold (default 10) behaviour is byte-identical to today. No new module dependencies.

  • Eligibility (internal/tools, internal/mcp): MCP tools are deferred-eligible via an optional Deferred() bool interface (tools.IsDeferred); built-ins never implement it, so they stay eager. No change to the core Tool interface.
  • Formatting (internal/tools/deferred.go): compact one-liners — name: <short-desc> | server: <X> | inputs (* required): a (string)*, …; +N more — wrapped in a <system-reminder> block by BuildDeferredReminder. Server label uses the MCP server name directly (MCPServerName()), falling back to a name parse.
  • The tool (internal/tools/tool_search.go): tool_search (SideEffectNone/PermissionAllow/AdvertiseInAuto). Query select:Name1,Name2 (exact) or keywords (ranked); returns each loaded tool's full schema and signals the loop via Meta["load_tools"]. It is operator-filter-aware — it never resolves, ranks, or lists a tool hidden by --enabled-tools/--disabled-tools.
  • Agent loop (internal/agent): partitionTools is the single source of truth — eligible counts only ToolVisible deferred tools; when eligible >= DeferThreshold, unloaded deferred tools are withheld into the reminder while tool_search + built-ins + already-loaded tools stay exposed. Meta["load_tools"] is lifted to ToolResult.LoadedTools and unioned into a per-run loaded-set so the next turn advertises them. The reminder is appended only to the per-turn request copy (never persisted history), including on both reactive-compaction retry paths. tool_search is always reachable when deferral is active (exempt from the EnabledTools allowlist) so an allowlist of deferred tools can never strand the loader.
  • Config + wiring (internal/config, internal/cli): tools.deferThreshold (default 10; 0 disables; negative rejected; explicit 0 preserved vs unset). tool_search is registered — and DeferThreshold threaded — in exec and the TUI, gated on the same visible-deferred count the loop uses, so registration and activation never disagree.

Loaded-set scope is within a single agent.Run (v1, fully covers headless exec); TUI cross-turn persistence is a documented follow-up.

Test Plan

  • go build ./... / go vet ./... clean; gofmt clean (touched files)
  • go test ./... green (new tests across tools, mcp, agent, config, cli)
  • go test -race ./internal/{tools,mcp,agent,config,cli}/... green
  • GOOS=windows GOARCH=amd64 go build ./... green

Reviewer focus

  • Byte-identical below threshold: partitionTools inactive output is reflect.DeepEqual to the legacy toolDefinitions (only tool_search dropped, which isn't registered when inactive).
  • Gate agreement: registration and activation count the same ToolVisible deferred population — prompt-gated MCP tools that aren't advertised in auto mode don't spuriously register tool_search.
  • No dead-ends: an --enabled-tools allowlist of deferred tools keeps tool_search reachable (regression-tested); a deferred tool hidden by --disabled-tools is invisible to tool_search.
  • Reminder never persisted: asserted absent from history on the main path and both reactive-retry paths.

Note

Recreated on top of current main. PR #146 (model escalation) has since merged; its tools.SideEffectNone constant (for escalate_model) and this branch's SideEffectNone (for tool_search) are reconciled here into a single declaration. Supersedes #147.

Summary by CodeRabbit

  • New Features

    • Deferred tool loading: large tool sets are compacted into a per-turn reminder; individual schemas can be loaded on-demand via a searchable tool.
    • Added searchable tool (tool_search) to discover and request deferred tool schemas; respects operator allow/deny filters.
    • Configurable defer threshold exposed to config/CLI/TUI to control when deferral activates.
  • Behavior Changes

    • Reminders are transient per turn (not persisted into results); loaded tools become exposed on subsequent turns.
  • Tests

    • Extensive test coverage for partitioning, loading, retries, CLI wiring, config parsing, and formatting utilities.

@github-actions

github-actions Bot commented Jun 9, 2026

Copy link
Copy Markdown
Contributor

Zero automated PR review

Verdict: No blockers found

Blockers

  • None found.

Validation

  • [pass] Diff hygiene: git diff --check
  • [pass] Tests: go test ./...
  • [pass] Build: go run ./cmd/zero-release build
  • [pass] Smoke build: go run ./cmd/zero-release smoke

Scope

Head: 962a5ab3f06e
Changed files (20): internal/agent/deferred_loop_test.go, internal/agent/loop.go, internal/agent/types.go, internal/cli/app.go, internal/cli/deferred_wiring_test.go, internal/cli/exec.go, internal/cli/exec_tools.go, internal/config/resolver.go, internal/config/resolver_test.go, internal/config/types.go, internal/config/types_test.go, internal/mcp/registry.go, and 8 more

This deterministic review checks validation status and basic diff hygiene. A human reviewer still owns product judgment and design quality.

…MCP tool sets

Adds deferred tool loading so a large MCP tool set no longer sends every tool's full JSON schema each turn. When the number of visible MCP tools crosses a threshold (config tools.deferThreshold, default 10; 0 disables), their schemas are withheld and advertised compactly in a per-turn system-reminder; the model pulls a tool's full schema on demand via a new tool_search tool. Strictly additive: below the threshold behaviour is byte-identical to today. No new module dependencies.

- Eligibility: MCP tools opt in via an optional Deferred() bool interface (tools.IsDeferred); built-ins stay eager. No change to the core Tool interface.
- Formatting (internal/tools/deferred.go): compact one-liners wrapped in a <system-reminder> by BuildDeferredReminder; server label via MCPServerName().
- tool_search (internal/tools/tool_search.go): SideEffectNone/PermissionAllow/AdvertiseInAuto; select:Name1,Name2 (exact) or keyword (ranked); returns full schemas and signals the loop via Meta["load_tools"]. Operator-filter-aware (never lists tools hidden by --enabled-tools/--disabled-tools).
- Agent loop: partitionTools is the single source of truth; eligible counts only ToolVisible deferred tools; Meta["load_tools"] lifts to ToolResult.LoadedTools and unions into a per-run loaded-set advertised next turn. Reminder appended only to the per-turn request copy (never persisted), including both reactive-compaction retry paths. tool_search stays reachable when deferral is active (exempt from the EnabledTools allowlist).
- Config + wiring: tools.deferThreshold (default 10; 0 disables; negative rejected); tool_search registered and DeferThreshold threaded in exec and TUI, gated on the same visible-deferred count the loop uses.

Recreated on top of current main; resolves the SideEffectNone clash with #146 (escalate_model). Supersedes #147.
@gnanam1990
gnanam1990 force-pushed the deferred-tool-loading branch from e59a867 to d3560db Compare June 9, 2026 04:52
@coderabbitai

coderabbitai Bot commented Jun 9, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: aebf86e5-7533-4aa7-bfc8-76e9e75f57b2

📥 Commits

Reviewing files that changed from the base of the PR and between 26ab40b and 962a5ab.

📒 Files selected for processing (2)
  • internal/agent/loop.go
  • internal/cli/exec.go
💤 Files with no reviewable changes (1)
  • internal/agent/loop.go
🚧 Files skipped from review as they are similar to previous changes (1)
  • internal/cli/exec.go

Walkthrough

Adds configurable deferred MCP-tool loading with a DeferThreshold, a new tool_search discovery/loading tool, deterministic deferred reminder formatting, per-run loaded-tool tracking and per-turn partitioning with optional reminders, CLI gating to register tool_search per run, and broad tests for partitioning, loading, retries, and config semantics.

Changes

Deferred MCP tool loading

Layer / File(s) Summary
Configuration threshold and override semantics
internal/config/types.go, internal/config/resolver.go, internal/config/resolver_test.go, internal/config/types_test.go
Introduces ToolsConfig and ToolsOverride, defaulting to a non-zero default when unset, preserves 0 as an explicit disable, validates non-negative values, and wires merge/override propagation with tests.
Tool deferral and registry filtering contracts
internal/tools/registry.go, internal/tools/registry_test.go, internal/mcp/registry.go, internal/mcp/registry_test.go, internal/tools/types.go
Adds RunOptions.EnabledTools/DisabledTools, IsDeferred helper, marks MCP registry tools as deferred-eligible and server-name aware, and updates tests and SideEffect ordering docs.
Deferred reminder formatting
internal/tools/deferred.go, internal/tools/deferred_test.go
Implements DeferredLine and BuildDeferredReminder with description shortening, deterministic input-schema hints, server-name preference/fallback, truncation/word-boundary rules, and tests.
tool_search discovery and loading
internal/tools/tool_search.go, internal/tools/tool_search_test.go
Adds tool_search providing select: exact-loading and keyword ranking, applies operator allow/deny filters, returns matched tool schemas and Meta["load_tools"], and includes comprehensive tests for selection, ranking, and filtering.
CLI registration gate and exec/TUI wiring
internal/cli/exec.go, internal/cli/app.go, internal/cli/exec_tools.go, internal/cli/deferred_wiring_test.go
Computes per-run effective DeferThreshold, counts visible deferred-eligible tools considering permission mode and operator filters, conditionally registers tool_search when threshold met, permits tool_search in filter validation pre-registration, threads threshold into agent.Options, and adds CLI wiring tests.
Agent types, partitioning, and execution wiring
internal/agent/types.go, internal/agent/loop.go
Adds Options.DeferThreshold and ToolResult.LoadedTools, per-run loaded set, partitionTools to build per-turn exposed definitions + optional reminder, parses Meta["load_tools"] into ToolResult.LoadedTools, forwards operator filters into execute options, and replaces the old toolDefinitions path.
Loop request/retry behavior and executeToolCall
internal/agent/loop.go
Appends reminders only to per-turn request copies, rebuilds retry requests reusing computed exposed and reminder to preserve deferred visibility across proactive/reactive retries, merges loaded-tool names into run state early, and special-cases tool_search dispatch gating.
Agent deferred-loop behavior tests
internal/agent/deferred_loop_test.go
Extensive tests covering field presence, executeToolCall metadata lifting, inactive/active partition behavior (including byte-identical inactive path), hidden/exposed rules, disabled-tool exclusion, multi-turn tool loading via tool_search, retry preservation, and allowlist/denylist regressions.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

  • Gitlawb/zero#47: Overlapping work on the tools registry and core tool infrastructure used by the deferred-tool filtering.
  • Gitlawb/zero#60: Related changes to agent loop and tool visibility/allowlist gating.
  • Gitlawb/zero#146: Related precedent for lifting tool-emitted metadata into ToolResult (similar pattern for other metadata keys).

Suggested reviewers

  • Vasanthdev2004
  • anandh8x
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 38.95% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main change: deferred (lazy) tool loading with tool_search and automatic threshold management for large MCP tool sets. It is concise, specific, and clearly reflects the primary objectives of the changeset.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch deferred-tool-loading

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🧹 Nitpick comments (1)
internal/tools/tool_search.go (1)

98-102: ⚡ Quick win

Dead code: deferredTools appears unused.

The method deferredTools() is not called anywhere in this file, and static analysis flags it as unused. If it's not needed for external callers, consider removing it to reduce maintenance surface.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/tools/tool_search.go` around lines 98 - 102, The method
deferredTools on type toolSearchTool is dead code; either remove it or make it
intentionally used/exported—delete the deferredTools() function body and its
declaration if there are no external callers, or if it was intended for external
use, rename/export it and update callers to use tool.visibleDeferredTools(nil,
nil) (reference symbols: deferredTools, toolSearchTool, visibleDeferredTools) so
there is no unused function left in the file.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@internal/agent/deferred_loop_test.go`:
- Around line 700-735: Update
TestDisabledToolSearchStaysHiddenAndRejectedWhenActive to assert the
eager/inactive fallback when "tool_search" is explicitly disabled: call
partitionTools (used in this test) and assert there is no "tool_search" loader
definition, and that the deferred tools (fakeDeferredTool names
"mcp__srv__alpha" and "mcp__srv__beta") are present in the exposed set; then
change the executeToolCall expectations so we do NOT assert that a disabled
tool_search call remains hidden/filtered — remove the DenialFiltered check and
instead assert that calls operate under the eager/inactive behavior (no loader
definition present and deferred tools are accessible) by ensuring
executeToolCall returns with no unexpected abortErr and that access to the
actual deferred tools succeeds rather than being blocked.

In `@internal/agent/loop.go`:
- Around line 939-998: The deferral path is enabled solely by the deferred count
(active) even when the tool_search loader is missing/disabled/unrunnable; change
the active gate to also require that tool_search is actually registered and
usable before hiding tools or emitting the reminder. Concretely, when computing
active (and before using exposedNames/hiddenLines), call
registry.Get(tools.ToolSearchToolName) and ensure it exists and is not disabled
via containsToolName(options.DisabledTools, tools.ToolSearchToolName) (and if
you have a runtime check for usability/runnable on the returned loader, include
that too); only set active true if both the defer-threshold condition and the
loader availability/usability check pass so the code falls back to the
eager/inactive path when tool_search cannot run (this prevents telling the model
to call tool_search while executeToolCall will later reject it).

In `@internal/cli/exec.go`:
- Around line 227-234: Compute a single effective defer threshold and force it
to 0 when "tool_search" is present in options.disabledTools, then use that
effective value everywhere instead of the raw resolved.Tools.DeferThreshold;
specifically, create a local variable (e.g., effectiveDeferThreshold)
initialized to resolved.Tools.DeferThreshold, set it to 0 if
options.disabledTools contains "tool_search", pass effectiveDeferThreshold into
registerToolSearchIfEligible(registry, ..., permissionMode,
options.enabledTools, options.disabledTools) and assign/use it for
agent.Options.DeferThreshold (and anywhere else the raw DeferThreshold is passed
into agent.Run or related registration/partition gates) so the run and
registration gate share the same threshold and deferred loading is disabled when
tool_search is explicitly disabled.

---

Nitpick comments:
In `@internal/tools/tool_search.go`:
- Around line 98-102: The method deferredTools on type toolSearchTool is dead
code; either remove it or make it intentionally used/exported—delete the
deferredTools() function body and its declaration if there are no external
callers, or if it was intended for external use, rename/export it and update
callers to use tool.visibleDeferredTools(nil, nil) (reference symbols:
deferredTools, toolSearchTool, visibleDeferredTools) so there is no unused
function left in the file.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 44b73782-3110-4582-bc21-ae26ac9d3345

📥 Commits

Reviewing files that changed from the base of the PR and between b83367e and e59a867.

📒 Files selected for processing (20)
  • internal/agent/deferred_loop_test.go
  • internal/agent/loop.go
  • internal/agent/types.go
  • internal/cli/app.go
  • internal/cli/deferred_wiring_test.go
  • internal/cli/exec.go
  • internal/cli/exec_tools.go
  • internal/config/resolver.go
  • internal/config/resolver_test.go
  • internal/config/types.go
  • internal/config/types_test.go
  • internal/mcp/registry.go
  • internal/mcp/registry_test.go
  • internal/tools/deferred.go
  • internal/tools/deferred_test.go
  • internal/tools/registry.go
  • internal/tools/registry_test.go
  • internal/tools/tool_search.go
  • internal/tools/tool_search_test.go
  • internal/tools/types.go

Comment thread internal/agent/deferred_loop_test.go
Comment thread internal/agent/loop.go Outdated
Comment thread internal/cli/exec.go Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

♻️ Duplicate comments (1)
internal/agent/loop.go (1)

939-989: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Deactivate deferral unless tool_search is actually runnable.

Line 939 enables deferral purely by deferred-count. If tool_search is missing, explicitly disabled, or not runnable in current mode, Lines 963-998 can still hide deferred tools and emit reminders, while calls are rejected later (see Line 422), creating a dead-end.

Suggested fix
-	active := options.DeferThreshold > 0 && eligible >= options.DeferThreshold
+	loader, loaderFound := registry.Get(tools.ToolSearchToolName)
+	loaderUsable := loaderFound &&
+		!containsToolName(options.DisabledTools, tools.ToolSearchToolName) &&
+		ToolAdvertised(loader, permissionMode)
+	active := options.DeferThreshold > 0 && eligible >= options.DeferThreshold && loaderUsable
@@
-	if active && !exposedNames[tools.ToolSearchToolName] && !containsToolName(options.DisabledTools, tools.ToolSearchToolName) {
-		if loader, ok := registry.Get(tools.ToolSearchToolName); ok {
+	if active && !exposedNames[tools.ToolSearchToolName] {
+		if loaderUsable {
 			definitions = append(definitions, zeroruntime.ToolDefinition{
 				Name:        loader.Name(),
 				Description: loader.Description(),
 				Parameters:  schemaToRuntimeMap(loader.Parameters()),
 			})
 		}
 	}
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@internal/agent/loop.go`:
- Around line 906-913: Remove the unused shim function toolDefinitions: delete
the toolDefinitions function and update any callers to call
partitionTools(registry, permissionMode, options, map[string]bool{}) directly
(preserving the same semantics/exposed return) and clean up any now-unused
imports; reference the toolDefinitions symbol for removal and partitionTools for
the replacement call.

In `@internal/tools/tool_search.go`:
- Around line 98-102: Remove the unused helper deferredTools defined on type
toolSearchTool; delete the function named deferredTools() that simply calls
visibleDeferredTools(nil, nil) since golangci-lint flags it as dead code. Search
for the method signature "func (tool toolSearchTool) deferredTools() []Tool" and
remove that entire method; ensure no other code references deferredTools (if
there are callers, replace them to call visibleDeferredTools directly with the
appropriate nil,nil args). After removal, run linters/tests to confirm no
remaining references.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 2f2d091b-34a1-4146-b9b9-aabb79f53063

📥 Commits

Reviewing files that changed from the base of the PR and between e59a867 and d3560db.

📒 Files selected for processing (20)
  • internal/agent/deferred_loop_test.go
  • internal/agent/loop.go
  • internal/agent/types.go
  • internal/cli/app.go
  • internal/cli/deferred_wiring_test.go
  • internal/cli/exec.go
  • internal/cli/exec_tools.go
  • internal/config/resolver.go
  • internal/config/resolver_test.go
  • internal/config/types.go
  • internal/config/types_test.go
  • internal/mcp/registry.go
  • internal/mcp/registry_test.go
  • internal/tools/deferred.go
  • internal/tools/deferred_test.go
  • internal/tools/registry.go
  • internal/tools/registry_test.go
  • internal/tools/tool_search.go
  • internal/tools/tool_search_test.go
  • internal/tools/types.go
✅ Files skipped from review due to trivial changes (1)
  • internal/tools/types.go
🚧 Files skipped from review as they are similar to previous changes (15)
  • internal/mcp/registry.go
  • internal/cli/exec_tools.go
  • internal/agent/types.go
  • internal/cli/app.go
  • internal/cli/exec.go
  • internal/config/resolver.go
  • internal/mcp/registry_test.go
  • internal/config/types_test.go
  • internal/config/resolver_test.go
  • internal/tools/deferred.go
  • internal/config/types.go
  • internal/cli/deferred_wiring_test.go
  • internal/tools/tool_search_test.go
  • internal/tools/deferred_test.go
  • internal/agent/deferred_loop_test.go

Comment thread internal/agent/loop.go Outdated
Comment thread internal/tools/tool_search.go Outdated

@Vasanthdev2004 Vasanthdev2004 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Verdict: Request changes

Findings:

  • P1 internal/agent/loop.go:939: deferral activates from DeferThreshold + eligible count alone, without requiring a runnable tool_search. If tool_search is disabled or absent, partitionTools can hide deferred tools and emit a reminder, while executeToolCall later rejects the loader. That leaves the model with neither the deferred schemas nor a usable loader. Please gate active on registry.Get("tool_search"), not disabled, and advertised/runnable in the current permission mode; otherwise fall back to eager exposure.

  • P1 test coverage internal/agent/deferred_loop_test.go:701: TestDisabledToolSearchStaysHiddenAndRejectedWhenActive currently locks in the dead-end behavior above. It should assert eager/inactive fallback when tool_search is explicitly disabled: no loader definition, empty reminder, and the deferred tools exposed normally.

  • Cleanup internal/agent/loop.go:910, internal/tools/tool_search.go:100: toolDefinitions and toolSearchTool.deferredTools are unused shim/helper functions. Remove them or wire callers directly through partitionTools(...) / visibleDeferredTools(...) so linter/static analysis stays clean.

Validation I ran locally on the PR worktree:

  • git diff --check origin/main...HEAD
  • go test ./internal/agent ./internal/tools ./internal/cli ./internal/mcp

Both passed, but the behavioral blocker above still needs a fix before merge.

…drop dead deferredTools()

Addresses #148 review:
- partitionTools gates 'active' on a registered, non-disabled, mode-advertised
  tool_search (mirrors the executeToolCall dispatch gate). When the loader is
  unavailable (explicitly disabled, missing, or not advertised e.g. spec-draft),
  fall back to the eager/inactive path instead of hiding deferred tools behind a
  loader the model can never call (the dead-end).
- Flip TestDisabledToolSearch... to assert the eager fallback; register a real
  tool_search in the active-path tests that previously relied on activation
  without a usable loader.
- Remove unused toolSearchTool.deferredTools().
…rop unused toolDefinitions shim

Further #148 review:
- exec computes one effective DeferThreshold, forced to 0 when tool_search is in
  --disabled-tools, and feeds it to both registerToolSearchIfEligible and
  agent.Options.DeferThreshold — so the registration gate and the loop partition
  gate agree, and a disabled loader never enables deferral.
- Remove the unused toolDefinitions shim (golangci-lint unused; no callers).
@gnanam1990

Copy link
Copy Markdown
Collaborator Author

@coderabbitai review — all 5 review findings addressed across 26ab40b and 962a5ab (loop gating on a runnable tool_search, exec effective-threshold parity, test flipped to eager fallback, dead code removed). Full suite + -race + Windows build green locally.

@coderabbitai

coderabbitai Bot commented Jun 9, 2026

Copy link
Copy Markdown

@gnanam1990 Thanks for the detailed update — loop gating on a runnable tool_search, exec effective-threshold parity, eager-fallback test flip, and dead code removal all sound like the right fixes. Kicking off a fresh review now.

✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@Vasanthdev2004 Vasanthdev2004 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rereviewed the latest head 962a5ab.

The previous blockers are resolved:

  • tool_search deferral now only activates when the loader is registered, not disabled, and advertised/runnable for the current permission mode.
  • exec now uses a single effective defer threshold and forces it to 0 when tool_search is disabled.
  • The disabled-tool_search regression test now asserts eager fallback with deferred tools exposed normally.
  • The unused helper/shim cleanup is done.

Validation run locally on the PR worktree:

  • git diff --check b83367e9d3f0d3f19d4c5bf659ec73f60d0f510b...HEAD
  • go test ./internal/agent ./internal/tools ./internal/cli ./internal/mcp
  • go test ./...

All passed. Approving.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants