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.
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.
| Provider | Tier | Support path |
|---|---|---|
| Claude Code | first tier | full workbench: PTY sessions, native history, hooks, spawn/observe through node → harness; transport core verified on 2.1.224 |
| Codex CLI | first tier | full workbench; transport core verified on 0.144.6 |
| Kimi Code | first tier | full workbench via the adapter registry; current transport-core PTY canary is failing (see the matrix below) |
| Grok CLI | first tier | full workbench via the adapter registry (gate4agent-adapters): PTY sessions, native history (~/.grok/sessions), hooks; a resume gap is tracked |
| qwen-code | wired, unverified | adapter registry entry exists; no verification claim |
| Gemini, OpenCode | legacy | transport-core paths last live-verified in the 0.2.5–0.2.6 era; outside the product target |
The root src/ tree is the transport core — the original library this
repo started as, still published as the gate4agent crate and still the
substrate the node embeds for spawning/streaming provider processes. It is
maintained, not legacy — but it is no longer where the product grows.
Everything else lives in crates/: the workbench layers (node, c2, harness,
TUI), the engine substrate under them, and gate4agent-pty (the in-house
PTY backend). All new development happens in crates/; the root library
changes only when the transport core itself does.
| 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.
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
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.
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.
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.
| 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 |
| Grok CLI | PTY via the adapter registry (workbench path) | — (no transport-core pipe client) | not active | resume gap tracked | First-tier through gate4agent-adapters, not this table's transport-core clients: PTY sessions, native history, hooks |
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.
use gate4agent::{CliTool, SessionConfig, AgentEvent, PipeSession, PipeProcessOptions};
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let config = SessionConfig {
tool: CliTool::ClaudeCode,
working_dir: std::env::current_dir()?,
env_vars: vec![],
name: None,
};
let session = PipeSession::spawn(config, "Say hello in 3 words", PipeProcessOptions::default()).await?;
let mut rx = session.subscribe();
while let Ok(event) = rx.recv().await {
match event {
AgentEvent::Text { text, .. } => print!("{text}"),
AgentEvent::SessionEnd { .. } => break,
_ => {}
}
}
Ok(())
}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.
- 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.
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)
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.
| 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 |
| Grok CLI | — (adapter-registry path, not a transport-core pipe client) | live through the workbench (node adapter registry) | not active | Live-verified in the workbench stack 2026-08-19: operator spawn/observe/stop through harness → c2 → node; resume gap tracked |
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.
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 |
| Grok CLI / qwen-code | per their vendors' instructions |
- 0.3.0 — the workspace era. The repo is an agent workbench
(node / c2 / harness / TUI over the transport core), not a single-crate
library; the vendored
portable-ptyfork is replaced by the in-housegate4agent-pty(std-only, zero external PTY dependencies: Windows ConPTY + a unix macOS/Linux backend, verified on all three OSes); first crates.io wave published at 0.3.0:gate4agent-pty,gate4agent-types,gate4agent-adapters,gate4agent-catalog,gate4agent,g4a(the remaining workbench crates publish after the planned crate consolidation). - 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.
- 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.
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.
-
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.
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 |
MIT