Two things.
1. The architecture_mapper proposal, applied AND made durable.
The GLM proposal (019fddd9) was accepted in full: the agent's system_prompt now
carries the Mermaid-first constraint and its brain was rewritten. Both verified
against the live row and the .h5 file.
But `apply_identity` writes `UPDATE agents SET system_prompt` and
`apply_brain_consolidation` writes that agent's brain — neither touches the team
TEMPLATE. That agent is mission-scoped, so the improvement would have died with
the mission. The model's actual insight was sharp and worth keeping: "Mermaid
diagrams beat prose" lived in the brain SEED and not in the system PROMPT, so it
only applied when the agent happened to consult its brain. That constraint is
now in templates/teams/codebase_research.toml, where every future Codebase
Research team inherits it.
(The proposal's second item mostly restated anti-patterns the seed already
lists, so the seed is unchanged. Applying an LLM's suggestion is not the same as
agreeing with all of it.)
2. Gemini is gone.
Removed: the `gemini.default` provider alias and its `is_exact_provider_match`
prefix, GEMINI_API_KEY forwarding to agent containers, the evaluator's
gemini->gemini family row, the model selectors in claws/teams/planner and in
TeamWizard + AgentComputer, and the commented provider block in the runtime
config example (whose ZEROCLAW_AGENT_MAP example still mapped a worker_gemini
that no longer existed).
`provider_alias_for("gemini")` now returns claude_cli.default via the
unrecognised-model branch, which LOGS. A stray gemini binding degrades visibly
rather than resolving to a provider row we no longer ship. A test pins that, and
another pins that GEMINI_API_KEY is forwarded in NEITHER auth mode, so adding it
back to the list is a visible change rather than an accident.
Avatar generation is DELETED, not disabled — it called Gemini's image model, and
there is no alternative: Claude and Kimi are text-only, and z.ai answers
"Unknown Model" for cogview-3-flash and cogview-4 on our plan (measured, not
assumed). AvatarModal keeps UPLOAD, which never needed a provider; only the
prompt-generation half is gone.
240 backend lib tests, 89 frontend tests, clean tsc + eslint, build succeeds.
241 lines
12 KiB
TOML
241 lines
12 KiB
TOML
# Phase-1 runtime config for a tenant: one provider model + a multi-agent
|
|
# role-cast. ONE container hosts the whole cast; Clawmates' cm-orchestrator owns
|
|
# the topology graph and drives a specific role by alias over /ws/chat?agent=...
|
|
#
|
|
# Secrets are NOT committed — the API key is injected at runtime via env
|
|
# ZEROCLAW_providers__models__groq__default__api_key=<key>
|
|
# (double-underscore = config nesting). Swap groq→anthropic for prod.
|
|
|
|
# Mark onboarding complete (headless equivalent of the browser Quickstart) so
|
|
# the agents will answer; otherwise /ws/chat returns NEEDS_ONBOARDING.
|
|
[onboard_state]
|
|
quickstart_completed = true
|
|
|
|
# Reasoning backend = the local Claude Code CLI (native `claude_cli` provider).
|
|
# The daemon spawns `claude -p --output-format json` per turn, authed by
|
|
# CLAUDE_CODE_OAUTH_TOKEN (injected at runtime) — i.e. the Claude subscription,
|
|
# no per-minute API TPM ceiling. Leave model unset to use the subscription
|
|
# default; set e.g. model = "haiku" to force a model.
|
|
[providers.models.claude_cli.default]
|
|
model = "haiku"
|
|
|
|
# claude_cli ACTING through the §15 door (subscription agent that ACTS, not just
|
|
# reasons). claude -p has no tool API ZeroClaw can drive, so instead we let
|
|
# Claude Code's OWN MCP client reach the door: `--mcp-config` points at a JSON
|
|
# file describing the door as an http MCP server; `tools` (--allowedTools)
|
|
# auto-approves the door tool; `disallowed_tools` (--disallowedTools) locks out
|
|
# claude's native tools so the gated door is the ONLY actuator.
|
|
# Create /zeroclaw-data/clawmates-mcp.json (NOT committed; token injected):
|
|
# { "mcpServers": { "clawmates": { "type": "http",
|
|
# "url": "http://clawmates_server_1:8080/mcp",
|
|
# "headers": { "Authorization": "Bearer <per-workspace door token>" } } } }
|
|
# NOTE: model MUST be set via env (ZEROCLAW_providers__models__claude_cli__door__model)
|
|
# — the TOML sub-table parses empty (resolve_default_model gotcha).
|
|
[providers.models.claude_cli.door]
|
|
tools = ["mcp__clawmates__email_send"]
|
|
disallowed_tools = ["Bash", "BashOutput", "KillShell", "Write", "Edit", "NotebookEdit", "Read", "Glob", "Grep", "WebFetch", "WebSearch", "Task"]
|
|
mcp_config = "/zeroclaw-data/clawmates-mcp.json"
|
|
|
|
# An agent that ACTS through the door (uncomment; it needs NO ZeroClaw
|
|
# mcp_bundles — the door comes via claude's own MCP client):
|
|
# [agents.subdoor]
|
|
# model_provider = "claude_cli.door"
|
|
# risk_profile = "toolfree"
|
|
|
|
# --- Extra worker LLMs: Kimi Code (native CLI) and GLM (Zhipu) ---
|
|
# Run a topology WORKER on another model.
|
|
#
|
|
# Kimi Code: subscription-billed. Its coding endpoint (api.kimi.com/coding) is
|
|
# coding-AGENT gated (User-Agent enforced) — a raw OpenAI-compat client gets 403,
|
|
# so we drive Kimi through its OWN CLI via the `kimi_cli` provider (mirrors
|
|
# `claude_cli`): it spawns `kimi -p` headless on the Kimi membership, with Kimi's
|
|
# own agent loop + sub-agents. Auth is the CLI's own ~/.kimi-code/config.toml
|
|
# (default_model + [providers."managed:kimi-code"] type="kimi" with the key) or
|
|
# `kimi login` — no key is passed by ZeroClaw. (HOME=/zeroclaw-data, so the file
|
|
# lives at /zeroclaw-data/.kimi-code/config.toml in the runtime volume.)
|
|
# [providers.models.kimi_cli.default]
|
|
# # binary_path = "kimi" # default; resolved on PATH
|
|
# # yolo = true # -y: auto-approve tool calls (needed for a door agent
|
|
# # # that acts via Kimi's own MCP client, mcp.json)
|
|
# [agents.worker_kimi]
|
|
# model_provider = "kimi_cli.default"
|
|
# risk_profile = "toolfree"
|
|
#
|
|
# GLM (Zhipu) — like Kimi, the GLM Coding Plan is subscription-billed and its
|
|
# raw OpenAI/SDK endpoint is ToS-THROTTLED ("1113 Insufficient Balance"); it is
|
|
# only served inside OFFICIALLY SUPPORTED tools, and Claude Code is the #1 one.
|
|
# So GLM rides our existing `claude_cli` provider via a z.ai env override — no
|
|
# new provider, no image rebuild. Needs a fresh HOME + CLAUDE_CODE_OAUTH_TOKEN=""
|
|
# so the container's cached Claude subscription doesn't override the routing
|
|
# (pre-create <HOME>/.claude.json = {"hasCompletedOnboarding":true}). The z.ai
|
|
# key is read from the daemon env via the `$NAME` indirection (keep it out of
|
|
# config). Models: glm-5.2 (Opus-class), glm-4.7 (Sonnet-class), glm-4.5-air.
|
|
# [providers.models.claude_cli.glm]
|
|
# model = "glm-4.7"
|
|
# env = { HOME = "/zeroclaw-data/glm-home", CLAUDE_CODE_OAUTH_TOKEN = "", ANTHROPIC_BASE_URL = "https://api.z.ai/api/anthropic", ANTHROPIC_AUTH_TOKEN = "$ZAI_API_KEY", API_TIMEOUT_MS = "3000000" }
|
|
# [agents.worker_glm]
|
|
# model_provider = "claude_cli.glm"
|
|
# risk_profile = "toolfree"
|
|
# Flagship tier (Opus-class): a second alias on glm-5.2 (same z.ai env). Costs
|
|
# 2-3x quota vs glm-4.7 — reserve for hard reasoning roles, not routine ones.
|
|
# [providers.models.claude_cli.glm5]
|
|
# model = "glm-5.2"
|
|
# env = { HOME = "/zeroclaw-data/glm-home", CLAUDE_CODE_OAUTH_TOKEN = "", ANTHROPIC_BASE_URL = "https://api.z.ai/api/anthropic", ANTHROPIC_AUTH_TOKEN = "$ZAI_API_KEY", API_TIMEOUT_MS = "3000000" }
|
|
# [agents.worker_glm5]
|
|
# model_provider = "claude_cli.glm5"
|
|
# risk_profile = "toolfree"
|
|
#
|
|
# Gemini is NOT wired. The platform stripped it: no provider alias, no key
|
|
# forwarded to agent containers, no selector entry. ZeroClaw still has a built-in
|
|
# `gemini` family, so an operator could add it back here — but `provider_alias_for`
|
|
# no longer resolves `gemini*` to it, and it would log as an unrecognised model.
|
|
#
|
|
# Groq — built-in `groq` family (key via ZEROCLAW_providers__models__groq__default__api_key).
|
|
# Fast, but LOW free-tier TPM: each turn carries a ~9.5k-tok system prompt, so 2
|
|
# turns/min can exceed 12k TPM → 429. Keep Groq to light/sequential roles.
|
|
# [providers.models.groq.default]
|
|
# # model via ZEROCLAW_providers__models__groq__default__model=llama-3.3-70b-versatile
|
|
# [agents.worker_groq]
|
|
# model_provider = "groq.default"
|
|
# risk_profile = "toolfree"
|
|
|
|
# --- Phase C: heterogeneous cross-model topologies ---
|
|
# The orchestrator maps a topology node's `role` → an agent alias via the SERVER
|
|
# env ZEROCLAW_AGENT_MAP="role=alias,role=alias" (+ ZEROCLAW_DEFAULT_AGENT for
|
|
# unmapped roles). Point each semantic role at a different model to run ONE
|
|
# topology across vendors, e.g.:
|
|
# ZEROCLAW_AGENT_MAP="coordinator=coordinator,researcher=worker_glm,analyst=worker_kimi,writer=worker_glm5,actor=worker_groq"
|
|
# Then POST /api/topologies/run {task, graph} with those role names. QUOTA CARE:
|
|
# GLM/Kimi plans have 5h/weekly caps + low concurrency (GLM Lite ~1 project at a
|
|
# time) — prefer pipeline (sequential) over swarm/mesh, keep tasks short, and ask
|
|
# the model for brief replies to cap output tokens.
|
|
|
|
# Tool-free profile — agents can only reason + call the MCP door (tools inject
|
|
# AFTER this allowlist filter). `level = "full"` removes ZeroClaw's OWN approval
|
|
# prompts so the agent is autonomous; governance happens at the Clawmates door
|
|
# (auto-policy, no human — "agents control their destiny"). Use "supervised" if
|
|
# you want ZeroClaw to also gate sensitive calls with an in-channel approval.
|
|
[risk_profiles.toolfree]
|
|
level = "full"
|
|
allowed_tools = []
|
|
excluded_tools = ["shell", "file_read", "file_write", "http_request", "browser", "composio"]
|
|
|
|
# Writable coding-loop profile (coder/tester/committer/engineer roles). The
|
|
# `allowed_tools` list is a STRICT allowlist — a tool must be named here to be
|
|
# callable. These MUST be the current ZeroClaw 0.8+ tool names:
|
|
# file_edit — create/overwrite/patch (the real write tool; `file_write`
|
|
# was renamed and now REFUSES on ephemeral workspaces, so a
|
|
# stale `file_write` entry silently leaves agents read-only)
|
|
# content_search — grep across the workspace
|
|
# glob_search — find files by glob
|
|
# git_operations — git status/add/commit/diff/log
|
|
# Regression guard: if you ever see an agent report "I only have file_read" and
|
|
# burn tokens dumping code inline, this list drifted back to pre-0.8 names.
|
|
[risk_profiles.coding_readwrite]
|
|
level = "full"
|
|
# `file_write` creates and overwrites; `file_edit` only replaces an exact
|
|
# existing string and rejects an empty `old_string`, so without file_write an
|
|
# agent literally cannot create a new file. Observed on mission 019fc372: the
|
|
# agent burned its turn reasoning about how to make file_edit create a file
|
|
# ("the tool rejected empty old_string... the shell is restricted") before
|
|
# working around it through `shell`. The comment below has claimed file_write
|
|
# was here since the profile was written; the list never had it.
|
|
allowed_tools = ["file_read", "file_write", "file_edit", "content_search", "glob_search", "git_operations", "shell"]
|
|
excluded_tools = ["http_request", "browser", "composio"]
|
|
|
|
# Read-only research profile (scout/researcher/reviewer/planner roles).
|
|
[risk_profiles.research_readonly]
|
|
level = "full"
|
|
allowed_tools = ["file_read", "content_search", "glob_search"]
|
|
excluded_tools = ["shell", "file_write", "http_request", "browser", "composio"]
|
|
|
|
# Read-only research + public web (papers, docs). Still no shell / no write.
|
|
[risk_profiles.research_web_readonly]
|
|
level = "full"
|
|
allowed_tools = ["file_read", "content_search", "glob_search", "web_search", "web_fetch"]
|
|
excluded_tools = ["shell", "file_write", "http_request", "browser", "composio"]
|
|
|
|
# The role-cast. node.role → agent alias is configured Clawmates-side via
|
|
# ZEROCLAW_AGENT_MAP (e.g. "analyst=researcher"); `scout` is the default
|
|
# fallback (ZEROCLAW_DEFAULT_AGENT) for any unmapped role.
|
|
[agents.coordinator]
|
|
model_provider = "claude_cli.default"
|
|
risk_profile = "toolfree"
|
|
mcp_bundles = ["clawmates_door"]
|
|
|
|
[agents.researcher]
|
|
model_provider = "claude_cli.default"
|
|
risk_profile = "toolfree"
|
|
mcp_bundles = ["clawmates_door"]
|
|
|
|
[agents.writer]
|
|
model_provider = "claude_cli.default"
|
|
risk_profile = "toolfree"
|
|
mcp_bundles = ["clawmates_door"]
|
|
|
|
[agents.worker]
|
|
model_provider = "claude_cli.default"
|
|
risk_profile = "toolfree"
|
|
mcp_bundles = ["clawmates_door"]
|
|
|
|
# Default fallback alias for roles not present above.
|
|
[agents.scout]
|
|
model_provider = "claude_cli.default"
|
|
risk_profile = "toolfree"
|
|
mcp_bundles = ["clawmates_door"]
|
|
|
|
# Step 1B — the Clawmates §15 MCP door. Tool-free agents reach it as their ONLY
|
|
# actuator; MCP tools inject AFTER the empty allowlist above. The door enforces
|
|
# §15 (audit, broker credential custody) but auto-decides via policy (no human;
|
|
# default allow-all → agents are autonomous). The Authorization bearer is a
|
|
# per-workspace door token (a Clawmates session token) — injected on gw-04, NOT
|
|
# committed; the placeholder below is replaced at deploy time.
|
|
[mcp]
|
|
enabled = true
|
|
deferred_loading = false
|
|
|
|
[[mcp.servers]]
|
|
name = "clawmates"
|
|
transport = "http"
|
|
url = "http://clawmates_server_1:8080/mcp"
|
|
tool_timeout_secs = 120
|
|
headers = { Authorization = "Bearer REPLACE_WITH_DOOR_TOKEN" }
|
|
|
|
[mcp_bundles.clawmates_door]
|
|
servers = ["clawmates"]
|
|
|
|
# ── A2A ingress (Phase 2) ───────────────────────────────────────────────────
|
|
# ZeroClaw's Agent2Agent server. These props are set at RUNTIME by cm-api
|
|
# (runtime_provision::enable_a2a_server / publish_claw) when a workspace opts in
|
|
# via POST /api/a2a/settings — they are NOT committed here. Shown for reference:
|
|
#
|
|
# [a2a.server]
|
|
# enabled = true
|
|
# bind = "0.0.0.0" # internal interface only
|
|
# port = 42617
|
|
# public_base_url = "https://api.clawmates.work/api/a2a/<workspace-id>"
|
|
# # the cm-api EDGE, never this daemon
|
|
# [agents.<alias>.a2a]
|
|
# published = true
|
|
# exposed_skills = ["search", "summarize"]
|
|
#
|
|
# SECURITY (must hold at deploy): the daemon's :42617 is NEVER host-published or
|
|
# Traefik-routed. Only cm-api is internet-facing; Traefik routes /api/a2a/** (and
|
|
# the rest of /api/**) to the cm-api service, which authenticates the external
|
|
# caller per-workspace (a2a_tokens), then injects the internal ZEROCLAW_TOKEN
|
|
# when proxying to this daemon's /a2a/{alias}. A leaked global door bearer must
|
|
# never reach an internet-exposed :42617. Cards advertise only the edge URL via
|
|
# public_base_url. Ingress throttle: CLAWMATES_A2A_POLICY=deny (kill switch),
|
|
# CLAWMATES_A2A_RATE_LIMIT=<n>/hour.
|
|
|
|
# NOTE: `claude_cli` is a TEXT-ONLY provider — `claude -p` doesn't surface
|
|
# tool-calls back to ZeroClaw, so claude_cli agents reason but can't invoke the
|
|
# door. An agent that ACTS through the door needs a tool-capable provider
|
|
# (groq/anthropic/openai). Example actor agent (uncomment + provide groq creds
|
|
# via env ZEROCLAW_providers__models__groq__default__{model,api_key}):
|
|
# [providers.models.groq.default]
|
|
# [agents.actor]
|
|
# model_provider = "groq.default"
|
|
# risk_profile = "toolfree"
|
|
# mcp_bundles = ["clawmates_door"]
|