Metadata-Version: 2.4
Name: wanzi-codex
Version: 0.7.0
Summary: Drive Cursor's latest mixed-model mode straight from codex / claude / cursor: one install, then just talk.
License-File: LICENSE
Requires-Python: >=3.11
Requires-Dist: fastapi>=0.115.0
Requires-Dist: httpx>=0.27.0
Requires-Dist: mcp
Requires-Dist: pydantic>=2.10.0
Requires-Dist: uvicorn>=0.30.0
Requires-Dist: wanzi-mcp>=1.0.12
Requires-Dist: websocket-client>=1.7.0
Provides-Extra: test
Requires-Dist: pytest-asyncio>=0.23; extra == 'test'
Requires-Dist: pytest>=8.0; extra == 'test'
Description-Content-Type: text/markdown

# wanzi-codex

**Drive Cursor's latest mixed-model mode straight from codex / claude / cursor — one install, then just talk.**

The package ships an MCP server (`wanzi-codex-mcp`) that exposes one zero-friction primitive: `cursor_enable_mixed`. Once it's registered with your AI client (codex CLI, Claude Desktop, Claude Code, or Cursor itself), you stop touching Cursor's UI. The MCP arms the latest roster (newest Claude Opus + GPT) past the historical N=8 cap, qualifies any folder for Cursor's GitHub-gated multi-model option, launches Cursor with a debug port, and toggles "Use Multiple Models" on via CDP — no Cursor file modified, no manual clicks.

---

## Quick start (two commands, then forever after just talk)

```powershell
pip install wanzi-codex
wanzi-codex install all
```

That second command registers the MCP server into every supported AI client config in one shot — Cursor `mcp.json`, Claude Desktop `claude_desktop_config.json`, Claude Code `~/.claude.json`, Codex CLI `~/.codex/config.toml`. Idempotent; pass `--force` to overwrite. Per-target subcommands also exist: `install cursor-mcp` / `install claude-mcp` / `install codex-mcp`.

**You do NOT need to run a separate `wanzi-codex-provider` daemon.** The MCP server embeds the provider in-process via ASGI by default — state lives at `~/.wanzi-codex/provider/`. Set `WANZI_PROVIDER_URL` only if you specifically want to point at an external HTTP provider.

Then in codex / claude / cursor just say:

> use cursor_enable_mixed for this project

The tool defaults `project` to the current working directory, so the agent rarely needs more than that one sentence. The full signature is:

```python
cursor_enable_mixed(
  project: str | None = None,    # defaults to cwd
  github_url: str | None = None, # qualify a non-GitHub folder; omit if already github-backed
  port: int = 9333,              # CDP debug port to attach
  count: int = 12,               # mixed-roster size to pre-arm
)
```

What it does end-to-end:

1. Ensures the folder satisfies Cursor's GitHub-backed gate (optional `github_url` for non-GitHub repos).
2. Pre-arms the latest mixed roster on disk (newest Claude Opus + GPT-5.x family, all N buckets normalized) when Cursor is not yet running.
3. Launches Cursor with `--remote-debugging-port`, returning the spawned `pid` for clean shutdown.
4. Drives Cursor's "Use Multiple Models" toggle via CDP and verifies the trigger text.

You can also drive the whole flow from a single CLI, no MCP needed:

```powershell
wanzi-codex-cursor-mixed --project "C:\path\to\proj" --ensure-github-remote https://github.com/<you>/<repo>.git
```

---

## What ships in the wheel

| Console script | Role |
|---|---|
| `wanzi-codex` / `wz` | JSON-first CLI for diagnostics, repro, manual install commands. |
| `wanzi-codex-mcp` | Stdio MCP server (registered into codex / claude / cursor by `install all`). |
| `wanzi-codex-provider` | Standalone HTTP provider (optional — MCP embeds it by default). |
| `wanzi-codex-cursor-mixed` | One-shot CLI that launches Cursor and toggles mixed mode on. |
| `wanzi-codex-cursor-enforce` | Watchdog that re-arms the latest mixed roster across Cursor relaunches. |

