Hubzoid
Concepts

Agents and models

How Hubzoid defines the main agent and sub-agents, when a sub-agent becomes a delegate on its own model, and how runtimes and providers are chosen.

Every Hub has one main agent, defined by AGENTS.md, and any number of sub-agents in agents/. The model id decides which of three runtimes runs them: the OpenAI Agents SDK with LiteLLM, the Claude Agent SDK with your claude CLI, or local Codex with your codex CLI. The Hub files are the same on each.

The main agent

AGENTS.md at the Hub root is the main agent. Its body is the system prompt, and its frontmatter sets the name, description, model and chat suggestions. Every surface talks to the main agent. It holds the full tool registry and decides when to load a skill or call a delegate.

AGENTS.md
---
name: ops-desk
description: Answers operations questions and prepares the weekly review.
model: claude-local
suggestions:
  - What changed in stock this week
---

You help the operations team. Use the knowledge documents for thresholds,
and load the weekly-review skill when someone asks for the weekly review.

The project structure page lists every frontmatter field.

Sub-agents

A sub-agent is a Markdown file in agents/, either agents/<name>.md or agents/<name>/AGENTS.md when it has supporting files. It uses the same frontmatter as the main agent, plus tools.

agents/researcher.md
---
name: researcher
description: Writes a short, sourced brief when someone asks for research on a topic.
model: claude-local/opus
tools: [web_search, http_get, read_knowledge]
---

Research the topic you are given. Use at most five sources and cite each one.
Return a brief of no more than 300 words with the sources listed at the end.
FieldPurpose
nameIdentifier. Defaults to the file or folder name.
descriptionWhen to use this sub-agent. The main agent sees it in its skill or delegate menu.
modelOptional. Decides whether the sub-agent runs as a skill or as a delegate.
toolsOptional list of tool names a delegate may call. Ignored when the sub-agent runs as a skill.

Skill or delegate

Hubzoid classifies each sub-agent when it builds the agent. The rule depends only on the sub-agent's model: compared with the Hub's model.

Sub-agent model:Runs asBehavior
Not setSkillThe main agent loads its body with load_skill and follows it in the same turn.
Same model as the HubSkillAs above.
A different engine (for example a claude-local id on a LiteLLM or codex-local Hub)SkillAs above. A sub-agent never switches runtime.
A different model on the same engineDelegateRuns on its own model in its own context and returns a result to the main agent.

For example, on a Hub running claude-local (Sonnet), the researcher above has model: claude-local/opus, so it becomes a delegate that runs on Opus. On a Hub running openai/gpt-4o-mini, a sub-agent with model: openai/gpt-4o is a delegate, and one with model: claude-local/opus is a skill. On a Hub running codex-local, a sub-agent with model: codex-local/<model-id> is a delegate on that Codex model.

A skill-classified sub-agent is added to the skill menu. If a skill in skills/ has the same name, the one in skills/ wins and a warning is logged.

How delegation works

The main agent keeps control. It calls a delegate within the turn with a brief, the delegate works in isolation on its own model, and its final message comes back as the result. The runtime addendum lists each delegate with its model and tells the main agent to relay the answer faithfully.

OpenAI Agents runtimeClaude Agent runtimeCodex runtime
MechanismThe delegate is a tool named handover_<name>, built with the Agents SDK's agent-as-tool pattern.The delegate is a native Claude sub-agent on its tier (opus, sonnet, haiku or a full claude-* id), dispatched through Claude's sub-agent tool.The delegate is a tool named handover_<name> that runs a separate Codex request on the delegate's model.
ToolsThe tools: list, intersected with the Hub's tools. Unknown names are dropped with a warning. With no list, every Hub tool.Same rule.Same rule.
Restricted toolsGuarded exactly as for the main agent.Same.Same.
If its model cannot be builtMissing provider key or another build error: the sub-agent is loaded as a skill instead, and the Hub still starts.Not applicable. The tier is passed to the CLI.Not applicable. The model is passed to Codex.

Delegates are not exposed over MCP, because an MCP caller brings its own model. There, every sub-agent is a skill. A delegate never receives the person's own personal MCP connections. Only the main agent does.

Runtimes

OpenAI Agents SDKClaude Agent SDKLocal Codex
Chosen whenThe model id does not start with claude-local or codex-localThe model id is claude-local or claude-local/<pin>The model id is codex-local or codex-local/<model-id>
Model callsThrough LiteLLMThrough the claude CLI, started as a subprocessThrough the Codex CLI's app-server, pinned to Codex CLI 0.147.0. Each request gets its own short-lived thread and an isolated configuration folder.
AuthenticationThe provider key in .envclaude login on the machine, CLAUDE_CODE_OAUTH_TOKEN, or ANTHROPIC_API_KEYA file-backed codex login by the account that runs the Hub, read from $CODEX_HOME/auth.json (by default ~/.codex/auth.json). Keychain-only logins are not supported.
Hub toolsFunction tools on the agentAn in-process MCP server named hubzoid. Claude Code's own tools (Bash, Read, Edit and so on) are disabled, and Claude Code settings files are not read.Offered to Codex as dynamic tools that Hubzoid runs. Codex's own shell, file, browser and web search tools are disabled, and the login's personal Codex settings, MCP servers, skills and hooks are not used.
MCP connectorsConnected when the bridge starts. A server that fails to connect is dropped with a warning.Passed to the SDK, which manages the connections. Only the servers Hubzoid passes are used, never the connectors or MCP settings of the Claude account the machine is signed in to.Connected by Hubzoid, which adds their tools to the ones it offers Codex.
Turn limit20 agent turns per chat turnSet by the SDK unless a scheduled run passes its own limit20 tool calls per request by default, and five minutes per request

