Files
dev/docs/integration-zed.md
T
zeekayandHanzo Dev d3d1dfa1d3 dev is ours: the Hanzo Cloud by default, a binary named dev, our namespaces
hanzoai/dev went public defaulting to someone else's cloud. Four things were
wrong, and all four now have a test that fails if they come back.

DEFAULT PROVIDER AND MODEL. built_in_model_providers() gains `hanzo` -- the
Hanzo Cloud at api.hanzo.ai/v1, Responses wire, bearer from HANZO_USER_KEY --
and it is the default. The default model is zen5-coder. This was not cosmetic:
the old default is not in the gateway catalog, so it 400s against our own
cloud. Measured, not assumed:

  POST api.hanzo.ai/v1/responses model=gpt-5-codex -> 400 "model is not
      available. Use GET /v1/models to list available models"
  POST api.hanzo.ai/v1/responses model=zen5-coder  -> 200, clean output

Our own CLI already documented the bug from the other side -- dev.rs says
"Name the model too -- codex's built-in default is not in the gateway catalog
and would 400" and passes `-c model=` on every launch to work around it. It
also asserts that on the native gateway NO model_provider override is needed,
which is only true once `hanzo` is the built-in default. It is now.

zen5-coder over enso because dev is a coding agent and because enso returns
system-prompt text on the Responses wire that dev actually speaks; zen5-coder
returns the answer. Model ids were read from GET /v1/models with a bearer, not
guessed. Other providers stay fully configurable -- this is the default, not
the only choice.

THE LOGIN COMMAND. Messages said `code login` / `codex login`. The real command
is `hanzo auth login` (`hanzo login` is not a command -- it is swallowed as an
AI task). It now lives once, as auth::LOGIN_COMMAND, so no message can drift
into naming a command that does not exist. References to this binary's own
subcommand became `dev login`.

A BINARY NAMED dev. HEAD built `code` and `codex` and nothing named `dev`, so a
release cut today would ship no `dev` -- and `hanzo code` launches a bare `dev`
off PATH, so every new install would break. `dev` is now the canonical bin,
declared first; `code` remains as an alias to the same program, never the
reverse. The CLI identifies as `dev` / "Hanzo Dev", not "OpenAI Codex".