| Python package | Purpose |
|---|---|
| `wanzi_codex_provider` | FastAPI provider, JSONL store, Cursor runtime adapter, CDP driver, state.vscdb writers, model ranking. |
| `wanzi_codex_mcp` | FastMCP server that delegates to the provider (in-process or HTTP). |
| `wanzi_codex_core` | Shared `ProviderClient` + legacy Wanzi HTTP client + readiness/waiter. |
| `wanzi_codex_cli` | argparse CLI with `install all` / `install codex-mcp` / `install claude-mcp` / `install cursor-mcp`. |

---

## How model selection actually reaches Cursor's backend

For anyone reverse-engineering or extending the bridge: when you pick a model in Cursor's UI, the choice is persisted into Cursor's SQLite `state.vscdb`, then materialized into each composer's `modelConfig` before any backend request:

1. **UI pick** &rarr; `cursor/lastSingleModelPreference` (single-model) or `cursor/bestOfNEnsemblePreferences` + `cursor/bestOfNCountPreference` (multi-model roster).
2. **Composer creation** &rarr; the preference materializes into `composerData.modelConfig.modelName` (single) or `subComposerIds[]` (each sub-composer carrying its own `modelConfig.modelName`).
3. **Message send** &rarr; the composer's `modelConfig` is included in the agent/stream request body sent to Cursor's backend.

The bridge intercepts at stage 1 by writing the preference keys directly (`_apply_selection_via_cursor_state`), so the composer materializes whatever roster you arm. `cursor_enable_mixed` combines this on-disk arming with a live CDP toggle so the UI also reflects the change without a window reload.

---

## Tests

```powershell
python -m pytest -q
```

365 passed, 2 skipped at the time of this README (the 2 skips are live wanzi-desktop integration tests gated behind `WANZI_CODEX_LIVE_TESTS=1`, not missing features). Live integration tests are skipped unless `$env:WANZI_CODEX_LIVE_TESTS = "1"`.


## Multi-model fan-out & orchestration (v0.3)

`codex_wanzi` / `cursor_enable_mixed` take you from one sentence to a whole
swarm. Say it in codex/claude and the agent maps it to parameters:

| You say | Parameters |
|---|---|
| "open 20 sessions" | `sessions=20` |
| "16x claude" / "16 claude, no gpt" | `count=16, families=["claude"]` |
| "16x claude-opus-4.8" | `models=["claude-opus-4.8"], count=16` |
| "...retry failures until 2 succeed" | `auto_retry=True, min_success=2` |
| "let them debate / co-author / just review" | `collaboration_mode=agent_chat_room\|co_authoring\|review_only` |

One Cursor window is launched (debug port); **each session is a composer chat
inside that single window** (never N windows); every session is driven by the
armed roster with "Use Multiple Models" on. `families` filters by model family,
`models` names exact models (family words resolve to the strongest model in that
family), and `count` is the Best-of-N "x". Verified live: `16× Opus 4.8 Max
Fast` armed across 3 sessions with GPT excluded.

### Auto-retry to quorum

`cursor_auto_retry` (and `auto_retry=True`) watches the bottom-right failure
toasts (`.notifications-toasts`) and clicks "retry" until at least `min_success`
model attempts stop failing, then stops so the survivors keep working. Override
the retry-button text with `WANZI_CODEX_RETRY_TEXT`.

### Auto-dismiss the two unattended-run blockers

`cursor_dismiss_popups` (and `auto_dismiss=True`, default on for
`cursor_enable_mixed` / `codex_wanzi`) handles the two Cursor dialogs that
otherwise stall a background run:

- **"Update recommended"** -> clicks Close (NOT Update) so the agent is not
  redirected into an installer flow mid-run.
