A single-binary coding-agent harness — web UI first, plus TUI and CLI — built on an explicit agent graph.
Features · Install · Quickstart · Architecture · Extending · HTTP API
English | 简体中文
woca is a harness for coding agents, written in Go. It drives any Anthropic-compatible or OpenAI-compatible model through an explicit, inspectable agent graph — model → tools → model — with a human-in-the-loop permission gate, token-level streaming, auto-compaction, subagents, skills, hooks, MCP servers, and an embedded web UI, all shipped as one static binary with no runtime dependencies.
The agent loop is not a hidden while loop buried in a framework callback.
It is a graph you can read: nodes, conditional edges, reducers, checkpoints,
and interrupt/resume, powered by the langgraph runtime from
langchain-golang and
composed in internal/agent. Every run is streamed
token-by-token, every tool call passes a deterministic permission gate, and
every conversation is persisted as a resumable transcript.
- Features
- Requirements
- Installation
- Quickstart
- Providers & models
- Permissions
- Architecture
- Editions & repository layout
- Configuration
- Sessions, memory & undo
- Multi-agent
- Extending woca
- Slash commands
- HTTP API
- Observability
- Development
- Contributing
- License
Agent runtime
- Explicit agent graph with conditional edges, an
add_messagesreducer, checkpoints, and interrupt/resume — pause any run mid-tool-call and continue it later, even across process restarts. - Token-level streaming of text and tool-call deltas through the graph; the TUI and web UI render output live while tools execute.
- Streaming tool execution: read-only calls are dispatched while the model is still streaming; permission-gated calls always wait for the deterministic gate so interrupts stay exact.
- Resilient model I/O: retry with exponential backoff and an optional
fallback model on both invoke and stream paths; truncated responses
(
max_tokens) are escalated and continued automatically; oversized prompts trigger reactive compaction with a single-retry guard; each tool round is held under an aggregate size budget with deterministic placeholder replacement. - Auto-compaction with a CJK-aware token estimator: old tool results are
snipped and large ones micro-compacted before the head is summarized; a
circuit breaker disables it after repeated failures (
/compactresets). - Mid-turn queued input: submit a message while a run is in flight and it is attached after the next tool round — nothing is lost.
- Thinking / reasoning support: reasoning effort levels and streamed reasoning content, rendered as collapsible blocks in the web UI.
Tools & safety
- 20+ built-in tools:
Bash,Read,Write,Edit,NotebookEdit,Glob,Grep,ListDir,TodoWrite,WebFetch,WebSearch,AskUserQuestion,EnterPlanMode/ExitPlanMode,Task/TaskOutput/TaskStop,TaskCreate/TaskGet/TaskUpdate/TaskList,Skill,SendMessage, plus LSP-backed code intelligence. - Permission gate with four modes, explicit allow/deny/ask rules
(exact names, regexes,
Tool(glob)matchers), and an input-sensitive read-only classifier —git statusauto-allows,rm -rfnever does. - File-level undo: every
Write/Editsnapshot is kept per session;/undo//redorevert and re-apply changes, repeatable.
Surfaces
- Embedded web UI (the recommended surface): sessions grouped by
workspace, streaming assistant bubbles with tool cards, file sidebar with
syntax-highlighted previews, image uploads and screenshot paste, settings
panel — served from the same binary over SSE and managed as a background
daemon via
woca web start | stop | restart | status. - Interactive TUI (bubbletea): live streaming, approval prompts, vim-mode input, themes, scrollable transcript.
- One-shot CLI for scripting and pipes.
Ecosystem
- Skills (
SKILL.md+ YAML frontmatter), custom subagents (.woca/agents/*.md), hooks (12 event groups, matchers, exit-code semantics), MCP client (stdio + streamable HTTP, tools, resources, prompts, notifications, OAuth), and capability plugins (managed browser automation and PDF tooling) — see Extending woca. - Layered configuration: flags > workspace
.woca/> user~/.woca/for skills, agents, MCP servers, and instruction files. - Persisted settings in SQLite (default) or PostgreSQL, with API keys and DSNs encrypted at rest (AES-256-GCM).
- Usage & quota: per-session token accounting with estimated cost, and remaining-quota display for subscription plans (GLM Coding Plan, Anthropic/OpenAI OAuth logins, DeepSeek balance).
- macOS or Linux (x86-64 / arm64)
- Go ≥ 1.26 to build from source
- A model provider: an Anthropic-compatible or OpenAI-compatible API key, a gateway, or an OAuth login
From source (recommended for now):
git clone https://github.com/ProjAnvil/WoCA.git
cd woca
make # build web frontend + binary into build/wocaWith go install (CLI only, without the embedded web UI):
go install github.com/projanvil/woca/cmd/woca@latestThe web UI is embedded at compile time via go:embed; run make web before
go build if you change anything under web/.
Make targets:
| Target | Description |
|---|---|
make / make serve |
build frontend + binary; serve also launches the web UI |
make build |
build the binary to build/woca |
make test / make race |
unit tests (optionally with the race detector) |
make lint |
golangci-lint |
make install |
install to /usr/local/bin |
Point woca at a provider (persisted, encrypted at rest):
$ woca config set provider glm
$ woca config set glm-api-key <key>or export ANTHROPIC_API_KEY / OPENAI_API_KEY and skip configuration
entirely.
The web UI is the primary way to use woca. Manage it as a background daemon:
woca web start # launch the web UI in the background → http://127.0.0.1:3484
woca web status # check whether it is running
woca web restart # restart the background web UI
woca web stop # stop itThe server embeds the frontend and exposes the agent over an SSE API — create sessions, start runs, watch streaming events, resolve permission prompts, browse workspace files, and manage settings.
Other ways to run it: woca serve keeps the server in the foreground
(--addr, --no-browser), and from an interactive session /web starts
it on demand (/web <port>, /web off, /web status).
woca # bubbletea TUI on a terminal; line REPL otherwise
woca --resume last # continue your most recent sessionAnswer approval prompts with y/n and scroll with pgup/pgdown. To
interrupt a running turn, cancel it from the web UI
(POST /api/runs/{id}/cancel). In the line REPL, a line starting with !
runs a shell command and feeds its output into the conversation. See
Slash commands.
woca -p "explain this repo's build system"
woca -p "fix the failing test" --dangerously-skip-permissions # use with care| Provider | API | Default model | Notes |
|---|---|---|---|
anthropic (default) |
Anthropic Messages | claude-sonnet-4-5 |
or OAuth login: woca login |
openai |
OpenAI Chat Completions | gpt-4o |
any compatible --base-url gateway |
glm |
GLM (Anthropic-compatible endpoint) | glm-4.6 |
GLM Coding Plan quota display supported |
deepseek |
DeepSeek (Anthropic-compatible endpoint) | deepseek-chat |
balance display supported |
Any OpenAI-compatible gateway works via --base-url
(OPENAI_BASE_URL / ANTHROPIC_BASE_URL or the persisted setting). Model
fallback (--fallback-model), thinking levels (/thinking), and per-model
reasoning-effort mapping are wired through the same router.
| Mode | Read-only / meta / web | File edits | Shell |
|---|---|---|---|
default |
allowed | ask | ask |
acceptEdits |
allowed | allowed | ask |
bypassPermissions |
allowed | allowed | allowed |
readOnly / plan |
allowed | denied | denied |
Read-only classification is input-sensitive: Bash("git status")
auto-allows while Bash("rm -rf …") asks. Explicit rules override the mode:
{
"permissions": {
"allow": ["Bash(git *)", "Read"],
"deny": ["Bash(rm *)"],
"ask": []
}
}Precedence is deny → ask → allow → mode default. Subagents are
fail-closed: an un-granted write permission surfaces as permission denied
instead of prompting.
cmd/woca cobra CLI: flags, config, REPL, one-shot, serve
internal/agent the coding-agent graph (langchain-golang/langgraph
runtime): gate, plan mode, streaming execution,
recovery, compaction, teams, goals
internal/tools tool set + registry + permission categories
internal/permissions permission modes, rules, read-only classifier
internal/model provider router: retry, fallback, invocation
internal/runner streaming run loop + interrupt handling + rendering
internal/console slash commands shared by TUI, REPL, and web
internal/tui bubbletea terminal UI
internal/server web server: sessions, runs, SSE, settings API
internal/session JSONL transcript persistence
internal/compact auto-compaction / summarization
internal/tokens CJK-aware token estimator
internal/skills SKILL.md loader (layered: flag > workspace > user)
internal/agents subagent definitions (.woca/agents/*.md)
internal/hooks hook runner (12 event groups)
internal/mcp MCP client (stdio + streamable HTTP, OAuth)
internal/plugins marketplace plugins (skills/agents bundles)
internal/pluginrt capability-plugin runtimes (browser, pdf)
internal/settings settings database + encrypted secrets
internal/quota provider quota/usage display
internal/merge layered configuration merge
woca ships as two editions built from one repository:
- Personal edition — the
wocabinary: CLI/TUI/REPL plus the localwoca serveweb UI, zero external dependencies (sqlite, in-process everything). This is what individual users install. - Enterprise edition — the server roles (
woca-server,woca-worker,woca-config,woca-tenant) assembled from the same core plus the enterprise tree: multi-tenant auth (API keys, native-client DPoP sessions), queue dispatch with the NATS message plane, the standalone config service, observability (OTel/Prometheus/Grafana), and registry DSN discovery.
The delivery axis is orthogonal to the edition axis: today both editions
are self-hosted (see deploy/selfhosted/, deploy/k8s/,
deploy/vm/); a managed cloud form is a future packaging of the same
enterprise code, not a third edition.
internal/ core, shared by both editions (agent, tools, server, fsb, …)
internal/ee/ enterprise-only: msgplane, tenant, configsvc, worker, obs,
discovery, nativeauth, dpop, tenantcmd, memcmd, skillsvc,
bridge (the private-cloud bridge service), entrygw (the
private-core machine registry), edgegw (the stateless edge
access point), edition (woca-server's composition root)
internal/platform/machineid shared machine identity (enrollment tokens,
Ed25519 identities, opaque access tokens)
web/ shared web UI (personal + enterprise web surface)
ee/ enterprise web consoles (configsvc-web, opsconsole-web,
skillsvc-web)
deploy/selfhosted/ enterprise docker-compose stack (data plane + full)
deploy/k8s/ Kubernetes manifests deploy/vm/ registry verify
cmd/woca personal entry (everything)
cmd/woca-* enterprise role entries (no interactive surfaces)
woca-bridge is the productized private-cloud execution plane: the same
serve surface plus the product identity model — organization credentials
issued by the tenant ops console on the private core (the bridge itself
carries no key management, v1.7; an unenrolled bridge keeps loopback
trust only), URL+key login with cookie sessions (revocation cascades
from the ops console through the config generation), the
workspace folder browser (/api/fs/*, jailed with os.OpenRoot), and a
fail-closed bind check (an unauthenticated non-loopback bind refuses to
start). Registered bridges see exactly one control-plane peer — the
stateless edge access point (cmd/woca-edge, internal/ee/edgegw; R7:
no database, no files, every machine-token check calls home to the
service plane). Behind it the private core splits in two: the registry
routes (/gw/enroll|renew|heartbeat|resolve|bridges, mounted on
woca-server's WOCA_GW_ADDR listener) and the config pull (/gw/config
on woca-config). A one-time enrollment token exchanges for a local
Ed25519 identity plus short-TTL opaque access tokens; pulled settings
install as the managed overlay (locked, managed_fields in
/api/settings), and a control-plane outage keeps the bridge running
with a STALE flag on the status page. The same outbound channel carries
scenario four's data plane: client traffic parks at the edge until the
bridge's tunnel picks it up and answers against its own handler stack
(SSE streams pass with flushes intact), so any-place web commands a
NAT-hidden bridge with zero inbound ports. Loopback bridges keep the
zero-login personal experience; the personal woca binary links none of
this.
The full private-cloud stage ships with it: TLS direct termination
(WOCA_TLS_CERT/KEY), the long-poll config channel (push semantics over
pull), worker-led enrollment (fingerprint approval — no token pasting),
the bridge's read-only operations view (live sessions + audit over HTTP +
SPA; user credentials are organization keys issued by the tenant ops
console — the bridge itself carries no key management, v1.7), a global
health lamp and Markdown diagnostics export, QR pairing on the login
page, and the native client's embedded bridge (bundled binary, zero-login
loopback profile, org-key login with TOFU pinning and OS-keychain key
storage),
the three-entry connection page (embedded / private cloud via the edge
with target-bridge selection / direct bridge — one profile mechanism
underneath), the bridge-local cloud-access settings (edge.* keys in the
settings database, SPKI TOFU pin, env override with source labels, page
at Settings → Cloud access), and the two dev compose groups (the
microservices twin plus the §6.3 edge group with public/internal
network layering and the bridge as an independent container — see
deploy/selfhosted/BRIDGE.md for the deployment runbook).
The boundary is enforced, not aspirational: the personal binary never links
internal/ee (its dependency baseline is pinned in ci/deps-personal.txt),
the enterprise tree never links the interactive frontends, and core
packages reach enterprise capabilities only through seams —
server.MsgPlane, server.NativeAuth, servefront.Edition, and
wocadb.DSNResolver. make check-editions guards all three directions.
Layout changes (2026-08-22). The enterprise packages moved under
internal/ee/(import paths changed accordingly — e.g.internal/msgplane→internal/ee/msgplane; the enterprise web consoles moved toee/);deploy/enterprise/is nowdeploy/selfhosted/. The personal binary no longer ships thetenantandmemorysubcommands (usewoca-tenant), andwoca serve --auth apikeynow requires thewoca-serverbinary — multi-tenant auth is an enterprise capability.
┌──────────┐ no tool calls ┌──────┐
START ─▶│ model │──────────────────▶│ END │
└────┬─────┘ └──────┘
│ has tool calls
▼
┌──────────┐ (gate + hooks) ┌──────┐
│ tools │───────────────────▶│ INT │
└────┬─────┘ └──┬───┘
│ (always loops back) │ resume
└────────────────────────────┘
The tools node runs a per-call gate (permissions, AskUserQuestion,
plan approval) sequentially — so interrupts pause deterministically — then
executes allowed calls concurrently. Pre/Post tool-use hooks wrap every
call; a Pre hook exiting 2 denies the tool, a Post hook exiting 2 feeds
its output back to the model. State is a single messages channel with an
add_messages reducer, so streaming deltas, tool results, and interrupts
all compose.
/goal <objective> switches the agent into an autonomous loop expressed as
its own graph on top of the agent graph:
START ─▶ assess ─▶ execute ─▶ evaluate ─┬─ active ─▶ assess (next cycle)
└─ terminal ─▶ END
Each cycle plans (assess), runs a full nested agent turn (execute), then
verifies with skepticism (evaluate) — the agent may claim completion,
but only verified evidence completes the goal. Budgets (--cycles,
--tokens), idle auto-continue, and mid-cycle interrupt/resume apply; goal
state persists per session and survives restarts. Manage the loop with
/goal status, /goal pause|resume, /goal budget, /goal clear; the web
UI shows cycle/token meters and per-cycle verdicts live.
Settings live in a database under ~/.woca — SQLite by default (pure Go,
no cgo), PostgreSQL optional via DSN. Credentials and DSNs are stored
encrypted at rest (AES-256-GCM; master key in ~/.woca/secret.key,
mode 0600). Three entrances share one database:
$ woca config set provider openai
$ woca config set openai-api-key sk-... # stored encrypted
$ woca config list- Terminal:
woca config get|set|list - Interactive:
/settings,/memory list|put|get|del - Web UI: settings panel (
GET/PUT /api/settings)
Key resolution order: --api-key flag → ANTHROPIC_API_KEY /
OPENAI_API_KEY → database (encrypted) → OAuth token. Graph checkpoints
and the memory store each pick a backend (sqlite, postgres, file,
memory, none).
Important flags:
| Flag | Description |
|---|---|
-p, --prompt |
one-shot prompt (runs non-interactively) |
--model / --provider |
model name and provider |
--base-url / --api-key |
provider endpoint and credential |
--permission-mode |
default | acceptEdits | bypassPermissions | readOnly | plan |
--workdir |
working directory (default: cwd) |
--mcp |
MCP server as name=command or name=https://… (repeatable) |
--fallback-model |
fallback on exhausted retries |
--max-turns |
cap model rounds per turn |
--auto-compact |
token-estimate compaction threshold (0 disables) |
--resume |
resume a transcript (or last) |
--db |
settings database: sqlite path, postgres:// DSN, or none |
--checkpoint-dir |
persist graph checkpoints for cross-process resume |
- Sessions persist as JSONL transcripts under
~/.woca/sessions, grouped by workspace;/renamesnapshots,/branchforks,/rewindjumps to an earlier point,/exportdumps markdown or resumable JSON, and--resume <name|last>continues anywhere. - Memory: with a memory backend configured, each turn prefetches
memories relevant to your message and attaches them after tool rounds;
/memory putwrites long-term context that reaches the model later. - Instruction files:
CLAUDE.mdandAGENTS.mdare injected from the user level down through every directory from the git root to your working directory. - Undo:
Write/Editsnapshots are kept per session;/undo//redorevert and re-apply the last change (repeatable). Changes made byBashor outside woca are not tracked.
Task spawns isolated subagents with their own context window and tool
subset; TaskOutput/TaskStop manage them. Define custom subagents as
markdown files:
<!-- .woca/agents/reviewer.md -->
---
name: reviewer
description: Reviews diffs for correctness and style issues
tools: Read, Grep, Glob
---
You are a critical code reviewer. Report only concrete, actionable issues.The model then calls Task(subagent_type="reviewer", prompt=…).
For parallel work, teams share a worktree and coordinate through a shared
task list with ownership (TaskUpdate claims atomically; DependsOn
ordering) and message routing (SendMessage pokes idle teammates).
Teammates run autonomous loops with per-teammate timeouts; /team shows
membership, tasks, and inboxes.
Drop a SKILL.md with YAML frontmatter under .woca/skills/<name>/:
---
name: commit
description: Write a conventional commit message
---
Write a conventional-commit message for the current diff.The model discovers it and can call Skill(skill="commit"). Skills resolve
layered: workspace copies override user-level ~/.woca/skills.
See Subagents. Definitions resolve from .woca/agents/
(workspace) over ~/.woca/agents/ (user).
Configure lifecycle hooks in ~/.woca/settings.json or
.woca/settings.json:
{
"hooks": {
"PreToolUse": [
{"matcher": "Bash(git *)", "hooks": [{"type": "command", "command": "echo 'git command incoming'"}]}
],
"PostToolUse": [
{"matcher": "Edit|Write", "hooks": [{"type": "command", "command": "cat"}]}
]
}
}Twelve event groups are supported: PreToolUse, PostToolUse,
UserPromptSubmit, Stop, SessionStart, SessionEnd, SubagentStop,
Notification, PreCompact, PostCompact, PermissionRequest, and
PostSampling. Each hook receives a JSON event on stdin;
stdout is fed back as context. A PreToolUse hook exiting 2 denies the
tool; a PostToolUse hook exiting 2 blocks continuation.
woca --mcp "filesystem=node /path/to/server.js /tmp" \
--mcp "search=https://mcp.example.com/mcp"Tools appear as mcp__<server>__<tool>, plus resources
(mcp__<server>__resources) and prompts. Both stdio (newline JSON-RPC) and
streamable HTTP (POST + JSON/SSE, Mcp-Session-Id) transports are
supported; OAuth-protected servers are authorized once with
woca mcp auth <name> <endpoint> (tokens stored encrypted). Server
definitions resolve layered: --mcp flag > workspace
.woca/mcp.json > user ~/.woca/mcp.json, and tools added mid-session are
picked up between rounds.
Managed, versioned tool bundles installed on demand into
~/.woca/plugins:
- browser — hosted browser automation (navigation, clicks, forms, screenshots) for web testing and research
- pdf — a sandboxed Python environment for PDF generation and processing
Browse and install from the settings panel (/plugins), which provisions
the pinned runtime and verifies SHA-256 checksums.
woca interoperates with common agent conventions: it reads CLAUDE.md /
AGENTS.md instruction files, discovers skills from .claude/skills, and
— from the web settings panel — can import skills and MCP server
definitions from an existing ~/.claude setup in one click. If you already
run agent tooling, woca picks up your configuration instead of asking you
to rewrite it.
Available in the TUI, REPL, and web chat input (prefix /, with
completion):
| Command | Description |
|---|---|
/help |
list available commands |
/status |
version, provider/model, permission mode, run stats |
/permissions [mode] |
show or set the permission mode |
/model [name] |
show or switch the model at runtime |
/thinking [level] |
reasoning effort: off | low | medium | high | tokens |
/quota |
remaining quota for subscription plans |
/cost |
cumulative token usage and estimated cost |
/clear |
start a fresh conversation |
/compact |
summarize the conversation to free context |
/context |
context-window usage breakdown |
/todos, /tasks |
todo list / shared task list |
/skills, /mcp, /agents, /plugins |
manage extensions |
/undo, /redo |
revert / re-apply the last file edit |
/rewind, /branch, /rename, /export |
session history navigation |
/goal … |
autonomous goal loop (set/status/pause/resume/budget/clear) |
/team |
agent team status |
/memory … |
inspect and edit the memory store |
/settings … |
persisted settings |
/init |
generate an AGENTS.md project guide |
/doctor |
check config, credentials, storage, hooks, permissions |
/theme, /vim, /keybindings |
UI preferences |
/web [port|off|status] |
embedded web UI control |
/output-style |
default | explanatory | learning |
The web server exposes the agent over REST + SSE:
| Endpoint | Description |
|---|---|
POST /api/sessions |
create a session (persisted immediately) → {"session_id":"…","workspace":"…"} |
GET /api/sessions |
list persisted sessions |
GET /api/sessions/{id} |
fetch a full transcript |
DELETE /api/sessions/{id} |
delete a session (tombstoned against revival) |
POST /api/runs |
start a run: {"session_id":"…","prompt":"…"} |
GET /api/runs/{id}/events |
SSE: delta / reasoning / tool_call / tool_result / goal / goal_note / interrupt / done / error (goal runs stream cycle narration as goal_note and end with one unified delta reply) |
POST /api/runs/{id}/answer |
resolve an interrupt: {"approve":true} or {"answers":[…]} |
POST /api/runs/{id}/input |
queue mid-turn input |
POST /api/runs/{id}/cancel |
cancel a run |
GET/PUT /api/settings |
read/update persisted settings |
Enable the observability log (--debug-log, the web settings panel, or
/debug-log on) for full harness logging under ~/.woca/logs/,
size-rotated:
logs/woca.log run lifecycle, model usage, summaries
logs/ws-<name>-<hash8>/<session>.jsonl one JSON event per line
logs/ws-<name>-<hash8>/<session>/<run>.json message-level transcript dump
Every graph-node dispatch is recorded with phase, duration, and state summary; in the web UI each assistant bubble gains a debug action that copies the exact log locations for troubleshooting.
make web # build the frontend (required once before go build)
make build # build the binary
make test # unit tests
make race # tests with the race detector
make lint # golangci-lintThe frontend is a Vue 3 + TypeScript + Vite SPA under web/;
the demo (npm run dev:demo) previews the whole UI with mock data. The
graph runtime, agent loop, permission gate, hooks, compaction, skills, MCP
client, and web server are all covered by unit tests — the project is
built test-first.
See CONTRIBUTING.md for code style and PR guidelines.
Issues and pull requests are welcome. For anything larger than a bug fix, please open an issue first to discuss the design. See CONTRIBUTING.md.
woca is released under the MIT License. It is built on langchain-golang, bubbletea, and cobra.