Skip to content

feat: add ZCode as a desktop Agent, provider only - #167

Merged
yujiezhang-ops merged 2 commits into
mainfrom
feat/zcode-desktop-agent
Aug 12, 2026
Merged

feat: add ZCode as a desktop Agent, provider only#167
yujiezhang-ops merged 2 commits into
mainfrom
feat/zcode-desktop-agent

Conversation

@yujiezhang-ops

Copy link
Copy Markdown
Collaborator

Adds ZCode alongside ChatGPT Desktop and WorkBuddy. Provider-only, as agreed — the user picks the model once inside ZCode, which is the division WorkBuddy already uses.

TRAE is not included; reasoning at the bottom.

The documentation was wrong about ZCode

Its docs (Connect Models) describe only the GUI and name no file path. On disk it turns out to be fully readable, and the schema is one this repository already writes:

"$schema": "https://opencode.ai/config.json"

ZCode uses OpenCode's config format. This only came out by inspecting the installed app; taking the docs at face value would have ruled the integration out.

Why the existing OpenCode adapter could not be reused

Two differences, both load-bearing:

Custom providers are keyed by UUID, not by a stable name. The real config on this machine contains "ed76b5b4-63bb-4cf0-b0f2-7fb651c50cac" next to builtin:* entries. WriteOpenAICompatible always writes the key "oneagent"; generating a fresh UUID per write — the shape ZCode itself uses — would append a duplicate provider on every reconfiguration. So the adapter looks up an existing entry by name and reuses its key. A builtin:* key is never reused.

The entry shape is kind / source / apiKeyRequired, not npm. apiKeyRequired matters: without it ZCode treats the endpoint as needing no credential and sends the request unauthenticated, which the Provider then rejects as a missing key.

Why provider-only

ZCode has no top-level model key, unlike OpenCode whose schema it shares. I looked for where it records the active model and did not find it: bot-config.json is a Feishu bot config, bots-model-cache.v2.json is a cache. Writing a guess into state the app owns is worse than asking the user to choose once. The model is registered under models so it appears in ZCode's picker.

No installer, and the UI had to stop claiming otherwise

No verifiable download URL for the app was found, so installZCode returns the vendor's page instead of fetching an executable from a guessed address.

That exposed a problem my change would otherwise have introduced: both selection pages render "Install the official desktop application" for any desktop Agent, which would have been a button that cannot do what it says. Definition therefore gains ManualInstall and Home, surfaced on DesktopAgentStatus, and the pages now show "install it yourself first, then OneAgent can configure it" for such an Agent. ChatGPT Desktop and WorkBuddy are unaffected:

ChatGPT Desktop  安装官方桌面应用          待安装
WorkBuddy        安装官方桌面应用          待安装
ZCode            需先自行安装,之后可配置    待安装

Wails bindings were regenerated with the pinned CLI (v3.0.0-beta.7) rather than hand-edited; the diff touches one generated file.

Verified against the real config, not just a fixture

I ran the adapter against a copy of this machine's actual ~/.zcode/v2/config.json (the live file was never written to):

provider 数: 7 -> 8
写入的键: oneagent-62365750c4ff906a
写入的条目: kind=openai source=custom baseURL=https://api.ppio.com/openai/v1 apiKeyRequired=true

Every pre-existing entry survived, including the user's own UUID-keyed one with its nested limit.context, and no top-level model key was introduced. Confirmed afterwards that the live config still has 7 providers and no OneAgent entry.

Three unit tests cover the shape, the idempotency (two writes must not produce two providers), and creating the file when absent. The fixture uses the real config's shape — builtin:* plus a UUID entry — because preserving both is the whole risk of writing into a file OneAgent does not own.

One piece of dead code removed before it shipped

I first wrote an Anthropic branch, since ZCode's own builtin entries use kind: "anthropic". It was unreachable: the registry pins Protocol: "openai", and ProtocolForAdapter defaults to openai anyway. Rather than ship a branch nothing can execute, it is gone, with a comment recording that supporting the other kind means giving Definition a real protocol choice.

