Phase 2 PR Specs — Consolidation (Tool Platform Unification)
Last Updated: 2026-02-28
Content-Type: How-to (implementation-ready PR specs)
Audience: Engineering
Goal of Phase 2
Eliminate “multi-path” tool execution drift by making:
- one canonical tool schema registry
- one canonical alias/normalization mapping
- one canonical policy/enablement enforcement path
…and ensuring all remaining legacy code is either removed or becomes a thin adapter over the canonical platform.
PR2.1 — Single schema registry + single alias mapping
Why (problem)
There are multiple overlapping sources of truth:
- Alias mappings exist in both:
backend/src/services/tools/toolCompilation.service.ts(TOOL_ALIAS_TO_CANONICAL)backend/src/services/llmToolGateway.service.ts(normalizeToolName+ additional heuristic mapping)
- Schemas exist in multiple places:
- canonical tool schemas:
backend/src/services/tools/toolSchemas.ts - legacy gateway tool schemas:
backend/src/services/llmToolGateway.service.ts - executor per-tool schema wiring:
backend/src/services/toolExecutor.service.ts(imports many schemas directly)
- canonical tool schemas:
This creates drift (example class: web_search query min length and normalization behavior).
Standardization target
Alias mapping lives in one module and is imported everywhere:
normalizeToolId(name: string): ToolId | nullcanonicalizeToolCallName(name: string): ToolId | null
Schema registry lives in one module:
TOOL_SCHEMA_BY_ID: Record<ToolId, z.ZodTypeAny>(already exists)- tool executor uses it directly (instead of importing many schemas independently)
Implementation steps
- Introduce a small new module (or promote an existing one) for alias mapping:
- e.g.
backend/src/services/tools/toolAliases.ts
- e.g.
- Update:
toolCompilation.service.tsto import and use the shared alias normalizerllmToolGateway.service.tsto use the shared alias normalizer (and remove heuristic “includes” mapping unless explicitly required and tested)
- Update tool executor schema wiring to use the registry:
- executor should fetch schema by toolId and validate payload centrally (uniform error shaping)
Acceptance criteria
- One alias mapping table in the repo (no duplicate mappings).
- Tool schema is defined once per toolId.
- Changing a tool schema affects all runtimes uniformly.
File touch list (expected)
backend/src/services/tools/toolSchemas.tsbackend/src/services/tools/toolCompilation.service.tsbackend/src/services/llmToolGateway.service.tsbackend/src/services/toolExecutor.service.ts- Tests for alias normalization + schema validation parity
PR2.2 — Legacy tool gateway becomes a thin adapter (or is retired)
Why (problem)
Legacy gateway still executes tools and has special web-search gating logic. It is currently used by:
- prompt preview:
backend/src/controllers/promptDebug.controller.ts - unit tests:
backend/src/tests/llmToolGateway.test.ts
Decision: retain vs retire
Option A (recommended): Keep parsing; retire execution
- Keep
parseLlmToolOutput()for prompt debugging/back-compat parsing only. - Remove
executeLlmToolCall()special-case execution and instead:- convert parsed call → canonical toolId + canonical args
- execute via
executeTool()using canonical schemas/policies/enablement
Option B: Retire entirely
- If prompt debug no longer needs it, remove the gateway and tests.
Standardization rules
- Any “tool gating” (e.g. allow web search) must be applied by the canonical platform, not by legacy helper functions.
- Any legacy module may not define its own schemas/aliases.
Acceptance criteria
- Prompt debug uses canonical execution paths when previewing tool results (if it does execution).
- Legacy gateway cannot drift policy/schema behavior from LangChain runtime.
- Unit tests assert parity with canonical execution.
File touch list (expected)
backend/src/services/llmToolGateway.service.tsbackend/src/controllers/promptDebug.controller.tsbackend/src/tests/llmToolGateway.test.ts
PR2.3 — Single guardrails location (remove double-guardrails)
Why (problem)
Similar “guardrails” (web/time prefetch, fallback behavior, etc.) exist in multiple layers:
- orchestration layer (preflight tool calls)
- LangChain tool-turn (fallback tool calls / forced web search / time fallback)
This can cause:
- duplicated tool calls
- inconsistent traces
- inconsistent tool progress reporting
Decision: where guardrails live
Pick exactly one layer:
- Orchestration-owned guardrails (preferred if you want deterministic prefetch)
- Agent-owned guardrails (preferred if you want adaptive, model-driven decisions)
Implementation steps
- Identify the guardrails list (time, time-since-last-talk, web prefetch/search, etc.).
- Remove/disable duplicates in the non-owner layer.
- Ensure tracing and tool progress events originate from the owner layer only.
Acceptance criteria
- A single chat turn results in at most one “current time” or “web prefetch” event per turn (unless explicitly configured).
- Traces are consistent across streaming vs non-streaming.