gate4agent 0.4.5

Transport library for CLI AI agents — first tier: Claude Code, Codex, Kimi, Grok. Pipe, PTY, ACP (Agent Client Protocol), and Daemon transports.
Documentation
# gate4agent

gate4agent is a library that drives CLI coding agents — Claude Code, Codex,
Kimi, Grok — over PTY, pipe, ACP, and daemon transports behind one API:
spawn, stream, resume; see [Transport core](#transport-core) below. On top
of that transport core it also carries the session runtime a node embeds to
run those agents as managed sessions: an adapter/tool catalog, a session
kernel, the native runtime, and per-CLI process hooks and history readers.
The node/c2/harness/TUI stack that used to live in this repository is now
`hatchery`, built on this library.

## Provider tiers

| 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 |

## Repository layout

The root `src/` tree is the **transport core** — spawning, streaming,
resuming, and owning interactive CLI-agent subprocesses through one API,
published as the `gate4agent` crate. See [Source layout](#source-layout)
below for its internal module structure.

`crates/` holds the session runtime that a node embeds on top of the
transport core, plus the in-house PTY backend:

- `gate4agent-pty` — the in-house PTY backend (std-only, zero external PTY
  dependencies: Windows ConPTY + a unix macOS/Linux backend).
- `gate4agent-types` — shared wire/data types.
- `gate4agent-adapters` — the adapter registry (Grok, qwen-code, and other
  CLIs wired outside the transport core's own pipe/PTY clients).
- `gate4agent-catalog` — the CLI interop reference registry.
- `gate4agent-engine`, `gate4agent-kernel`, `gate4agent-handle` — the
  session runtime substrate: `catalog` → `kernel` → `runtime-native` call
  down into the root crate's transports.
- `gate4agent-tool-protocol`, `gate4agent-tool-engine` — tool-call wire
  types and execution.
- `gate4agent-shell-history`, `-shell-capabilities`, `-shell-hooks`,
  `-shell-managed-hooks`, `-shell-one-shot`, `-shell-native` — per-CLI
  shell integration: history readers, capability probing, hook wiring.
- `gate4agent-runtime-native` — the native runtime a node embeds to run a
  managed session end to end.
- `gate4agent-provider-ports` — the provider-facing port/trait boundary.
- `gate4agent-testkit` — authentication-free provider fixtures and the
  Windows headless test supervisor (`windows-headless-supervisor`).
- `g4a` — a placeholder crate reserving the crates.io name.

**All new development happens in `crates/`; the root library changes only
when the transport core itself does.**

## 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>
```

All builds share the workspace's own `target/`. A per-run `--target-dir` is
a full copy of the dependency build and they pile up fast; when two builds
overlap, Cargo's build lock simply makes the second wait. Tests gated by
`require_windows_headless_supervisor_for_test()` reject themselves outright if
run any other way.

## 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 |
| **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.

### Quick start

```rust
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.

### 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 |
| **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.

## 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` |
| Grok CLI / qwen-code | per their vendors' instructions |

## Versioning

- **0.4.1** — ordinary patch bump after websession→master merge (bridge skeleton, Claude ProviderHome `--settings` network overlay, dig2 station probe/lease). Same first crates.io wave crates; **not published to crates.io from this tip** (owner publish later).
- **0.4.0** — breaking: non-fleet vendors cut (Gemini, OpenCode, Qwen
  parsers, pipe builders and history readers removed); the `rusqlite`
  dependency is gone, so the transport core no longer pins
  `libsqlite3-sys` against consumers on `rusqlite` 0.32+; the harness
  mailbox moved out into its own service. Same first crates.io wave:
  `gate4agent-pty`, `gate4agent-types`, `gate4agent-adapters`,
  `gate4agent-catalog`, `gate4agent`, `g4a`.
- **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-pty` fork is replaced by the in-house
  `gate4agent-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`, `AgentEvent` renamed, `PipeSession` removed, OpenClaw fantasy transport
- **0.2.1** — cleanup: OpenClaw removed (was never functional), `PipeSession` restored for 0.1.x compatibility, `TransportSession` is now a thin router over `PipeSession`
- **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-auto` replaces removed `--ask-for-approval`)
- **0.2.5** — live integration tests: fixed Codex flags, OpenCode `run` subcommand, Gemini `-p` flag, Windows `cmd /C` quoting; 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.node` is 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_item` with role=user), old `.json` session format removed (no cwd field = leaked into all projects).
- **0.2.25–0.2.28** — `CliCapabilities` API: `ModelInfo`, `PermissionModeInfo`, `CliFeatures` per CLI tool. Gemini `--model` flag 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`, OpenCode `opencode.json`). Model picker enrichment at runtime.
- **0.2.30** — **Probe + Context tracking**: `probe_all()` discovers installed CLIs with caching (`~/.gate4agent/probe-cache.json`). `ContextTracker` accumulates tokens per session, computes remaining context. Extended `TurnComplete` with `cache_read_tokens`, `cache_write_tokens`, `reasoning_tokens`, `context_window`, `is_cumulative`. Codex `event_msg/token_count` parser (cumulative totals + `model_context_window`). Claude/Gemini/OpenCode parsers extract cache and reasoning tokens. Fixed Claude model IDs (4 → 4.6). Removed `image_to_prompt_reference()` and `PipeSession::tool()`.
- **0.2.31** — **ContextTracker wired into runtime**: `AgentInstance` now holds a `ContextTracker`, updated on every `TurnComplete` event. `AgentRenderSnapshot` gains `context_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`/`haiku` alias entries from Claude.
- **0.2.36** — **feat: cure runs lazily on first history load or session start**. `ensure_cure_once()` populates `~/.gate4agent/models.json` from OpenCode cache before `tool.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, so `context_percent` shows 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. Codex `event_msg` normalizes `input_tokens` by subtracting `cached_input_tokens` to avoid double-counting. New `cure` module: runtime model discovery from OpenCode disk cache (`~/.cache/opencode/models.json`) with optional OpenRouter fallback (`cure-network` feature). 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 `ContextTracker` from model capabilities at `SessionStart` (matches model ID → `context_window`). Reset tracker on new session spawn so stale data doesn't persist across sessions.

The phased roadmap (P1–P4: one app↔harness protocol, light-harness
extraction, full node-surface relay, cowork) was for the node/c2/harness/TUI
stack and now lives in `hatchery`'s ROADMAP.md, not here.

## Migration guide

### 0.2.0 → 0.2.1

- **OpenClaw removed** — `CliTool::OpenClaw` no longer exists. If you matched on it, delete that arm. OpenClaw was never functional (unverified daemon protocol, fictional acpx API surface).
- **`PipeSession` restored** — 0.1.x callers that used `PipeSession::spawn(config, prompt, options)` compile again. The `PipeSession` now includes SessionEnd synthesis (previously only in the 0.2.0 `pipe_runner`).
- **`TransportSession`** is now a thin wrapper over `PipeSession`. Its public API (`spawn`, `subscribe`, `session_id`, `send_prompt`, `kill`) is unchanged. Internal: no more `TransportHandle` enum, no dead `Pty` variant.
- **`DaemonNotRunning` / `DaemonProbeTimeout` error variants removed** — they were only reachable via OpenClaw. Remove any match arms for these.

### 0.2.18 → 0.2.19

- **`RpcSession` removed** — if you were using `gate4agent::rpc::RpcSession` or the top-level `gate4agent::RpcSession` / `RpcSessionOptions` / `RpcSessionError` re-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 `rpc` primitives unchanged** — `RpcRequest`, `RpcResponse`, `RpcError`, `RpcNotification`, `RpcId`, `HostHandler`, `MethodRouter`, `RejectAllHandler`, `PendingRequests`, `IdGen`, `classify_line` are all still exported. Only the `RpcSession` transport struct is gone.

### 0.1.x → 0.2.1

1. **Events**: `AgentEvent::Pipe*` → neutral names. Rename all match arms:
   - `PipeText` → `Text`
   - `PipeToolStart` → `ToolStart`
   - `PipeToolResult` → `ToolResult`
   - `PipeThinking` → `Thinking`
   - `PipeTurnComplete` → `TurnComplete`
   - `PipeSessionStart` → `SessionStart`
   - `PipeSessionEnd` → `SessionEnd`

2. **`PipeSession::spawn`** — signature unchanged: `PipeSession::spawn(config, prompt, options)`. Compiles directly.

3. **`SpawnOptions`**: new unified struct. Fields: `working_dir`, `prompt`, `resume_session_id`, `model`, `append_system_prompt`, `extra_args`, `env_vars`.

4. **`CliTool`** is 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