Hijacking Claude Code's Subagents: Routing UltraCode Fan-Out to DeepSeek Without Rewriting Your Workflows
Hook
Every time Claude Code spawns a subagent for parallel analysis, you're paying premium rates for what could be commodity inference. This Python gateway intercepts those calls before Claude even sees them, routing fan-out lanes to DeepSeek while your Workflow scripts run unchanged.
Context
Claude Code's Workflow/UltraCode system is powerful: you write orchestration logic in a DSL, call agent() to spawn subagents for parallel work, and Claude manages the fan-out. But there's a cost problem. When you fan out 50 lanes to analyze a codebase—each lane reading files, summarizing changes, running audits—you're invoking Claude's API 50 times at $3 per million input tokens. For a 10-file review where each lane processes 20k tokens, that's $30 in subagent costs alone. Do this daily and you're burning $600+/month on what are essentially parallel map operations.
The obvious solution is to route subagent work to cheaper models like DeepSeek v4 ($0.14/M tokens, 20x cheaper). But Claude Code's agent() syntax is tightly coupled to Anthropic's infrastructure—you can't just swap the backend in a config file. You'd need to rewrite your Workflows to call external APIs, manage fan-out yourself, and lose Claude Code's IDE integration. Teams were stuck choosing between Claude's quality and DeepSeek's economics. Ultimate-deepseek-ultracode solves this with surgical interception: it runs a local gateway that Claude thinks is an MCP tool server, hooks into Claude's pre-execution phase to rewrite agent() calls into gateway dispatches, and routes those lanes to ephemeral DeepSeek processes. Your Workflow scripts stay identical, but fan-out happens on DeepSeek.
Technical Insight
The architecture has four layers that work together to transparently hijack subagent dispatch. First, a Python launcher manages the charade—it spawns Claude Code with a modified MCP configuration pointing to the gateway's Unix socket, ensuring Claude discovers the reasonix_fleet tool at startup. Second, the gateway itself is a FastMCP server implementing Anthropic's API format, sitting at the boundary between Claude's orchestration and actual inference. Third, and most clever, is the PreToolUse hook: before Claude executes any tool, this hook pattern-matches the request for Workflow's agent() syntax and surgically rewrites it as an MCP call to reasonix_fleet. Finally, a forked reasonix engine bundled as a prebuilt Node.js library runs each lane as a one-shot process with session:undefined, preventing cache pollution between concurrent lanes.
The PreToolUse rewriting is where the magic happens. Here's the conceptual flow:
# Before: Claude's Workflow DSL (what you write)
agent(
role="file_analyzer",
task="Summarize security issues in auth.py",
context={"file": "src/auth.py"}
)
# After: What Claude actually executes (rewritten by hook)
mcp.call_tool(
server="reasonix_fleet",
tool="dispatch_lane",
arguments={
"bucket": "reader", # routing hint
"prompt": "Summarize security issues in auth.py",
"context": {"file": "src/auth.py"},
"discipline": "READ_SUMMARY" # cap output tokens
}
)
Claude never sees this rewrite—it happens in the hook before tool execution. From Claude's perspective, it called agent() and got a result. But the gateway intercepted the call, routed it through its bucketing logic (reader vs executor lanes based on verb analysis), and dispatched to DeepSeek.
The ephemeral session model solves a critical caching problem. Early attempts used persistent reasonix sessions across lanes, but this thrashed DeepSeek's KV cache: lane 1 loads "analyze auth.py," lane 2 loads "analyze db.py," and they pollute each other's cache because the session shares history. Cache hit rates tanked to 60%. The fix: spawn a fresh Node process per lane, import the bundled engine in-process, run exactly one inference with session:undefined (ephemeral mode), then terminate. Now each lane is isolated, and lanes that share prompt prefixes ("You are a code analyst...") hit DeepSeek's server-side prefix cache at 99%+ rates because they never contaminate each other's context.
The gateway implements request-level routing heuristics that encode operational wisdom. The READER_BROADEN policy scans prompts for verbs like "analyze," "review," "audit" and routes them to the reader bucket with output discipline—responses are capped at READ_SUMMARY tokens to prevent verbose DeepSeek outputs from blowing Claude's context budget. The OVERSCOPE_REJECT policy blocks prompts asking to "read all files" or "scan entire codebase," which would waste tokens. Here's a simplified routing decision:
# Gateway routing logic (conceptual)
def route_lane(prompt, context):
if any(verb in prompt.lower() for verb in ["analyze", "review", "summarize"]):
bucket = "reader"
discipline = "READ_SUMMARY" # cap at 500 tokens
elif any(verb in prompt.lower() for verb in ["modify", "implement", "fix"]):
bucket = "executor"
discipline = None # no cap, needs full code
else:
bucket = "default"
discipline = None
if "all files" in prompt or "entire codebase" in prompt:
return {"error": "OVERSCOPE_REJECT: narrow your query"}
return dispatch_to_deepseek(
prompt=prompt,
bucket=bucket,
discipline=discipline,
session=None # ephemeral
)
This isn't just routing—it's behavioral guardrails. The system knows how LLM agents misuse subagents (verbose outputs, overbroad queries) and enforces constraints at the gateway level.
The one-shot Node shim trades latency for isolation. Spawning a Node process per lane adds 200-500ms startup overhead, but it guarantees cleanup and prevents memory leaks during 100+ lane fan-outs. The bundled fork (vendor/reasonix-engine/) is committed as a prebuilt artifact to lock in the ephemeral session behavior—if you npm install reasonix, upstream changes might revert that fork, breaking the cache model. This is deliberate vendor-locking: the system needs specific engine behavior and doesn't trust upstream stability.
Cost tracking is first-class. The gateway logs per-lane token usage and cost, making economics visible:
Lane reader_01: 18.2k input, 340 output | $0.003 DeepSeek vs $0.061 Claude (20x savings)
Lane executor_04: 12.8k input, 890 output | $0.002 DeepSeek vs $0.041 Claude (20x savings)
Total fan-out (16 lanes): $0.047 DeepSeek vs $0.912 Claude
This visibility lets you reason about whether the operational complexity is worth the savings. For teams running 50+ fan-outs daily, saving $40+/day ($1200/month) justifies the maintenance burden.
Gotcha
The bundled fork is a maintenance nightmare. The vendor/reasonix-engine/ directory contains a prebuilt Node.js library with custom patches for ephemeral sessions, but there's no clear diff showing what changed from upstream reasonix. When reasonix releases improvements (faster inference, better caching, bug fixes), you can't just npm update—you need to manually merge into your fork, rebuild the bundle, and commit it. This creates a dead-end dependency path where you're stuck maintaining a bespoke engine build.
The PreToolUse hook coupling is brittle. It pattern-matches Claude's Workflow DSL syntax, which Anthropic doesn't document as a stable API. If Claude Code changes how agent() calls are structured—maybe they add new parameters, rename the primitive, or introduce agent_v2()—the hook breaks and subagents stop routing to DeepSeek. You'd be debugging why fan-out suddenly got expensive again, tracing through hook logic to find the pattern match failure. There's no fallback: if the rewrite fails, the call just doesn't happen, and Claude sees a tool error. For production workflows, this is a silent failure mode waiting to happen.
Process-per-lane architecture doesn't scale to massive fan-outs. Spawning 100 Node processes concurrently (for a 100-lane fan-out) means 100 cold-boots of the engine bundle, each taking 200-500ms. That's 20-50 seconds of pure overhead before any inference happens. A persistent worker pool running 16 long-lived processes would be faster, but you'd lose the cache isolation that makes ephemeral sessions valuable. There's a fundamental trade-off here: isolation or throughput, and the system chose isolation. If you need 500+ concurrent lanes, this architecture breaks down.
Verdict
Use if: You're already running Claude Code's Workflow/UltraCode features heavily (10+ fan-outs per day), hitting cost walls on subagent operations, and willing to accept operational brittleness for 10-20x cost savings on parallel lanes. This is purpose-built for teams who need Claude's orchestration quality but can't afford Claude rates for every subagent call. The ephemeral session architecture and cache optimization show this was built by someone who debugged production fan-out bottlenecks. Skip if: You're not using Claude Code's Workflow DSL (the hook interception is useless without agent() calls), you need auditable agent execution (the PreToolUse rewrites are invisible to Claude's logs), you want a general-purpose LLM gateway (this is hyper-specialized for one use case), or you can't tolerate maintenance debt from bundled forks and undocumented hooks. This is a clever hack that solves a real problem, but it's duct tape over architectural mismatches. For production use, I'd want upstream reasonix to support ephemeral sessions natively and Claude Code to support configurable subagent backends before betting on this long-term.