Mark

Generic Lucide symbol, same basis as WorkBuddy and Aider: ZCode has no mark in lobe-icons, and the only vectors in circulation come from the app bundle, which carries no redistribution grant. NOTICE is unchanged and generate_third_party_licenses.py --check passes.

TRAE was not added

Its documentation (TokenHub, SiliconFlow) describes GUI-only configuration, and unlike ZCode I could not verify the disk format — TRAE is not installed here. Trae-AI/TRAE#888 is an open request for exactly the file-based configuration an adapter would need, and Trae-Proxy reports that custom providers can only be chosen from a fixed list.

Writing into its VS Code-derived internal state would be the same overreach as the Codex CDP injection ruled out earlier, and would break on any TRAE update. If it is wanted, guide-only is the honest shape until TRAE offers a config interface. I did not guess at it.

Verification

go build, go vet, go test ./... — 14 packages. Frontend: 350 tests across 44 files, tsc --noEmit clean, build clean. check-docs.py passes on 55 files. Console clean in the running app.

The frozen status-empty-linux-arm64.json gains the ZCode entry and its two new fields; that file is a deliberate tripwire, so the diff is the point.

🤖 Generated with Claude Code

ZCode's config turned out to be readable on disk even though its documentation
describes only the GUI. ~/.zcode/v2/config.json declares
"$schema": "https://opencode.ai/config.json" -- it uses OpenCode's format, which
this repository already writes for two other Agents.

It writes the provider and stops there, the same division WorkBuddy keeps. ZCode
has no top-level "model" key, unlike OpenCode, and where it records the active
model is neither documented nor anywhere I could find on disk: not bot-config.json
(a Feishu bot), not bots-model-cache.v2.json (a cache). Writing a guess into state
the app owns is worse than letting the user pick the model once.

Two details differ from the existing OpenCode adapter and are the reason it could
not simply be reused. ZCode keys custom providers by UUID rather than by a stable
name, so the write looks up an existing entry by name -- keying each write afresh
would leave a duplicate provider per reconfiguration. And the entry carries
kind/source/apiKeyRequired instead of npm; without apiKeyRequired ZCode sends the
request unauthenticated and the Provider rejects it as a missing key.

There is no installer. No verifiable download URL for the app was found, so
install returns the vendor's page rather than fetching an executable from a
guessed address. That made the UI's "Install the official desktop application"
label a promise it could not keep, so Definition gains ManualInstall and Home and
both selection pages say "install it yourself first" for such an Agent.

Verified against a copy of this machine's real config: 7 providers became 8, every
pre-existing entry survived including a user's own UUID-keyed one, and no
top-level model key was introduced. The live file was never written to.

The mark is a generic Lucide symbol. ZCode has none in lobe-icons and the only
vectors available come from the app bundle, which carries no redistribution
grant, so NOTICE is unchanged.
@yujiezhang-ops
yujiezhang-ops requested a review from a team August 12, 2026 05:52
@yujiezhang-ops

Copy link
Copy Markdown
Collaborator Author

Holding this until ZCode is updated.

The adapter's shape was derived from the config of the installed build on one machine — ZCode 3.1.1 — and that installation is behind. If a newer version changes the config format, this PR encodes assumptions that are already stale. The specific things that were read off disk and would need re-checking are:

  • $schema is https://opencode.ai/config.json
  • custom providers keyed by UUID next to builtin:* entries
  • entry fields kind / source / options.apiKeyRequired
  • no top-level model key, and no located store for the active model

Every one of those is an observation of 3.1.1, not a documented contract — the published docs describe GUI configuration only and name no file path at all, so there is nothing upstream to validate them against.

The branch feat/zcode-desktop-agent stays on the remote, so this can be reopened or superseded once the config of a current build has been re-inspected.

