From a7c09a4ae467a09bdbe3a64875beb1ec24d32bf0 Mon Sep 17 00:00:00 2001 From: thuanlm215 Date: Wed, 12 Aug 2026 06:27:23 +0000 Subject: [PATCH 01/14] feat: add Grok CLI provider --- CHANGELOG.md | 7 +- README.md | 10 +- docs/agent-profile.md | 6 +- docs/grok-cli.md | 223 +++++++ docs/tool-restrictions.md | 27 +- examples/assign/README.md | 3 +- src/cli_agent_orchestrator/api/main.py | 1 + .../cli/commands/launch.py | 1 + src/cli_agent_orchestrator/models/provider.py | 1 + .../providers/grok_cli.py | 524 +++++++++++++++ .../providers/manager.py | 11 + .../services/terminal_service.py | 3 +- .../utils/tool_mapping.py | 19 + test/api/test_api_endpoints.py | 5 +- test/cli/commands/test_launch.py | 8 + test/e2e/conftest.py | 43 +- test/e2e/test_allowed_tools.py | 84 +++ test/e2e/test_assign.py | 32 + test/e2e/test_handoff.py | 83 +++ test/e2e/test_send_message.py | 14 + test/e2e/test_skills.py | 68 ++ test/e2e/test_supervisor_orchestration.py | 42 ++ .../fixtures/grok_cli_completed.ansi.txt | 10 + .../fixtures/grok_cli_completed.raw.ansi.txt | 1 + .../providers/fixtures/grok_cli_completed.txt | 17 + test/providers/fixtures/grok_cli_error.txt | 3 + .../fixtures/grok_cli_idle.raw.ansi.txt | 1 + test/providers/fixtures/grok_cli_idle.txt | 7 + test/providers/fixtures/grok_cli_login.txt | 9 + .../fixtures/grok_cli_permission.txt | 8 + .../fixtures/grok_cli_processing.txt | 9 + .../fixtures/grok_cli_second_turn.txt | 13 + .../fixtures/grok_cli_telemetry_banner.txt | 9 + test/providers/test_grok_cli_unit.py | 619 ++++++++++++++++++ test/providers/test_provider_manager_unit.py | 31 + test/services/test_terminal_service.py | 11 + test/test_constants.py | 14 + test/utils/test_tool_mapping.py | 50 ++ web/src/components/AgentPanel.tsx | 2 +- web/src/test/components.test.tsx | 3 +- 40 files changed, 2010 insertions(+), 22 deletions(-) create mode 100644 docs/grok-cli.md create mode 100644 src/cli_agent_orchestrator/providers/grok_cli.py create mode 100644 test/providers/fixtures/grok_cli_completed.ansi.txt create mode 100644 test/providers/fixtures/grok_cli_completed.raw.ansi.txt create mode 100644 test/providers/fixtures/grok_cli_completed.txt create mode 100644 test/providers/fixtures/grok_cli_error.txt create mode 100644 test/providers/fixtures/grok_cli_idle.raw.ansi.txt create mode 100644 test/providers/fixtures/grok_cli_idle.txt create mode 100644 test/providers/fixtures/grok_cli_login.txt create mode 100644 test/providers/fixtures/grok_cli_permission.txt create mode 100644 test/providers/fixtures/grok_cli_processing.txt create mode 100644 test/providers/fixtures/grok_cli_second_turn.txt create mode 100644 test/providers/fixtures/grok_cli_telemetry_banner.txt create mode 100644 test/providers/test_grok_cli_unit.py diff --git a/CHANGELOG.md b/CHANGELOG.md index 5eeeb65bc..dab40c391 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,12 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## [Unreleased] +### Added + +- Add the official xAI Grok Build CLI as the `grok_cli` provider, including + isolated per-terminal MCP configuration, native hard tool restrictions, + multi-turn TUI support, orchestration e2e coverage, and provider docs. + ### Fixed - tmux listing parse failures are retried once and reported as a distinct condition instead of surfacing as a bare `ValueError` that reads like "session not found" one layer up. libtmux 0.53.1+ zips `parse_output`'s fields with `strict=True`, so any short row (a pane or session vanishing mid-listing, or trailing fields tmux omits) raised `ValueError: zip() argument 2 is shorter than argument 1` — which propagated through `server.sessions`/`window.panes`, blocked launches outright, and left the pipe-liveness watchdog unable to tell a genuinely-gone session from a transient parse failure. Adds `TmuxLookupError` and routes the listing reads in `clients/tmux.py` through a single retry-and-classify wrapper; a failed `create_session` no longer leaves an orphaned tmux session that blocks relaunching the same name. Also caps `libtmux<0.53.1`, the last release that zips non-strict (caom-anv) @@ -842,4 +848,3 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - accept optional U+03BB (λ) after % in kiro and q CLIs (#44) - diff --git a/README.md b/README.md index 95fb01b79..dcf76624b 100644 --- a/README.md +++ b/README.md @@ -31,8 +31,9 @@ Install: [Codex CLI](docs/codex-cli.md), [Antigravity CLI](docs/antigravity-cli.md), [Hermes](docs/hermes.md), [Kimi CLI](docs/kimi-cli.md), [GitHub Copilot CLI](docs/copilot-cli.md), - [OpenCode CLI](docs/opencode-cli.md), or - [Cursor CLI](docs/cursor-cli.md) + [OpenCode CLI](docs/opencode-cli.md), + [Cursor CLI](docs/cursor-cli.md), or + [Grok Build CLI](docs/grok-cli.md) The focused provider guides contain installation, authentication, and provider-specific behavior. @@ -130,8 +131,9 @@ provider override while keeping the same sequence. [Codex CLI](docs/codex-cli.md), [Antigravity CLI](docs/antigravity-cli.md), [Hermes](docs/hermes.md), [Kimi CLI](docs/kimi-cli.md), [GitHub Copilot CLI](docs/copilot-cli.md), - [OpenCode CLI](docs/opencode-cli.md), and - [Cursor CLI](docs/cursor-cli.md). + [OpenCode CLI](docs/opencode-cli.md), + [Cursor CLI](docs/cursor-cli.md), and + [Grok Build CLI](docs/grok-cli.md). - [Security policy](SECURITY.md): vulnerability reporting and deployment guidance. diff --git a/docs/agent-profile.md b/docs/agent-profile.md index bc830b52a..e338b2d58 100644 --- a/docs/agent-profile.md +++ b/docs/agent-profile.md @@ -70,9 +70,9 @@ Provider support for pass-through fields differs. Use the focused guides for [Kiro CLI](kiro-cli.md), [Claude Code](claude-code.md), [Codex CLI](codex-cli.md), [Antigravity CLI](antigravity-cli.md), [Hermes](hermes.md), [Kimi CLI](kimi-cli.md), -[GitHub Copilot CLI](copilot-cli.md), [OpenCode CLI](opencode-cli.md), and -[Cursor CLI](cursor-cli.md) instead of relying on a duplicated compatibility -catalog here. +[GitHub Copilot CLI](copilot-cli.md), [OpenCode CLI](opencode-cli.md), +[Cursor CLI](cursor-cli.md), and [Grok Build CLI](grok-cli.md) instead of +relying on a duplicated compatibility catalog here. ## Tool restrictions diff --git a/docs/grok-cli.md b/docs/grok-cli.md new file mode 100644 index 000000000..b62409fd8 --- /dev/null +++ b/docs/grok-cli.md @@ -0,0 +1,223 @@ +# Grok Build CLI Provider + +## Overview + +The `grok_cli` provider runs the official [xAI Grok Build +CLI](https://docs.x.ai/build) as a long-lived, multi-turn agent in a tmux +window. Community Grok command-line clients and direct xAI API wrappers are +not supported by this provider. + +CAO launches Grok's interactive TUI with inline rendering, adds the selected +agent profile and CAO skill catalog as rules, and exposes CAO orchestration +tools through MCP. Grok's own subagent system is disabled so `assign` and +`handoff` remain the only agent-delegation paths in a CAO session. + +The integration was developed and tested with Grok Build `1.0.0` and the +`grok-4.5` model. Newer Grok versions may change TUI markers or native tool +names; report status or extraction regressions with `grok --version` output. + +## Prerequisites + +- tmux 3.3 or later +- The official `grok` executable on `PATH` +- An authenticated Grok account or an xAI API key + +Install the CLI using xAI's installer: + +```bash +curl -fsSL https://x.ai/cli/install.sh | bash +grok --version +``` + +Authenticate once in a normal terminal before launching it through CAO: + +```bash +grok login +grok models +``` + +For a remote machine without a browser, use `grok login --device-auth`. Grok +also accepts an API key from `XAI_API_KEY`: + +```bash +export XAI_API_KEY="xai-..." +grok models +``` + +Do not put an API key in an agent profile or commit it to a repository. + +## Quick Start + +Start `cao-server`, then install and launch a profile for Grok: + +```bash +cao install developer --provider grok_cli +cao launch --agents developer --provider grok_cli +``` + +Profile instructions use the normal Markdown format. The body is appended to +Grok's native system prompt with `--rules`, together with the runtime CAO skill +catalog. This preserves Grok's coding-agent behavior while applying the +profile's role and protocols. + +Set a default model in profile frontmatter: + +```yaml +--- +name: grok_developer +description: Developer backed by Grok Build +provider: grok_cli +model: grok-4.5 +role: developer +--- + +Implement the requested change and verify it. +``` + +An explicit launch override takes precedence: + +```bash +cao launch --agents grok_developer --provider grok_cli --model grok-4.5 +``` + +Use `grok models` to discover model IDs available to the authenticated +account. + +## Runtime Behavior + +The command has this shape: + +```text +grok --no-alt-screen --always-approve --no-subagents \ + [--model MODEL] [--rules RULES] [--deny RULE ...] +``` + +- `--no-alt-screen` keeps the rendered conversation observable by CAO. +- `--always-approve` prevents ordinary tool approval prompts from blocking + unattended orchestration. +- Native `--deny` rules still override auto-approval and provide hard tool + restrictions. +- `--no-subagents` prevents Grok-native workers from bypassing CAO roles, + permissions, callbacks, or terminal accounting. +- A single Enter submits bracketed-paste input. `/quit` exits the session. + +The empty `❯` composer may remain visible while Grok is working. CAO therefore +prioritizes current `Waiting for response…` and `Esc:cancel` markers over the +composer. A settled turn has a `Worked for ...` boundary, which CAO also uses +to extract only the latest response in a multi-turn session. + +## MCP Isolation + +CAO creates a private Grok home for every terminal and launches Grok with +`GROK_HOME` pointing to it. The terminal root is mode `0700`; CAO writes its +generated config atomically with mode `0600`. It does not run `grok mcp add` +and does not modify the user's `~/.grok/config.toml`. + +The isolated config contains the profile's MCP servers. CAO injects the +terminal-specific `CAO_TERMINAL_ID` into stdio MCP server environments so +`cao-mcp-server` can route `assign`, `handoff`, and `send_message` correctly. +Existing login state is reused without copying credential contents into CAO +logs or the repository. Generated state is removed when the terminal is +cleaned up. + +A newly isolated home can show Grok's `Help improve Grok` telemetry choice. +The banner is non-blocking and is ignored by CAO's status and response +extraction logic. + +## Tool Restrictions + +Grok is a hard-enforcement provider. CAO translates missing capabilities into +native Grok deny rules: + +| CAO capability | Grok tools denied when absent | +|---|---| +| `execute_bash` | `Bash` | +| `fs_read` | `Read`, `NotebookRead` | +| `fs_write` | `Edit`, `Write`, `NotebookEdit` | +| `fs_list` | `Grep`, `Glob` | +| `web_fetch` | `WebFetch`, `WebSearch`, with web search disabled | + +`allowedTools: ["*"]` adds no restrictive deny rules. For a restricted role, +deny rules are applied alongside `--always-approve`; auto-approval does not +turn a denied tool back on. The provider also always passes `--no-subagents` +to close the native-subagent escape path. + +`@cao-mcp-server` follows CAO's current shared MCP limitation: it records the +profile's orchestration intent, but individual MCP tools are not blocked at +the provider level. See [Tool Restrictions](tool-restrictions.md). + +## Assign and Handoff Example + +Install all profiles for this provider before running the full orchestration +example: + +```bash +cao install examples/assign/data_analyst.md --provider grok_cli +cao install examples/assign/report_generator.md --provider grok_cli +cao install examples/assign/analysis_supervisor.md --provider grok_cli +cao launch --agents analysis_supervisor --provider grok_cli --auto-approve +``` + +`--auto-approve` skips CAO's launch confirmation but retains role-based tool +restrictions. Do not substitute `--yolo` when validating supervisor safety. + +## Known Limitations + +- The provider targets Grok Build's interactive TUI and currently requires the + tmux backend. Headless `-p` and ACP modes are not CAO transports. +- TUI parsing is calibrated against Grok Build 1.0.0. A future layout change + may require updated status and extraction fixtures. +- CAO reuses existing Grok authentication. Complete interactive login first; + CAO does not drive account or device-code login screens. +- Per-tool MCP gating is not available. `@cao-mcp-server` does not selectively + hide `assign`, `handoff`, or `send_message`. +- Grok-created non-secret files inside the private `0700` home can use their + own modes; the `0600` guarantee applies to CAO-authored config files. + +## Troubleshooting + +### Login or model errors + +Run `grok login` and `grok models` outside CAO. On a headless host, use +`grok login --device-auth` or set `XAI_API_KEY`. If a profile selects an +unavailable model, replace it with an ID printed by `grok models`. + +### MCP tools are missing or time out + +Confirm `cao-mcp-server` is installed in the same environment as `cao-server`. +Inspect the Grok terminal for an MCP startup error, then recreate the terminal +so CAO regenerates its isolated config and terminal ID. + +### Terminal remains processing + +Attach to the tmux session and check whether Grok still shows +`Waiting for response…` or `Esc:cancel`. If Grok is visibly settled but CAO +does not report completion, include a scrubbed pane capture and `grok --version` +in the bug report. + +### Permission or telemetry prompt is visible + +The telemetry banner is non-blocking. An actual permission picker should be +reported as waiting for user input; answer it in tmux. Restricted tool calls +should be denied automatically rather than prompting. + +### Broken rendering + +Use tmux 3.3 or later and a normal color terminal such as +`TERM=xterm-256color` or `TERM=tmux-256color`. Verify `grok --no-alt-screen` +works in a standalone tmux pane. + +## Validation + +```bash +# Provider unit tests +uv run pytest test/providers/test_grok_cli_unit.py -v -o "addopts=" + +# All Grok lifecycle, permissions, skills, and orchestration e2e tests +uv run pytest -m e2e test/e2e/ -k Grok -v -o "addopts=" + +# Maintainer-required three-analyst workflow +uv run pytest -m e2e \ + test/e2e/test_supervisor_orchestration.py \ + -k GrokCliSupervisorOrchestration -v -o "addopts=" +``` diff --git a/docs/tool-restrictions.md b/docs/tool-restrictions.md index a33184d96..37384d79f 100644 --- a/docs/tool-restrictions.md +++ b/docs/tool-restrictions.md @@ -197,15 +197,15 @@ The confirmation prompt is a **review gate** — it shows the resolved role and CAO defines a universal tool vocabulary (`execute_bash`, `fs_read`, `fs_write`, `fs_list`). However, not all providers understand this vocabulary natively. There are two categories: -**Providers that need translation** — Claude Code and Copilot CLI each have their own native tool names (e.g., Claude Code calls bash execution `Bash`, Copilot calls it `shell`). CAO uses an internal `TOOL_MAPPING` to translate the CAO vocabulary to provider-native names, then computes which native tools to block and passes them as CLI flags (e.g., `--disallowedTools Bash`, `--deny-tool shell`). +**Providers that need translation** — Claude Code, Copilot CLI, and Grok Build CLI each have their own native tool names (e.g., Claude Code and Grok call bash execution `Bash`, while Copilot calls it `shell`). CAO uses an internal `TOOL_MAPPING` to translate the CAO vocabulary to provider-native names, then computes which native tools to block and passes them as CLI flags (e.g., `--disallowedTools Bash`, `--deny-tool shell`, or `--deny Bash`). -| CAO Tool | Claude Code | Copilot CLI | -|----------|-------------|-------------| -| `execute_bash` | `Bash` | `shell` | -| `fs_read` | `Read` | `read` | -| `fs_write` | `Edit`, `Write` | `write` | -| `fs_list` | `Glob`, `Grep` | `list`, `grep` | -| `web_fetch` | `WebFetch`, `WebSearch` | (not mapped) | +| CAO Tool | Claude Code | Copilot CLI | Grok Build CLI | +|----------|-------------|-------------|----------------| +| `execute_bash` | `Bash` | `shell` | `Bash` | +| `fs_read` | `Read` | `read` | `Read`, `NotebookRead` | +| `fs_write` | `Edit`, `Write` | `write` | `Edit`, `Write`, `NotebookEdit` | +| `fs_list` | `Glob`, `Grep` | `list`, `grep` | `Grep`, `Glob` | +| `web_fetch` | `WebFetch`, `WebSearch` | (not mapped) | `WebFetch`, `WebSearch` + disabled web search | **Providers that accept CAO vocabulary directly** — Kiro CLI accepts `allowedTools` in the agent JSON at install time, using the same vocabulary as CAO. No translation needed. Kimi CLI and Codex use system prompt instructions to enforce restrictions. For all three, CAO passes the `allowedTools` list directly without translation — so no `TOOL_MAPPING` entry exists for them, and none is needed. @@ -251,6 +251,7 @@ As described in [How Tool Restrictions Are Enforced](#how-tool-restrictions-are- | **Kiro CLI** | Hard | `allowedTools` in agent JSON at install time | | **Copilot CLI** | Hard | `--deny-tool` flags override `--allow-all` | | **OpenCode CLI** | Hard | `permission:` YAML frontmatter enforced natively at install time | +| **Grok Build CLI** | Hard | Native `--deny` rules override `--always-approve`; native subagents are disabled | | **Kimi CLI** | Soft | Security system prompt only | | **Codex** | Soft | Security system prompt only | | **Antigravity CLI** | Soft | Security system prompt only | @@ -280,6 +281,16 @@ claude --dangerously-skip-permissions --disallowedTools Bash --disallowedTools E copilot --allow-all --deny-tool shell --deny-tool write ``` +**Grok Build CLI** — Adds native `--deny` rules alongside auto-approval and +disables Grok-native subagents: + +```bash +grok --always-approve --no-subagents --deny Bash --deny Edit --deny Write +``` + +See the [Grok Build CLI provider guide](grok-cli.md#tool-restrictions) for the +complete mapping and isolation behavior. + **Kimi CLI / Codex** — Prepends to the system prompt: ``` You may ONLY use these tools: @cao-mcp-server, fs_read, fs_list diff --git a/examples/assign/README.md b/examples/assign/README.md index de41b8a8c..ba3164600 100644 --- a/examples/assign/README.md +++ b/examples/assign/README.md @@ -181,6 +181,7 @@ cao launch --agents analysis_supervisor --provider codex cao launch --agents analysis_supervisor --provider copilot_cli cao launch --agents analysis_supervisor --provider cursor_cli cao launch --agents analysis_supervisor --provider kimi_cli +cao launch --agents analysis_supervisor --provider grok_cli ``` ## Usage @@ -327,7 +328,7 @@ T=33s: Present final report ## E2E Testing -The `data_analyst` and `report_generator` profiles from this directory are used in the E2E test suite to validate assign and handoff flows across all providers (codex, claude_code, kiro_cli, kimi_cli). +The `data_analyst` and `report_generator` profiles from this directory are used in the E2E test suite to validate assign and handoff flows across supported providers, including `grok_cli`. ```bash # Install profiles for E2E testing diff --git a/src/cli_agent_orchestrator/api/main.py b/src/cli_agent_orchestrator/api/main.py index 07aee2cee..489135169 100644 --- a/src/cli_agent_orchestrator/api/main.py +++ b/src/cli_agent_orchestrator/api/main.py @@ -2237,6 +2237,7 @@ async def list_providers_endpoint() -> List[Dict]: "opencode_cli": "opencode", "cursor_cli": "agent", "antigravity_cli": "agy", + "grok_cli": "grok", } result = [] for provider, binary in provider_binaries.items(): diff --git a/src/cli_agent_orchestrator/cli/commands/launch.py b/src/cli_agent_orchestrator/cli/commands/launch.py index 271cd18c3..02ebeb00c 100644 --- a/src/cli_agent_orchestrator/cli/commands/launch.py +++ b/src/cli_agent_orchestrator/cli/commands/launch.py @@ -29,6 +29,7 @@ "codex", "copilot_cli", "cursor_cli", + "grok_cli", "hermes", "kimi_cli", "kiro_cli", diff --git a/src/cli_agent_orchestrator/models/provider.py b/src/cli_agent_orchestrator/models/provider.py index ef9e8e5cc..0194ad330 100644 --- a/src/cli_agent_orchestrator/models/provider.py +++ b/src/cli_agent_orchestrator/models/provider.py @@ -13,5 +13,6 @@ class ProviderType(str, Enum): HERMES = "hermes" CURSOR_CLI = "cursor_cli" ANTIGRAVITY_CLI = "antigravity_cli" + GROK_CLI = "grok_cli" # Credentials-free mock provider for tests/CI (no real CLI binary). MOCK_CLI = "mock_cli" diff --git a/src/cli_agent_orchestrator/providers/grok_cli.py b/src/cli_agent_orchestrator/providers/grok_cli.py new file mode 100644 index 000000000..d99f0d0f3 --- /dev/null +++ b/src/cli_agent_orchestrator/providers/grok_cli.py @@ -0,0 +1,524 @@ +"""Official xAI Grok Build CLI provider implementation. + +Observed with ``grok 1.0.0 (3cd0d0cbce) [stable]`` in ``--no-alt-screen`` +mode. The empty composer remains visible while a turn is running, so status +detection gives the live ``Waiting for response…`` / ``[stop]`` / +``Esc:cancel`` markers priority. Completed turns end at ``Worked for