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.
| 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 |
ouroboros setup --runtime zcode
ouroboros run workflow seed.yaml --runtime zcodeSetup 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.cjsorchestrator:
runtime_backend: zcode
zcode_cli_path: /Applications/ZCode.app/Contents/Resources/glm/zcode.cjsThe runtime resolves the CLI in this order:
- Constructor argument
cli_path=... OUROBOROS_ZCODE_CLI_PATHorchestrator.zcode_cli_pathin~/.ouroboros/config.yaml/Applications/ZCode.app/Contents/Resources/glm/zcode.cjszcodeonPATH
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.
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.
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=zcodeCommands 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.
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.
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.
| 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 |
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.
| 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 |
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.