gate4agent
gate4agent is an agent workbench, not just a transport library: a node/c2/harness/TUI stack for running, observing, and orchestrating CLI coding-agent sessions (first tier: Claude Code, Codex, Kimi, Grok) on top of a provider transport core. A node wraps one machine's providers (PTY/inline sessions, the file browser, local git, worktrees); c2 relays any number of nodes to their clients; a harness — light or full — is the one stateful backend a client app talks to, behind a single app-facing protocol, adding task kanban, session context, and delivery on top of the c2 transport; the TUI is the current client, running in harness mode or in a direct-c2 light mode. The library that started this repo — spawn, stream, resume CLI-agent subprocesses through one API — is still here, still usable standalone, and is now the substrate the rest of the stack builds on; see Transport core below.
Layers
One direction of wrapping: providers → node → c2 → harness → client app. All
crate names below are prefixed gate4agent- (e.g. -node = gate4agent-node).
- Providers — blackbox vendor CLIs (Claude Code, Codex, Kimi, Grok,
qwen-code) wrapped by the transport core: root crate
gate4agent(src/),-pty,-types,-adapters,-provider-ports,-catalog,-engine,-kernel,-handle,-tool-protocol,-tool-engine,-shell-history,-shell-capabilities,-shell-hooks,-shell-managed-hooks,-shell-one-shot,-shell-native,-runtime-native. - Observation — read-only monitoring facts projected from provider
sessions, never prompts/transcripts/credentials:
-observation-protocol,-observation-api,-observation-engine,-observation-store,-observation-service. - Node — wraps providers on one machine: PTY/inline sessions, the file
browser, local git, worktrees:
-node-protocol,-node-wire,-node(bingate4agent-node). - C2 — relays any number of nodes to their clients and routes commands
(spawn, session control) down to nodes:
-c2-protocol,-c2-client(bingate4agent-c2ctl),-c2(bingate4agent-c2). - Harness — the stateful backend behind one app-facing protocol: task
kanban over SQLite, session extraction/continuation, delivery of
skills/plugins/MCP config, an operator surface:
-harness-protocol,-harness-engine,-harness-service(bingate4agent-harness),-harness-api,-harness-client(bingate4agent-harnessctl),-harness-mcp(bingate4agent-harness-mcp),-harness-delivery. - Client —
crates/gate4agent-tui, its own nested cargo workspace: binsgate4agent-tui(harness mode) andgate4agent-tui-light(direct-c2 light mode). - Testing —
gate4agent-testkit: authentication-free provider fixtures and the Windows headless test supervisor.
Local endpoints
| Layer | Local pipe | API |
|---|---|---|
| Node | \\.\pipe\gate4agent-node (Unix: local socket) |
127.0.0.1:18310 |
| C2 | \\.\pipe\gate4agent-c2 (Unix: local socket) |
127.0.0.1:18320 |
| Harness | — | operator surface on 127.0.0.1:18330 |
All three are loopback/local-only; nothing here is reachable off the host by default.
Credentials
Env vars only — never pass a token as argv, never commit a value:
GATE4AGENT_NODE_TOKEN, orGATE4AGENT_NODE_TOKEN_<NORMALIZED_ID>for a per-node override (id uppercased, non-alphanumeric characters replaced with_)GATE4AGENT_C2_TOKENGATE4AGENT_HARNESS_OPERATOR_TOKEN
Tests
Windows PTY/session-touching tests run only through the headless test
supervisor (gate4agent-testkit's windows-headless-supervisor binary) — it
suppresses Windows fault dialogs and enforces a hard per-test timeout that
plain cargo test cannot:
target\release\windows-headless-supervisor.exe <timeout_ms> <ABS path to test exe> --exact <test_fn>
Parallel test arcs build against isolated --target-dir values
(target-<scenario>) instead of sharing target/, so independent runs never
collide on Cargo's build lock. Tests gated by
require_windows_headless_supervisor_for_test() reject themselves outright if
run any other way.
The TUI's uzor dependency
crates/gate4agent-tui depends on the uzor-tui crate from crates.io (the
uzor UI framework, maintained by the same owner). A fresh clone of this repo
builds every crate, the TUI included, with no sibling checkouts.
Transport core
The root gate4agent crate is a standalone Rust library for spawning,
streaming, resuming, and owning interactive CLI-agent subprocesses through one
API — usable on its own, with no node/c2/harness in the loop.
Supported CLI tools
| Tool | Transport | Pipe mode | ACP | Resume | Notes |
|---|---|---|---|---|---|
| Claude Code | Structured inline + PTY | current stream-json |
not in default catalog | current --resume <id> |
Full native Windows PTY lifecycle verified on 2.1.224 |
| Codex CLI | Structured inline + PTY | current exec --json |
not in default catalog | current exec resume |
Inline and Windows PTY verified on 0.144.6; inline defaults to read-only |
| Kimi Code | Structured inline + raw PTY | current stream-json |
unsupported | active adapter --session <id>; legacy PipeSession -r <id> |
Version 0.31.1 exposes --session; the latest PTY canary exited before readiness with a local provider EPERM, so current PTY lifecycle is not claimed |
The table above is the transport core's own verified matrix. Grok and
qwen-code ride the modern adapter registry (gate4agent-adapters) used by the
node stack: Grok is a first-tier provider (a known resume gap is tracked);
qwen-code is wired but unverified. The separate agent module also carries a
33-entry reference registry of CLI interop metadata derived from the Orca
project's public tool descriptions (pinned by revision in
gate4agent-catalog; credit to Orca for the original grounding); those
entries are transitional code debt, not support claims. Gemini,
OpenCode, and the other reference entries were last live-verified in the
0.2.5–0.2.6 era and are outside the current product target.
Quick start
use ;
async
Resume an existing session with
SpawnOptions { resume_session_id: Some("abc-123-session".into()), ..opts };
each active adapter handles it in its own way (Codex exec resume, Claude
--resume <id>, Kimi --session <id>) behind that one field.
Transport classes
- Structured inline — spawn one owned vendor child, read JSONL, then create a new child with the provider session id to resume.
- PTY — own the interactive terminal process: ordered bytes, VT100 state, bounded replay, input, resize, interrupt, teardown. Never auto-accepts workspace-trust or update prompts; those stay visible for the operator.
- ACP and daemon modules are compatibility surfaces, not part of the
current product target (
acp::AcpSession,daemon::DaemonSession).
LaunchPlan (via plan_launch) always produces an executable plus an
argument vector — it never concatenates a prompt into a shell command.
prepare_input() produces bounded, UTF-8-safe writes with bracketed-paste
neutralization of embedded terminal control sequences.
Source layout
src/
├── lib.rs — library root, re-exports
├── agent/ — AgentId, registry, built-in specs, argv planner, typed input preparation
├── core/ — AgentEvent, CliTool, SessionConfig, AgentError
├── transport/ — TransportSession (thin router over PipeSession), SpawnOptions
├── pipe/ — PipeSession, per-CLI NDJSON parsers + command builders
├── pty/ — PtyWrapper, PtySession, VTE/screen parsers, per-CLI PTY parsers
├── acp/ — Agent Client Protocol transport (compatibility surface)
├── rpc/ — shared JSON-RPC 2.0 primitives, used internally by acp/
├── probe/ — probe_all(), CliProbe, cache logic
├── context/ — ContextTracker, TurnCompleteData
├── cure/ — runtime model discovery (OpenCode cache → OpenRouter → hardcoded)
├── daemon/ — DaemonSession, per-daemon adapters [skeleton, not functional]
└── history/ — per-CLI session history readers (Claude, Codex, Gemini, OpenCode)
Windows spawn strategy
Reviewed Claude, Codex, and Kimi npm installations resolve to their direct
executable or JavaScript entrypoint, so prompts stay real argv/stdin data
instead of being reparsed by a .cmd shim. Unknown legacy wrappers fall back
to a shell. Unix uses direct process execution.
Current live testing status
| Tool | Pipe | PTY | ACP | Notes |
|---|---|---|---|---|
| Claude Code 2.1.224 | fresh observed; current resume canary failed | full lifecycle verified | not active | Current PTY: initial/follow-up prompt, resize, in-flight interrupt, recovery, and teardown live-verified |
| Codex 0.144.6 | fresh/resume verified | live | not active | Initial/follow-up, resize, in-flight interrupt, recovery, cleanup |
| Kimi Code 0.31.1 | current canary exited before completion | current canary failed before readiness | unsupported | Local provider state reported EPERM; no current PTY lifecycle claim |
Vendor-live inline/PTY tests are opt-in (--ignored) — they need an
installed, authenticated CLI and network access. Plain cargo test is
hermetic and never touches a provider account.
Prerequisites
At least one CLI agent must be installed on the host. gate4agent does not install them.
| CLI | Install |
|---|---|
| Claude Code | npm install -g @anthropic-ai/claude-code |
| Codex | npm install -g @openai/codex |
| Kimi Code | npm install -g @moonshot-ai/kimi-code |
Versioning
- 0.1.x — original 3-CLI library (Claude, Codex, Gemini)
- 0.2.0 — breaking: 6 CLIs,
TransportSession,AgentEventrenamed,PipeSessionremoved, OpenClaw fantasy transport - 0.2.1 — cleanup: OpenClaw removed (was never functional),
PipeSessionrestored for 0.1.x compatibility,TransportSessionis now a thin router overPipeSession - 0.2.2 — parser isolation: NdjsonParser trait extracted, per-CLI parser modules split out
- 0.2.3 — source tree restructure into core/pty/pipe layout; proper pipe builders+parsers for Codex, Gemini, Cursor, OpenCode (research-based, NOT yet tested against live CLI output)
- 0.2.4 — docs update, Codex flags fixed (
--full-autoreplaces removed--ask-for-approval) - 0.2.5 — live integration tests: fixed Codex flags, OpenCode
runsubcommand, Gemini-pflag, Windowscmd /Cquoting; all parsers verified against real CLI output - 0.2.6 — Gemini + OpenCode live-verified; OpenCode parser rewritten from real CLI output
- 0.2.7 — Cursor removed (no native Windows support, broken headless mode, closed-source CLI). 4 CLI tools remain: Claude Code, Codex, Gemini, OpenCode.
- 0.2.8 — SpawnOptions extended: continue_last, allowed_tools, permission_mode, mcp_config, max_turns, sandbox. Per-CLI builders updated.
- 0.2.9 — Daemon transport skeleton: DaemonSession, DaemonConfig, DaemonType (OpenCode, OpenClaw). Not yet functional — API surface documented for future implementation.
- 0.2.10 — Bidirectional JSON-RPC 2.0 primitives: RpcRequest, RpcResponse, RpcNotification, PendingRequests, HostHandler, MethodRouter. Shared infrastructure for ACP transport.
- 0.2.11 — Critical bugfixes: stale transport_session cleared on exit, send_prompt() returns BrokenPipe instead of silent no-op, OpenCode emits SessionStart, Gemini skips non-JSON banners silently, history readers for Codex/Gemini/OpenCode
- 0.2.12 — Test coverage: Gemini parser (14 tests), Claude parser (+8), builder argv parity (22 tests), PipeSession live test. README/DEBUGGING.md fixed. Examples added.
- 0.2.13–0.2.15 — OpenCode default model, env sanitization, test cleanup, TermCell improvements
- 0.2.16 — ACP transport: full Agent Client Protocol (JSON-RPC 2.0 over stdio) implementation. AcpSession with initialize + session/new handshake, multi-turn prompt(), session/update streaming, agent→host callbacks (fs, terminal, permissions). Live-verified with Gemini, OpenCode, Claude, Codex. 199 unit tests.
- 0.2.17 — Cursor removed again (no Windows binary:
node_sqlite3.nodeis a Linux ELF, crashes on Windows with "is not a valid Win32 application"; no official Windows build exists). 4 CLI tools remain: Claude Code, Codex, Gemini, OpenCode. - 0.2.18 — ACP host handler extended: TerminalAcpHandler with real terminal execution, FilesystemAcpHandler root whitelisting.
- 0.2.19 — RpcSession removed: standalone RPC transport was a pre-ACP intermediate step, now superseded by AcpSession. Shared JSON-RPC primitives (message, pending, handler, id) retained in
rpc/for ACP internal use. - 0.2.20 — History readers: workdir scoping for Codex (cwd field), Gemini (projects.json slug), OpenCode (directory field). All readers now filter sessions by working directory.
- 0.2.21 — Docs: fixed README Quick Start example, renamed rpc_hello → acp_hello example.
- 0.2.22 — History readers: preview extraction for Codex/Gemini/OpenCode (first real user message), system message filtering (Codex injected XML/AGENTS.md content excluded).
- 0.2.23 — History readers: Codex zombie session filter (sessions with no user input excluded), OpenCode SQLite reader (reads from ~/.local/share/opencode/opencode.db instead of nonexistent ~/.opencode/).
- 0.2.24 — History readers: Codex duplicate message fix (skip
response_itemwith role=user), old.jsonsession format removed (no cwd field = leaked into all projects). - 0.2.25–0.2.28 —
CliCapabilitiesAPI:ModelInfo,PermissionModeInfo,CliFeaturesper CLI tool. Gemini--modelflag support, Codex configurable permission modes, Claude conditional--dangerously-skip-permissions. - 0.2.29 — Dynamic model discovery:
discover_capabilities()reads CLI configs (Codex~/.codex/config.toml, OpenCodeopencode.json). Model picker enrichment at runtime. - 0.2.30 — Probe + Context tracking:
probe_all()discovers installed CLIs with caching (~/.gate4agent/probe-cache.json).ContextTrackeraccumulates tokens per session, computes remaining context. ExtendedTurnCompletewithcache_read_tokens,cache_write_tokens,reasoning_tokens,context_window,is_cumulative. Codexevent_msg/token_countparser (cumulative totals +model_context_window). Claude/Gemini/OpenCode parsers extract cache and reasoning tokens. Fixed Claude model IDs (4 → 4.6). Removedimage_to_prompt_reference()andPipeSession::tool(). - 0.2.31 — ContextTracker wired into runtime:
AgentInstancenow holds aContextTracker, updated on everyTurnCompleteevent.AgentRenderSnapshotgainscontext_percent: Option<f64>— consumers get live context window usage without any extra work. - 0.2.37 — Full OpenCode model catalog + remove Claude aliases. All 49 OpenCode built-in models (12 free first, 37 paid). Removed redundant
opus/sonnet/haikualias entries from Claude. - 0.2.36 — feat: cure runs lazily on first history load or session start.
ensure_cure_once()populates~/.gate4agent/models.jsonfrom OpenCode cache beforetool.capabilities()is called, so context windows are accurate from the first interaction. - 0.2.35 — feat(history): SessionUsage from loaded sessions.
load_session_with_usage()extracts token counts from Claude JSONL history. Context tracker is initialized when loading past sessions, socontext_percentshows real values in UI instead of 0%. - 0.2.34 — fix(context): correct usage_percent formula + cure module.
used_tokens()now =input + output + cache_read + cache_write(matches OpenCode's formula). Per-turn mode: input/cache REPLACE (snapshot), output ACCUMULATES. Codexevent_msgnormalizesinput_tokensby subtractingcached_input_tokensto avoid double-counting. Newcuremodule: runtime model discovery from OpenCode disk cache (~/.cache/opencode/models.json) with optional OpenRouter fallback (cure-networkfeature). Persists to~/.gate4agent/models.json, overlays context windows onto hardcoded capabilities. - 0.2.33 — fix(capabilities): correct context windows and model IDs for all 4 CLIs — Claude Opus/Sonnet 4.6 → 1M tokens, Codex all → 272K, Gemini preview IDs fixed, OpenCode models updated to current.
- 0.2.32 — Fix context_percent always 0%: Initialize
ContextTrackerfrom model capabilities atSessionStart(matches model ID →context_window). Reset tracker on new session spawn so stale data doesn't persist across sessions.
See ROADMAP.md for what's next and DEBUGGING.md for known issues and mitigations.
Migration guide
0.2.0 → 0.2.1
- OpenClaw removed —
CliTool::OpenClawno longer exists. If you matched on it, delete that arm. OpenClaw was never functional (unverified daemon protocol, fictional acpx API surface). PipeSessionrestored — 0.1.x callers that usedPipeSession::spawn(config, prompt, options)compile again. ThePipeSessionnow includes SessionEnd synthesis (previously only in the 0.2.0pipe_runner).TransportSessionis now a thin wrapper overPipeSession. Its public API (spawn,subscribe,session_id,send_prompt,kill) is unchanged. Internal: no moreTransportHandleenum, no deadPtyvariant.DaemonNotRunning/DaemonProbeTimeouterror variants removed — they were only reachable via OpenClaw. Remove any match arms for these.
0.2.18 → 0.2.19
RpcSessionremoved — if you were usinggate4agent::rpc::RpcSessionor the top-levelgate4agent::RpcSession/RpcSessionOptions/RpcSessionErrorre-exports, migrate to [AcpSession] instead. ACP does everything RpcSession did (bidirectional JSON-RPC 2.0, host handlers, multi-turn) but follows the standard Agent Client Protocol.- Shared
rpcprimitives unchanged —RpcRequest,RpcResponse,RpcError,RpcNotification,RpcId,HostHandler,MethodRouter,RejectAllHandler,PendingRequests,IdGen,classify_lineare all still exported. Only theRpcSessiontransport struct is gone.
0.1.x → 0.2.1
-
Events:
AgentEvent::Pipe*→ neutral names. Rename all match arms:PipeText→TextPipeToolStart→ToolStartPipeToolResult→ToolResultPipeThinking→ThinkingPipeTurnComplete→TurnCompletePipeSessionStart→SessionStartPipeSessionEnd→SessionEnd
-
PipeSession::spawn— signature unchanged:PipeSession::spawn(config, prompt, options). Compiles directly. -
SpawnOptions: new unified struct. Fields:working_dir,prompt,resume_session_id,model,append_system_prompt,extra_args,env_vars. -
CliToolis now non-exhaustive in effect (new variant:OpenCode). Add arms or a_ =>fallback.
Support the Project
If you find this tool useful, consider supporting development:
| Currency | Network | Address |
|---|---|---|
| USDT | TRC20 | TNxMKsvVLYViQ5X5sgCYmkzH4qjhhh5U7X |
| USDC | Arbitrum | 0xEF3B94Fe845E21371b4C4C5F2032E1f23A13Aa6e |
| ETH | Ethereum | 0xEF3B94Fe845E21371b4C4C5F2032E1f23A13Aa6e |
| BTC | Bitcoin | bc1qjgzthxja8umt5tvrp5tfcf9zeepmhn0f6mnt40 |
| SOL | Solana | DZJjmH8Cs5wEafz5Ua86wBBkurSA4xdWXa3LWnBUR94c |
License
MIT