Skip to content

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)

This creates drift (example class: web_search query min length and normalization behavior).

Standardization target

  1. Alias mapping lives in one module and is imported everywhere:

    • normalizeToolId(name: string): ToolId | null
    • canonicalizeToolCallName(name: string): ToolId | null
  2. 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

  1. Introduce a small new module (or promote an existing one) for alias mapping:
    • e.g. backend/src/services/tools/toolAliases.ts
  2. Update:
    • toolCompilation.service.ts to import and use the shared alias normalizer
    • llmToolGateway.service.ts to use the shared alias normalizer (and remove heuristic “includes” mapping unless explicitly required and tested)
  3. 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.ts
  • backend/src/services/tools/toolCompilation.service.ts
  • backend/src/services/llmToolGateway.service.ts
  • backend/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.ts
  • backend/src/controllers/promptDebug.controller.ts
  • backend/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:

  1. Orchestration-owned guardrails (preferred if you want deterministic prefetch)
  2. 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.

ASO Universal Consciousness System Documentation