OUR NAMESPACES. The pipeline published to @just-every/* on npm and pushed
formulae to just-every/homebrew-tap. Both of ours already exist and are already
populated at v0.6.91 -- @hanzo/dev plus all five @hanzo/dev-<target> packages,
and hanzoai/homebrew-tap's Formula/hanzo-dev.rb, which installs `dev` and
expects assets named dev-<triple>. Nothing here is squatted; this is our own
distribution, reclaimed. Assets are renamed to match the formula.

Two live supply-chain bugs fell out of that, both worse than a naming problem:
postinstall.js (runs on every npm install) and the tui auto-updater fetched
from a third party's releases -- the updater also told our users to
`npm install -g @just-every/code@latest`. Worse, a shadowed `isWSL` const put
the two fast paths in the temporal dead zone, so their catch blocks swallowed
the error and EVERY install fell through to that download: the five published
platform packages were never used, and the third-party fetch was not an edge
case but the only path that ran. Fixed and exercised in a sandbox across all
three paths. codex-cli/.pack/ -- a committed `npm pack` artifact CI
regenerates, and the reason the bad URL had two homes -- is now untracked.

DURABILITY, because upstream-merge.yml merges openai/codex every 30 minutes.
A one-time edit to a default is a suggestion that survives until the next
merge. Two mechanisms:

  - .github/merge-policy.json prefer_ours_globs listed only codex-rs/** while
    the workspace that SHIPS is code-rs/**, so it protected nothing that
    ships. The identity-bearing code-rs files are now listed.
  - code-rs/core/tests/hanzo_identity.rs asserts all four properties -- the
    provider, a Zen default, a bin named dev, and no third-party publish
    target. Upstream has no such file, so a merge cannot conflict with it or
    carry the guard away with the thing it guards. Policy can be edited;
    a red build cannot be ignored.

The gate is not decorative: it failed on the un-retargeted pipeline
("release.yml still references `@just-every/`") and went green only when the
pipeline was actually fixed.

Verified: cargo build --bin dev clean; hanzo_identity 4/4; code-core lib
332 passed, 1 failed -- git_info::test_collect_git_info_with_remote, which
fails identically on a clean origin/main checkout here because this machine
has a global [email protected]:.insteadof rewrite. Pre-existing and
environmental, not from this change.

  $ dev exec "say OK"        # fresh profile, no config
  Hanzo Dev v0.0.0
  model: zen5-coder
  provider: hanzo

Co-authored-by: Hanzo Dev <[email protected]>
2026-08-01 14:05:00 -07:00

3.4 KiB
Raw Blame History

Zed Integration

To point Zed at Code's ACP server, add this block to settings.json:

{
  "agent_servers": {
    "Code": {
      "command": "npx",
      "args": ["-y", "@hanzo/dev", "acp"]
    }
  }
}

Adjust the command or args only if you pin a different version or use a globally installed binary.

Zed prerequisites

  • Zed Stable 0.201.5 (released August 27, 2025) or newer adds ACP support with the Agent Panel. Update via Zed → Check for Updates before wiring Code in. Zed’s docs call out ACP as the mechanism powering Gemini CLI and other external agents.
  • External agents live inside the Agent Panel (cmd-?). Use the + button to start a new thread and pick Code from the external agent list. Zed runs our CLI as a subprocess over JSON‑RPC, so all prompts and diff previews stay local.
  • Zed installs dependencies per entry automatically. If you keep command = "npx", Zed will download the published @hanzo/dev package the first time you trigger the integration.

How Code implements ACP

  • The Rust MCP server exposes ACP tools: session/new, session/prompt, and fast interrupts via session/cancel. These are backed by the same conversation manager that powers the TUI, so approvals, confirm guards, and sandbox policies remain intact.
  • Streaming session/update notifications bridge Codex events into Zed. You get Answer/Reasoning updates, shell command progress, approvals, and apply_patch diffs in the Zed UI without losing terminal parity.
  • MCP configuration stays centralized in CODEX_HOME/config.toml. Use [experimental_client_tools] to delegate file read/write and permission requests back to Zed when you want its UI to handle approvals.
  • MCP configuration stays centralized in CODEX_HOME/config.toml. Use [experimental_client_tools] to delegate file read/write and permission requests back to Zed when you want its UI to handle approvals. A minimal setup looks like:
[experimental_client_tools]
request_permission = { mcp_server = "zed", tool_name = "requestPermission" }
read_text_file = { mcp_server = "zed", tool_name = "readTextFile" }
write_text_file = { mcp_server = "zed", tool_name = "writeTextFile" }

Zed wires these tools automatically when you add the Code agent, so the identifiers above match the defaults.

  • The CLI entry point (npx @hanzo/dev acp) is a thin wrapper over the Rust binary (cargo run -p code-mcp-server -- --stdio) that ships alongside the rest of Code. Build-from-source workflows plug in by swapping command for an absolute path to that binary.

Tips and troubleshooting

  • Need to inspect the handshake? Run Zed’s dev: open acp logs command from the Command Palette; the log shows JSON‑RPC requests and Codex replies.
  • If prompts hang, make sure no other process is bound to the same MCP port and that your CODEX_HOME points to the intended config directory. The ACP server inherits all of Code’s sandbox settings, so restrictive policies (e.g., approval_policy = "never") still apply.
  • Zed currently skips history restores and checkpoint UI for third-party agents. Stick to the TUI if you rely on those features; ACP support is still evolving upstream.
  • After a session starts, the model selector in Zed lists Code’s built-in presets (e.g., gpt-5-codex, gpt-5 high/medium/low). Picking a new preset updates the running Codex session immediately, so you don’t have to restart the agent to change models.