Re-checked the adapter against ZCode 3.1.3 after the machine it was derived from
updated from 3.1.1. Three things moved, and one of them made this adapter wrong:

- `"$schema": "https://opencode.ai/config.json"` is gone from the file entirely.
  The adapter added it back when absent, which would have reintroduced a key the
  current version deliberately removed. It no longer writes the key at all.
- builtin entries dropped `enabled` and `systemDisabledReason`. Nothing here reads
  or writes those, so no change was needed, but it is recorded because it shows
  this file's shape is not stable across patch releases.
- Everything the write depends on still holds: UUID-keyed custom providers beside
  `builtin:*`, the kind/source/options triple, `apiKeyRequired`, and no top-level
  `model` key.

Verified against a copy of the real 3.1.3 config: 7 providers became 8, every
pre-existing entry stayed byte-identical, the top-level key set stayed `[provider]`
alone, and no `model` key appeared. The live file was never written to.

The doc comment now names the version the shape was read from and what changed
between the two, because none of it is documented upstream -- the published docs
describe GUI configuration and name no file path, so there is nothing to validate
against except a build in front of you.
@yujiezhang-ops

Copy link
Copy Markdown
Collaborator Author

Re-verified against ZCode 3.1.3 (the machine updated from 3.1.1) and pushed one fix. Reopening.

One of the four assumptions had changed

checked 3.1.1 3.1.3
$schema https://opencode.ai/config.json key removed entirely
custom providers keyed by UUID beside builtin:* yes yes
kind / source / options.apiKeyRequired yes yes
no top-level model key yes yes

The adapter used to add $schema back when it was absent, which would have reintroduced a key the current version deliberately dropped. It no longer writes that key at all — every field outside its own provider entry is now left exactly as found.

Also noted, though nothing here touches them: builtin entries lost enabled and systemDisabledReason. Worth recording because it shows this file's shape moves between patch releases, not just minor ones.

Verified against the real 3.1.3 config

Run against a copy; the live file was never written to.

provider 数: 7 -> 8
顶层键: [provider] -> [provider]        <- no $schema reintroduced
写入 键=oneagent-62365750c4ff906a kind=openai source=custom
     baseURL=https://api.ppio.com/openai/v1 apiKeyRequired=true

Every pre-existing entry compared byte-identical before and after, including the user's own UUID-keyed one. Confirmed afterwards that the live config still holds 7 providers, no OneAgent entry, and [provider] as its only top-level key.

Why this matters beyond the one-line fix

Two patch releases changed the file's shape. The doc comment now names the version the shape was read from and what moved between them, since there is nothing upstream to validate against — the published docs describe GUI configuration and name no file path. Anyone revisiting this should re-inspect a current build rather than trust the comment.

go build, go vet, go test ./... — 14 packages. Frontend: 350 tests, tsc --noEmit clean.

@Paulkm2006 Paulkm2006 linked an issue Aug 12, 2026 that may be closed by this pull request
@yujiezhang-ops
yujiezhang-ops merged commit fe6fb4f into main Aug 12, 2026
4 checks passed
@yujiezhang-ops
yujiezhang-ops deleted the feat/zcode-desktop-agent branch August 12, 2026 07:19
yujiezhang-ops added a commit that referenced this pull request Aug 12, 2026
main carries #167, which is this branch's first two commits squashed, so most of
the overlap was the same content arriving under a different sha. Resolutions:

- zcode.go was an add/add conflict. main's copy is byte-identical to this
  branch's pre-installer state (verified against 4a0ccf6), so taking this
  branch's version keeps all of main's content and adds the installer.
- registry.go merged both ZCode entries, registering the agent twice. Dropped
  main's copy, which still carried ManualInstall: true -- no longer true now that
  the signed feed is consumed.
- The status fixture keeps this branch's side: it adds the WorkBuddy
  International entry and drops manualInstall from ZCode.
- The remaining conflicts were this branch's additions against main's absence of
  them, kept as-is.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add desktop Agent support: ZCode (Z.ai)

1 participant