Skip to content

Latest commit

 

History

History
223 lines (175 loc) · 9.22 KB

zcode.md

File metadata and controls

223 lines (175 loc) · 9.22 KB

Zcode CLI Runtime

Run Ouroboros workflows and completion-backed authoring through Z.ai's locally installed ZCode coding agent. The runtime supports either the macOS app-bundle zcode.cjs entry script or a zcode executable available on PATH.

The vendor does not currently publish a stable CLI or JSON-output contract. This adapter is pinned to behavior measured from Zcode CLI 0.15.0 and 0.15.2 and to the captured fixtures under tests/fixtures/zcode/. Treat ZCode application upgrades as compatibility events and rerun the Zcode runtime tests after an upgrade.

Prerequisites

Requirement Why
ZCode desktop app or zcode executable Provides the coding-agent runtime
A configured Z.ai provider/model Zcode reads provider and model selection from its own config
~/.zcode/cli/config.json with an effective model.main The headless CLI exits with Model config is missing without it; see Model provider configuration
Compatible Node.js for a standalone .cjs path Official app bundles use their bundled Electron/Node runtime; standalone scripts use the system Node
Ouroboros base package No provider-specific Python extra is required

Quick start

ouroboros setup --runtime zcode
ouroboros run workflow seed.yaml --runtime zcode

Setup warns, and ouroboros status health reports a warning, when ~/.zcode/cli/config.json has no effective model.main — the CLI would otherwise fail only after Ouroboros has already spawned it.

If setup cannot find ZCode automatically, configure one of:

export OUROBOROS_ZCODE_CLI_PATH=/Applications/ZCode.app/Contents/Resources/glm/zcode.cjs
orchestrator:
  runtime_backend: zcode
  zcode_cli_path: /Applications/ZCode.app/Contents/Resources/glm/zcode.cjs

CLI path resolution

The runtime resolves the CLI in this order:

  1. Constructor argument cli_path=...
  2. OUROBOROS_ZCODE_CLI_PATH
  3. orchestrator.zcode_cli_path in ~/.ouroboros/config.yaml
  4. /Applications/ZCode.app/Contents/Resources/glm/zcode.cjs
  5. zcode on PATH

Official app bundles include .node-bundle-meta.json with runtime: electron-node. Ouroboros reads that metadata, launches ZCode.app/Contents/MacOS/ZCode, and sets ELECTRON_RUN_AS_NODE=1, matching ZCode's own launcher. A configured script inside a .app bundle fails closed when the metadata, plist, or bundled executable is missing or invalid; it never falls back to an unrelated system Node. Standalone .cjs, .js, or .mjs paths use the system Node.js. Other paths are treated as executable wrappers or binaries and are invoked directly. NODE_OPTIONS is removed from both Node launch shapes so a project or parent process cannot preload JavaScript into the vendor CLI.

Model provider configuration

Besides the CLI path, the Zcode CLI resolves its model provider from ~/.zcode/cli/config.json on every run — including the headless --prompt --json invocations Ouroboros spawns. Without an effective model.main the CLI exits immediately with:

Error: Model config is missing. Create /Users/<you>/.zcode/cli/config.json
with an explicit model provider before running ZCode.

model.main must be the "provider-id/model-id" string; an object entry is silently dropped by the CLI's config parser, which then reports the same missing-config error. The referenced provider id needs an entry in the same file's provider registry carrying kind, options.baseURL, and options.apiKey:

{
  "model": { "main": "builtin:zai-coding-plan/GLM-5.3" },
  "provider": {
    "builtin:zai-coding-plan": {
      "name": "Z.ai - Coding Plan",
      "kind": "anthropic",
      "options": {
        "baseURL": "https://api.z.ai/api/anthropic",
        "apiKey": "<your key>",
        "apiKeyRequired": true
      },
      "enabled": true
    }
  }
}

The desktop app's ~/.zcode/v2/config.json uses the same provider registry shape, so copying the active provider entry from there is the quickest way to satisfy the CLI. OAuth credentials in ~/.zcode/v2/credentials.json are shared between the desktop app and the CLI, but the model reference itself is not: the desktop app's model selection does not create ~/.zcode/cli/config.json.

