Skip to content

refactor: separate tool discovery from request preparation - #339

Merged
PsiACE merged 8 commits into
mainfrom
refactor/unified-tool-catalogs
Oct 7, 2026
Merged

PsiACE merged 8 commits into
mainfrom
refactor/unified-tool-catalogs

Conversation

@PsiACE

@PsiACE PsiACE commented Oct 1, 2026 •

Copy link
Copy Markdown
Member

Keep the existing ToolProvider abstraction and call signatures. Register discovery inventories separately, resolve allowlists against known names, run caller providers in supplied order, and prepare builtin code mode last. Source registration determines duplicate-name precedence independently of provider order.

Register selected original declarations for execution while using provider-transformed definitions for each request. Builtins stay immediate; commands and subagents can resolve unloaded tools.

Relative to main, production changes are limited to builtin/agent.py and the subagent resolver (26 additions, 6 deletions). ToolProvider, code-mode implementation, skills and system prompt remain unchanged. Independent prompt/cache experiments are excluded.

Validation: 572 core tests, 701 contrib tests against this core, Ruff, mypy, lock validation and EN/ZH docs build passed. Behavioral ablations cover discovery, execution registration, final builtin preparation and transformed provider definitions.

Companion: bubbuild/bub-contrib#74.

@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Oct 1, 2026 •

Copy link
Copy Markdown

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

Status Name Latest Commit Preview URL Updated (UTC)
✅ Deployment successful!
View logs
bub 52fb7d0 Commit Preview URL

Branch Preview URL
Oct 07 2026, 08:24 AM

@PsiACE
PsiACE marked this pull request as ready for review October 1, 2026 18:23
@frostming

Copy link
Copy Markdown
Collaborator

I suggest keeping the original ToolProvider abstraction for request-time tool preparation:

ToolProvider = Callable[[list[Tool], Tape], Awaitable[tuple[list[Tool], str]]]

ToolCatalog currently combines two responsibilities: declaring tools for discovery/allowlist resolution, and transforming the scoped tools for a model request. DirectToolCatalog only supplies definitions and has a no-op prepare(), while CodeModeCatalog only transforms the toolset and declares an empty tools mapping. These implementations suggest that the two responsibilities do not need one shared abstraction.

Knowing tool names before allowed_tools filtering is useful, especially for MCP and deferred exposure. Please keep that capability through an explicit tool-registration/discovery mechanism, while using ToolProvider for selection, deferred exposure, and prompt fragments. Providers should continue to respect the filtered scope.

For preparation order, run caller-supplied providers in their supplied order, then the builtin preparation (including code mode) last. Keep duplicate-name precedence separate from preparation order. This preserves the simpler extension API and lets builtin preparation operate on the final toolset without introducing catalogs for every processing stage.

@PsiACE PsiACE changed the title refactor: unify tool discovery and preparation in catalogs refactor: separate tool discovery from request preparation Oct 4, 2026
Comment thread src/bub/builtin/agent.py Outdated
Comment on lines +261 to +263
known_tools = self.known_tools
if name in known_tools:
self.tools[name] = known_tools[name]

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.

I think it's better to allow _run_command to use all known_tools, instead of populating self.tools list.

Record the model aliases of the tools left after providers in the turn
state and let the unknown-tool interceptor check them, instead of
registering selected tools on the shared Agent.tools. Commands resolve
through known_tools without mutating Agent.tools either.

This keeps Agent.tools stable across sessions: tools loaded in one
session no longer pass the interceptor in another, and code mode no
longer registers every discovered tool permanently.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@PsiACE
PsiACE merged commit 2b23812 into main Oct 7, 2026
6 checks passed
frostming added a commit that referenced this pull request Oct 7, 2026
Replace the tool_sources/known_tools/tool_providers mechanism from #339 with
a deferred flag on Tool. Deferred tools are listed by name in the system
prompt and loaded through tool.describe, which records a tool.loaded tape
event; each request appends loaded tools after the direct tools. Code mode
sees every allowed tool, deferred ones included.

Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
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