- **"Resubmit for all N agents?"** -> clicks Resubmit so the multi-agent
  fan-out the user already asked for actually fires. Set
  `accept_resubmit=False` to cancel instead (Escape).

Detection is text-anchored (English + 中文) so it survives Cursor's obfuscated
class-name changes between builds; up to `max_rounds` scan/click cycles run
because closing one popup can reveal the next.

### Auto off before the mixed-model picker

Cursor only lists individual models (and the "Use Multiple Models" option)
once the picker's **Auto** toggle is off — while Auto is on the dropdown
shows just "Auto". `enable_multi_model_via_cdp` / `open_sessions_via_cdp` now
detect a real Auto toggle inside the open picker popover (a switch /
checkbox control, never the trigger label) and switch it off first, then
arm the roster. The result
reports `auto_off` so callers can see whether the step fired.

### Live deliver: type the task in, wait for a done marker

`CdpDeliver` is the optional last-mile that turns the orchestrator from
"open one session per task" into "send the work packet and wait until the
model says it is done". Plug it in via:

```python
from wanzi_codex_provider import cursor_cdp
from wanzi_codex_provider.orchestrator.executor import CursorFanoutExecutor

executor = CursorFanoutExecutor(
    runtime=runtime, cdp=cursor_cdp, port=9333,
    deliver=cursor_cdp.CdpDeliver(port=9333, timeout=300.0, poll_seconds=2.0),
)
```

Or from codex with the MCP tool: `orchestrator_run(plan=..., deliver=True,
deliver_timeout=300.0)`. It types each task's title + context packet into the
focused composer, appends a unique done-marker hint, and polls
`document.body.innerText` until the marker appears (or the timeout elapses).
Sessions stay isolated (a fresh CDP connection per task) so one slow task
cannot block the others.

### Enterprise orchestration (`wanzi_codex_provider.orchestrator`)

Turns "N redundant attempts" into "N specialists on a decomposed plan":

- **DAG scheduler** - a `ProjectPlan` of scope-bounded tasks + interface
  contracts; runs dependency-ordered parallel waves and never lets two writers
  touch overlapping scopes at once.
- **Global ledger + contract registry** - cross-session decisions / conflicts,
  and an interface change is broadcast to exactly the consuming scopes.
- **Roles + scope-bounded writes** - one writer per scope, the rest review-only.
- **Verification gates** - real test/lint/build must pass before a merge.
- **Persistent memory** - decisions / contracts / lessons carry across sessions.
- **Audit** - per-model contribution / acceptance / conflict summary.

Preview a decomposition from codex with the `orchestrator_preview` MCP tool
(validates the DAG and shows the execution waves + per-task writer/reviewer
assignments).

### "Can I run Cursor without opening the UI?"

Cursor's own answers (confirmed via `cursor --help` on Cursor 2.x):

- `cursor agent` — headless agent in the terminal (subcommand). Best for
  scripted runs that do not need the IDE.
- `cursor --chat` — standalone chat window without the full IDE; still a UI
  process but skips the editor / file tree / sidebar.
- `cursor serve-web --port=N` — runs the editor as a web server; the desktop
  app stays closed and you reach it from a browser instead.
- `cursor --suppress-popups-on-startup` — pre-flight gate that hides the
  "Update recommended" dialog on a fresh launch. `wanzi-codex` now passes
  this by default in `build_launch_args` so the popup never blocks the
  unattended path; pair it with `cursor_dismiss_popups` for any dialog that
  shows up later.

There is no public Cursor HTTP API for chat completions yet, so completely
bypassing Cursor (talking to Claude / OpenAI directly) requires their
respective SDKs; the value-add of this package is doing it through Cursor
because that is where the user's models, settings, and account are.

### Cross-platform

Cursor executable discovery, the `state.vscdb` path, and the new-composer
shortcut are all OS-aware (Windows/Linux use Ctrl, macOS uses Cmd).
