Steerable¶
The model-quality layer that makes local, quantized, and cheap models behave.
Recovers and executes malformed tool calls · vetoes bad completion drafts · catches fabricated data · self-calibrates token estimates. Plus the plumbing you'd otherwise rewrite: typed wire protocol · pluggable LLM runtime · embeddable Python sidecar · headless React chat UI. Pick any subset, skip the rest — every layer ships on its own.
80.7% Steerable + GLM-5.3-Flash
+7.3 vs Pi on the same Flash model
$0.146 per solved task
Why Steerable
Every agent SDK assumes the model emits clean, structured tool_calls. Local, quantized, and cheap models don't.
Steerable is the model-quality layer that closes that gap — plus the plumbing layers you'd otherwise rewrite, each shippable on its own.
The model-quality layer¶
Local, quantized, and cheap models break the structured-tool_calls assumptions every SDK makes. Steerable recovers and executes malformed calls (MiniMax XML, DeepSeek <function=>, markdown), vetoes completion drafts (accept / retry / narrate), judges grounding, and self-calibrates token estimates. Why this is the differentiator.
One wire protocol¶
One JSON Schema → generated TypeScript types + Pydantic models. content, tool_call, tool_result, error, done, budget_exhausted — all standardised, with a conformance suite keeping both SDKs byte-compatible. All 8 published packages share one lockstep X.Y.Z; npm tarballs ship sigstore provenance attestations.
Pure-function harness¶
Policy, budget, retry, completion, tracing, safety patterns. Zero I/O coupling — drop into FastAPI, Celery, or a notebook. Unit and golden tests in CI.
Pluggable runtime¶
One LLMProvider interface across four wire protocols — OpenAI-compatible chat/completions (Ollama, vLLM, DeepSeek, Groq, …), OpenAI Responses, Anthropic-native, Gemini-native — plus a live gateway model catalog, per-vendor sampling presets, @tool decorator, ToolRouter, SSE-over-HTTP and stdio JSON-RPC transports.
Embeddable sidecar¶
A portable, signed CPython binary speaking JSON-RPC over stdio (34 methods). Ship local LLMs inside Electron / Tauri / Wails — macOS notarised, Windows code-signed — with an OS sandbox, per-host egress proxy, and plugin lifecycle built in.
Headless React UI¶
7 components + 14 rich cards + 3 hooks + Tailwind preset. Every state covered by Storybook, axe a11y, and visual-regression baselines locked in CI.
Terminal-Bench 2.1
The score of record is still Steerable + GLM-5.3-Flash 80.7% at reasoning_effort=max (six-run mean, SD 2.9). The figures below are a separate n=1 protocol: GLM-5.3-Flash and DeepSeek-V4-Flash 0731 at high, Qwen3.8-27B at medium, five harnesses, one OpenRouter account, Harbor catalog-89. Timeout, error, and missing trials count as fail. Terminus is omitted. DSH is on both figures (SHA c576a88); its dollars are OpenRouter analytics tokens for the catalog window, scored with the same pinned-host list formula as the twelve Harbor cells. Protocol and run list: Evals.
Grouped bars are pass/89. Color is the harness. This is n=1 at high/medium, not the 80.7% @max six-run.
Mean = pass/89. GLM/DS @high pin z-ai / alibaba; Qwen @medium pin alibaba (Pi maps medium to --thinking high). DeepSeek is 0731 GA, not the 0423 preview. Steerable/Pi/CC/Codex SHA 6f70bf5 + fill 19213d7. DSH SHA c576a88 (Qwen missing winning-avg-corewars counts as fail).
Color is the harness. Shape is the model: circle GLM, square DeepSeek, diamond Qwen. Larger marker is Steerable. Dollars are OpenRouter list on the pinned host, not the published GLM @max $0.146 axis. Green DSH markers use OpenRouter analytics tokens for the catalog window (Harbor DSH trials still write no tokens).
List dollars from per-trial Harbor result.json tokens on the pinned host (GLM Z.AI $0.15/$0.50/cache $0.03 per 1M; DeepSeek Alibaba $0.352/$1.056; Qwen Alibaba $0.425/$2.55). Cache billed at cache-read when n_cache_tokens ≤ n_input_tokens (DeepSeek/Qwen cache-read uses the input rate, matching the other twelve cells). Codex GLM/Qwen and Pi Qwen include GitHub 360-minute unfinished snapshots scored as fail. DSH tokens are OpenRouter analytics for 2026-09-14 13:14–20:00 UTC (catalog window; cheap-12 that morning excluded); Harbor DSH trials still write no tokens. Not mixed with the published GLM @max $0.146.
Same GLM-5.3-Flash, same Harbor catalog-89 protocol: Claude Code 83.1% at $0.162 per solved task, Steerable 80.7% at $0.146, Pi 73.4% at $0.061. About $10.50 per full 89-task run — usable as a coding agent, not a demo loop.
Quickstart — pick your path
from steerable_agent_runtime import ToolRouter, tool
from steerable_agent_protocol import ToolCall
router = ToolRouter()
@tool(router=router, description="Read a file by path")
async def read_file(path: str) -> dict:
return {"path": path, "content": open(path).read()}
result = await router.dispatch(
ToolCall(id="c1", name="read_file", arguments={"path": "README.md"})
)
# result.success, result.data, result.error — all typed.
# Bundle the sidecar binary into resources/python-runtime/<platform>/
# TS hosts: link:../steerable-framework/packages/agent-runtime/ts
import { AgentRuntime } from '@steerable/agent-runtime';
const runtime = new AgentRuntime({ sidecarPath });
await runtime.start(); // spawn + lifecycle.ready handshake
const stream = await runtime.chatStream({
provider: 'openai_compat',
model: 'glm-5.3-flash',
baseUrl: process.env.GATEWAY_BASE_URL!,
apiKey: process.env.GATEWAY_API_KEY!,
messages: [{ role: 'user', content: 'hi' }],
});
for await (const event of stream.events) { /* typed SSEEvent stream */ }
Architecture
Five tiers, strict no-upward-imports rule. Tier N never imports Tier N+1 — adopting any layer means inheriting only the layers below it.
graph BT
T5["<b>Tier 5 · Host Shell</b> (TypeScript, private)<br/>@steerable/agent-shell · agent-shell-web · pack-sdk<br/>Electron main + headless HTTP server · local backend ·<br/>sidecar supervision · product-neutral renderer SPA"]
T4["<b>Tier 4 · UI</b> (TypeScript / React)<br/>@steerable/agent-ui<br/>Hooks: useChatStream · useToolCallStatus · useAgentSession<br/>Components: ChatPanel · MessageList · AgentSelector · ModelSelector ·<br/>OrchestrationPlanCard · ToolCallRenderer · SSEStreamView + 14 cards<br/>Tailwind preset (dark-mode aware)"]
T3S["<b>Tier 3 · Sidecar</b> (portable CPython binary)<br/>steerable-sidecar<br/>JSON-RPC over stdio · 34 methods · graceful shutdown<br/>agent.chat.stream · tool.invoke · agent.session.* · plugin.* · models.list"]
T3R["<b>Tier 3 · Runtime</b> (Python only)<br/>steerable-agent-runtime<br/>CoreLoop · LLMProvider (OpenAI-compat / Responses / Anthropic / Gemini)<br/>ToolRouter · StorageAdapter · TransportAdapter (FastAPI SSE)"]
T2["<b>Tier 2 · Harness</b> (Python — single source of truth)<br/>steerable-agent-harness<br/>Policy · Budget · Retry · Completion · Tracing · Safety<br/><i>thin TS facade @steerable/agent-harness exists for parity tests</i>"]
T1["<b>Tier 1 · Protocol</b> (TypeScript + Python, lock-step versions)<br/>@steerable/agent-protocol · steerable-agent-protocol<br/>SSEEvent · ChatMessage · ToolCall · ToolResult · AgentSession<br/>HarnessTrace · TraceSpan · SidecarRequest/Response/Notification"]
T5 -->|"spawns + supervises"| T3S
T5 -.->|"renders with"| T4
T4 -.->|"shared wire types"| T1
T4 -->|"spawns + JSON-RPC stdio (Electron)"| T3S
T3S -->|"embeds"| T2
T3S -->|"validates against"| T1
T3R -->|"embeds"| T2
T3R -->|"validates against"| T1
T2 -->|"imports types"| T1
classDef tier fill:#f4f4f5,stroke:#3f3f46,color:#18181b,rx:6,ry:6,padding:12;
class T1,T2,T3R,T3S,T4,T5 tier;
What's in the box
| Package | Tier | What you get |
|---|---|---|
@steerable/agent-protocol · steerable-agent-protocol |
1 | SSEEvent envelope, ToolCall / ToolResult, ChatMessage, sidecar JSON-RPC types — codegen from spec/, drift-checked in CI |
@steerable/agent-harness · steerable-agent-harness |
2 | decide_tool_mode, consume_budget, next_retry_delay_ms, is_terminal_result, command-safety patterns |
steerable-agent-runtime · @steerable/agent-runtime (TS, source-only) |
3 | CoreLoop + LLMProvider adapters (OpenAI-compat / Responses / Anthropic / Gemini), ToolRouter + @tool, storage & transport adapters (FastAPI SSE, stdio JSON-RPC); the TS package owns the sidecar process lifecycle for pure-TypeScript hosts |
steerable-sidecar · steerable-egress-proxy |
3 | Portable CPython binary — boots in <1s, macOS notarised, Windows signed — plus the bundled per-host CONNECT allow-list egress proxy |
@steerable/agent-ui |
4 | ChatPanel, MessageList, AgentSelector, ModelSelector, OrchestrationPlanCard, ToolCallRenderer, SSEStreamView + 14-card /cards subpath + hooks + Tailwind preset |
@steerable/agent-shell · agent-shell-web · pack-sdk |
5 | Electron + headless host shell, local backend, sidecar supervision, product-neutral renderer — published to npm (dist / source / pure-types respectively) |
How it compares
Steerable isn't the only way to ship an agent. The short, honest version against the frameworks you're probably also evaluating:
| Dimension | Steerable | OpenAI Codex | DeepSeek Harness | LangGraph | OpenAI Agents SDK | Claude Code / Agent SDK |
|---|---|---|---|---|---|---|
| Form factor | Layered library — the same loop in a desktop sidecar and a server | Product family: CLI, IDE, desktop, cloud — Rust core | Plugin-based harness (TS) on Cordis | Orchestration library — you write the graph | Lightweight framework over the Responses API | One closed-source bundle behind both the CLI and the Agent SDK |
| Loop / events | CoreLoop + structured LoopEvent taxonomy; mid-turn agent.chat.steer |
Session-owned turn loop; interrupt/abort; 12-event hooks | Inbox-driven ReactLoop; steer / inject / followup | Interrupts at node boundaries | Handoffs + guardrails; no mid-run steer | Mid-turn fold queue; hooks can veto continuation |
| Sandbox | Seatbelt + bwrap/Landlock, fail-closed; command classifier; per-host egress proxy | Approvals + ExecPolicy + Seatbelt/Landlock + Guardian | sandbox.confine, fail-closed |
None built-in | Guardrails; no sandbox | Permission modes; Seatbelt/bwrap opt-in, fail-open |
| Model quality | Recovers malformed tool calls; completion veto; grounding; token calibration | Assumes structured tool_calls |
Assumes structured tool_calls |
Substrate — you own the loop | Built for frontier models | Built for frontier models |
| Maturity | 0.6.x; one production consumer |
Massive real-world usage | Pre-release (0.1.x); internal use |
Widely adopted in production | Production, OpenAI-tied | Production; Anthropic models via first-party, Bedrock, Vertex, Foundry, Gateway |
Where Steerable genuinely differs, where it lags, and how to choose: Full comparison.
Who's using it
DeepPath — web (agent-protocol + agent-ui), API (all three Python packages), Electron desktop (sidecar + UI).
The framework was extracted from this codebase and is dogfooded back into it on every release.