All three runtimes stream text and tool activity in the same format, record token usage per turn, and use the same access guard. Each one shows the agent only the controlled tools (restricted/ tools, remember, call_jev) the person may use, and refuses any other call. The architecture page shows where the runtime sits in a request.

Choosing the model

The Hub's model is resolved in this order. The first one set wins.

  1. A per-run override: a markdown task's model: frontmatter, or hubzoid schedule run --model.
  2. MODEL in the Hub's .env.
  3. model: in AGENTS.md.
  4. claude-local.

On a claude-local Hub, an override of a bare tier (opus, sonnet, haiku) or a claude-* id stays on the Claude runtime, so model: opus in a task means claude-local/opus. A full id for another engine, such as openai/gpt-4o or codex-local, switches that run to it.

hubzoid init in an interactive terminal checks for a logged-in claude CLI and a logged-in Codex CLI. It writes the only one it finds into the new Hub's .env, or asks once when both are usable. hubzoid init my-hub --model codex-local sets the model without asking. Detection only reads the login status and makes no model call.

Providers

MODEL prefixProviderRequired settingsExample
openrouter/OpenRouterOPENROUTER_API_KEYopenrouter/anthropic/claude-haiku-4.5
openai/OpenAIOPENAI_API_KEYopenai/gpt-4o-mini
anthropic/AnthropicANTHROPIC_API_KEYanthropic/claude-haiku-4-5
azure/Azure OpenAIAZURE_API_KEY, AZURE_API_BASE (https://<resource>.openai.azure.com), optional AZURE_API_VERSIONazure/<deployment-name>
claude-localClaude subscription through the claude CLIA claude login, CLAUDE_CODE_OAUTH_TOKEN or ANTHROPIC_API_KEYclaude-local, claude-local/opus
codex-localYour Codex account through the Codex CLI 0.147.0A file-backed codex logincodex-local, codex-local/<model-id>

For Azure, the part after azure/ is the deployment name you created in Azure, not the underlying model name. For the four key-based prefixes, a missing key stops the agent from building with an error naming the variable. Any other model id is passed to LiteLLM unchanged, and LiteLLM reads that provider's own environment variables.

Tiers for claude-local

MODELRuns on
claude-localSonnet
claude-local/sonnetSonnet
claude-local/opusOpus
claude-local/haikuHaiku, with lower latency
claude-local/claude-<model-id>That exact model id

An unrecognized pin falls back to Sonnet with a warning in the log, so an unattended run never goes to the CLI with a typo. For a server without an interactive login, mint a subscription token with claude setup-token on a logged-in machine and set it as CLAUDE_CODE_OAUTH_TOKEN in the Hub's .env. The claude CLI must be installed and on the PATH of the user that runs the Hub. The Docker image has no claude CLI, so use a provider key there.

Codex models

codex-local uses the Codex CLI's default model. codex-local/<model-id> pins a model the logged-in account can use, and Codex is told not to fall back to another one. Install the pinned CLI and log in as the account that runs the Hub:

npm install -g @openai/codex@0.147.0
codex -c 'cli_auth_credentials_store="file"' login

Other CLI versions are refused until their tool isolation has been reviewed, so upgrade the CLI only with a Hubzoid release that supports it. hubzoid doctor reports the CLI and login as runtime.codex_login. Your Codex account's model access and usage limits still apply. Token counts are recorded, and the cost is an estimate only when the model's price is known. The Docker image has no Codex CLI.

Model settings

These .env keys apply to the whole Hub.

KeyValuesEffect
REASONING_EFFORTlow, medium, highReasoning models on OpenAI and Azure receive this effort. On Claude it sets an extended thinking budget. On Codex it is sent as the turn's effort. Unset leaves the model's default.
SHOW_THINKINGoff, indicator (default), fullClaude runtime only. indicator shows a thinking panel without the text, full streams the summarized reasoning.
SHOW_TOOLSoff, compact (default), fullHow tool calls appear in chat. compact is a collapsible entry per call in the web chat and is hidden in Slack.
HUBZOID_VISIONtrue (default), falsePass attached images to the model. HUBZOID_VISION_MAX_EDGE (default 1568 pixels) and HUBZOID_VISION_MAX_IMAGES (default 4) bound them.
HUBZOID_DISABLE_PROMPT_CACHEtrue to disablePrompt caching that Hubzoid adds for Anthropic-family models on LiteLLM. HUBZOID_CACHE_TTL sets its lifetime, 1h by default.
HUBZOID_OPENAI_TRACINGtrue to enableLets the OpenAI Agents SDK export runs to OpenAI's trace dashboard. Off by default.
MODEL_LABELAny nameThe model name the bridge reports on /v1/models. Defaults to the agent name.

The configuration reference lists every key.

Models in workflows

Code workflows call the Hub's models in two ways. hub.call_agent(task) runs the full agent with its tools, skills and knowledge. hub.call_llm(prompt) is a single model call with no tools and no Hub instructions, returning text, JSON or a validated Pydantic object, on LiteLLM models, claude-local and codex-local. Both default to the Hub's model and record a usage row. The experimental hub.call_jev asks TypeSafe's Jev through OpenRouter for typed decisions with its own JEV_OPENROUTER_API_KEY, never the Hub's model key. See the workflow API.

Next steps

Read this page as Markdown

Choose which cookies Hubzoid can use. You can change this at any time from Cookie settings in the footer. Read the Cookie Policy for details.