ouroboros setup --runtime zcode warns when this file has no effective model.main, and the ouroboros status health rows for the zcode runtime and LLM backend surface the same probe.

Runtime and LLM backend

Zcode can drive both agentic execution and Ouroboros completion roles. Set the runtime independently with ouroboros setup --runtime zcode, or select Zcode for interview, Seed generation, evaluation, and QA through:

export OUROBOROS_LLM_BACKEND=zcode

Commands that expose an explicit backend flag also accept --llm-backend zcode. The completion adapter uses the same measured --prompt --json summary contract as the runtime, returns the top-level response, and validates requested json_object or json_schema output before accepting a result.

Zcode owns model selection in its configuration and exposes no --model flag. Requested Ouroboros model names are therefore not reported as observed effective identity unless a trusted Zcode output or configuration source confirms them.

Headless contract

For an official macOS app-bundle script, each task uses this command shape:

ELECTRON_RUN_AS_NODE=1 <ZCode.app/Contents/MacOS/ZCode> <zcode.cjs> \
  --json \
  --prompt <PROMPT> \
  --mode <edit|yolo> \
  [--cwd <PATH>] \
  [--resume <SESSION_ID>]

The measured --json behavior is one pretty-printed summary object emitted at the end of the turn, not an NDJSON event stream. The runtime reassembles stdout before parsing it and maps the top-level response to one terminal assistant message. The top-level sessionId becomes the resume handle.

Live compatibility evidence

The official notarized macOS ARM packages were exercised end to end with a local OpenAI-compatible model endpoint, so the vendor process, streaming model adapter, JSON summary, persisted session, and --resume path were all real:

ZCode app CLI Result
3.2.5 0.15.0 Headless JSON response and same-session resume passed
3.3.5 0.15.2 Headless JSON response and same-session resume passed

Both bundles declare runtime: electron-node and include Electron 41 / Node 24. Running their zcode.cjs directly with Node 20 fails on the vendor's node:sqlite import; the app-bundle launcher selection above prevents that environment-dependent failure.

The adapter does not emit --non-interactive, --approval-mode, or --model. Those are not accepted Zcode 0.15.0 or 0.15.2 flags. Model selection remains in Zcode's own configuration, including ~/.zcode/cli/config.json model.main.

Permission mapping

Ouroboros mode Zcode --mode Behavior
acceptEdits edit Default non-interactive edit mode
bypassPermissions yolo Explicit full bypass
default edit Normalized to the safe non-interactive default

Buffered-output timeout behavior

Zcode stays silent until its final JSON summary is ready. The inherited 60-second first-output watchdog is therefore disabled by default because it would otherwise become a 60-second total-task limit. Callers that require a no-output deadline can pass startup_output_timeout_seconds explicitly.

This is an operational tradeoff: without an explicit outer deadline, a vendor process that never emits its summary can remain pending. Production callers should retain their workflow-level deadline or configure a suitable startup output timeout for their expected task duration.

Capabilities and limits

Capability Status
Headless execution Yes, via --prompt --json
Structured final output Yes, one summary object per turn
Intermediate tool events No, not present in measured stdout
Targeted session resume Yes, via --resume <sessionId>
Per-call model override No, model selection is Zcode-owned
LLM-completion backend Yes, via the buffered JSON summary adapter
Setup-owned instruction artifact No, capability guide rendering is the fallback

Troubleshooting

Zcode is not detected. Set OUROBOROS_ZCODE_CLI_PATH, configure orchestrator.zcode_cli_path, or put a zcode executable on PATH.

Headless runs exit with Model config is missing. The Zcode CLI has no model.main it can resolve in ~/.zcode/cli/config.json. See Model provider configuration; the two usual causes are a missing file and an object-form model.main, which the CLI silently drops — the reference must be the "provider-id/model-id" string.

A standalone script reports a missing Node built-in such as node:sqlite. Use the intact ZCode app-bundle path so Ouroboros can select the bundled Electron/Node runtime, install a Node version compatible with that Zcode build, or configure a directly executable zcode wrapper.

A model override has no effect. Zcode has no per-invocation --model flag. Select the model in ZCode's own provider/model configuration.

Parsing breaks after a ZCode update. Run tests/unit/orchestrator/test_zcode_cli_runtime.py and recapture the vendor summary fixture before changing the parser contract.