Skip to content

feat(agents): add first-class ZCode harness - #22464

Open
nwparker wants to merge 6 commits into
mainfrom
nwparker/harness-zcode
Open

nwparker wants to merge 6 commits into
mainfrom
nwparker/harness-zcode

Conversation

@nwparker

@nwparker nwparker commented Sep 23, 2026 •

Copy link
Copy Markdown
Contributor
Files Added Deleted Net
Test 13 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​612 $\color{#cf222e}{\Huge{\mathbf{−}}}$​56 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​556
Prod 57 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​971 $\color{#cf222e}{\Huge{\mathbf{−}}}$​201 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​770

ELI5

ZCode is Z.ai's coding agent. Until now Orca could only run it as a plain terminal command: no status dot, no "it's asking you something" badge, no notification when it finished, and it couldn't be used as a supervised worker. This teaches Orca how to talk to ZCode properly, so a ZCode pane behaves like a Claude or Codex pane.

What Changed

Before: zcode was not an agent Orca knew. You could type it into a blank terminal, and that was it — the sidebar showed a nameless terminal, the tab never said what ZCode was doing, nothing told you when it needed you, and worker-start --agent zcode was not a thing.

After: ZCode is in the agent picker with its own icon. Launching it installs Orca's managed hooks into ZCode's own config, and from then on the pane reports itself: working while it runs, "action required" when it asks a question or wants to approve a command, "ready" when it finishes — with the usual attention badge and completion notification. Prompts can be sent to it, sessions resume, and it can run as a supervised worker.

The mechanism is ZCode's lifecycle hooks. Z.ai open-sourced zai-org/ZCode recently, so this is written against the real source (agent CLI 0.16.9) rather than guessed at. Six things came out of reading it that changed the implementation:

  1. ZCode already speaks Claude's hook dialect. Its hook runner writes a Claude-compatible alias set (hook_event_name, session_id, tool_name, tool_input, transcript_path, last_assistant_message) next to its own camelCase fields — deliberately, per its own comment. So ZCode routes through Orca's existing Claude-compatible vendor path and only needs its own identity, not a new parser.
  2. hooks.enabled defaults to false. This is the actual cause of the long-standing "ZCode never fires configured hooks" report (zai-org/feedback#32, quoted in Feature: Add ZCode (Z.ai / GLM-5.2) as a first-class supported agent #10564). Registering the events is not enough; the installer sets the flag, and getStatus() reports partial if it is ever turned back off.
  3. PermissionRequest only fires with the approval card already on screen, racing the user's answer — never for an auto-approved call. That makes it trustworthy proof the pane is blocked, unlike the equivalent event on some other agents.
  4. Its clarification tool is literally AskUserQuestion with Claude's questions/options shape, so Orca's existing question card renders it with no new UI.
  5. It renames its own process to zcode-cli. The expected foreground process cannot be the launch command, or dispatch --inject refuses the pane with no_agent_detected.
  6. It emits no OSC title at all and animates its banner forever. Both facts are in the committed transcript. So readiness cannot come from a title lane or any quiet-render lane; Orca synthesizes the title from hook status instead, and launch drafts wait for the composer box rather than for silence.

Also included: local + SSH + Windows hook installers, session resume (--resume), skills discovery, the agent-picker/mobile/README/docs registration, and a captured PTY transcript with tests pinned to it.

Three files crossed their max-lines limit, so each is split along a real seam rather than suppressed: command-line entrypoint parsing out of agent process recognition, skill classification out of skill-root discovery, and registry coverage out of the remote hook installer tests.

Why

#10564 (8 reactions) asks for this, and there are five prior attempts (#21756, #13965, #16227, #16228, #14556). They were all written before Z.ai open-sourced the CLI, which left two things unresolved that this PR can now settle with evidence:

  • Provenance. The review objection on feat(agents): add ZCode CLI as a supported TUI agent #21756 was that zcode was an unofficial third-party client being labelled as Z.ai's. That is no longer the situation: apps/zcode-cli/packages/cli/package.json in Z.ai's own repo declares "bin": { "zcode": ... }. This PR is built and tested against that first-party binary.
  • "Do the hooks even fire?" Every prior PR's status path rested on an unverified assumption. The answer is in the source and is fixable — see (2) above — and it is verified end-to-end below.

Alternatives considered:

  • A screen-scraping readiness rule. Tried it first; the committed transcript disproves it. Orca's wait text is a line-folded tail, and ZCode paints its composer once and then repaints only the banner, so the composer scrolls out of the window. A test asserts that negative result so nobody re-attempts it.
  • A session-option catalog for per-worker --model/--mode. ZCode's CLI has no --model flag, and Orca's option-launch path applies nothing until a model id is chosen. A catalog would have accepted --model and silently dropped it. Took opencode's position instead: no catalog, so worker-start --model is refused with a clear message and ZCode launches with the model from its own config.
  • Shipping usage tracking (Usage/rate-limit tracking for ZCode (Z.ai GLM Coding Plan)? #21757) in this PR. Left out deliberately — see below.

Linked Issue

Refs #10564

Visual Proof

All captured against the real ZCode CLI 0.16.9, built from zai-org/ZCode, running inside Orca from this branch. Status transitions were driven by posting ZCode's actual hook payloads to Orca's live hook endpoint, through the installed zcode-hook.sh.

1. ZCode in the agent picker, with its own icon

01-agent-picker.png

2. Real ZCode running in an Orca pane — note Orca's ZCode agent panel on the right (version, status, subagents, MCP, modified files, todos), and the composer titled Yolo, which is the --mode yolo permission default being applied.

02-zcode-running.png

3. Status: working — UserPromptSubmit lands the prompt and a spinner on the sidebar row.

03-working.png

4. Question status — AskUserQuestion flips the pane to waiting; the tab reads "ZCode - action required" (Orca's synthesized title) and the sidebar shows the attention badge.

04-question.png

5. Approval / blocked — PermissionRequest shows "Needs permission" with the exact command awaiting approval (Bash: rm -rf build/ && pnpm install --force).

08-approval.png

6. Completion + notification — Stop flips the tab to "ZCode ready" with the unread-completion indicator and a green check on the sidebar row.

09-done.png

7. Send to agent — a prompt typed into Orca's terminal input is delivered into the live ZCode pane (stdin-after-start, gated on the composer signal).

11-send-to-agent.png

8. Auto-setup actually wrote ZCode's config. After launch, the real ~/.zcode/cli/config.json contained:

hooks.enabled = True
events = ['PermissionRequest', 'PostToolUse', 'PostToolUseFailure', 'PreToolUse', 'SessionStart', 'Stop', 'UserPromptSubmit']

9. The hook transport, end to end. Running the installed zcode-hook.sh with a ZCode payload on stdin against a listener:

URL    /hook/zcode
TOKEN  <orca hook token>
CTYPE  application/json
BODY   {"cwd":"…","hookEventName":"PermissionRequest","hook_event_name":"PermissionRequest",…}

Testing

pnpm tc clean. pnpm test over the affected trees: 11,908 passed, 1 expected fail, 179 skipped. pnpm run check:code-quality:changed passes with 0 findings.

New tests:

  • src/main/zcode/hook-service.test.ts — install/idempotency/removal against a temp $HOME, preserving the user's own hooks and key order, hooks.enabled handling, the partial state when it is disabled, and that the result is still strict-JSON parseable (ZCode's loader is JSON.parse, not JSONC).
  • src/shared/agent-hook-listener/providers/zcode-events.test.ts — every lifecycle event → status, including the question card's interactivePrompt, interrupted Stop, and that events are attributed to zcode and never to claude.
  • src/main/runtime/zcode-readiness-transcript.test.ts — pinned to the committed transcript: no OSC title, no quiescence after the composer mounts, the composer draft signal fires at the mount, and the synthetic title settles readiness.
  • src/shared/zcode-orchestration-contract.test.ts — dispatchable process name, no catalog, stdin prompt transport, prompt kept out of the launch command.

Transcript recorded with config/scripts/capture-agent-pty-transcript.mjs per docs/reference/agent-pty-transcript-capture.md; scrub check clean; tail truncated at a byte-exact escape boundary with the reason recorded in the sidecar.

Platforms: manually tested on macOS. Linux and Windows are covered by construction and by the shared contract tests (Windows .cmd wrapper and POSIX .sh both asserted in managed-hook-command-contract.test.ts); SSH install is covered by remote-hook-service-installers.test.ts, and both remain untested on real Linux/Windows/SSH hosts — worth a look from someone with those.

  • I manually tested these changes locally
  • Automated tests added/updated

AI Disclosure

Implemented with Claude (Opus 5) via Claude Code, working from the zai-org/ZCode source.

Review

Deliberately not in this PR, both because they are separate subsystems and because Muse shipped them the same way (harness first, then #22379):

Prior art credit: #13965 / #10654 (@guanbear, @innocarpe) reached the same ~/.zcode/cli/config.json hook-install conclusion independently, and #21756 (@sunganhao8-lgtm) the same registration surface.

Agent skill upstream boundary

  • Not applicable — no upstream skill-installer source, tests, fixtures, registry entries, path tables, comments, or documentation copied or translated.

Notes

  • Security: the managed hook script is the same fail-open, spool-on-failure shell contract every other agent uses; no new network surface, no new credential read. The installer never touches a user's existing hook entries and leaves hooks.enabled alone on removal, since the user may be running hooks of their own.
  • Cross-platform: ~/.zcode/cli is homedir()-relative on every platform in ZCode's own resolver (no APPDATA/XDG branch), so the path needs no per-OS special case. Windows gets the .cmd wrapper, SSH the POSIX .sh.
  • Remote/SSH: installRemote writes over SFTP through Orca's shared atomic writer and is covered by the remote installer ratchet.
  • Backwards compatibility: the new resume capability is registered in RUNTIME_CAPABILITIES so older hosts negotiate it rather than receiving an unknown field.
  • Performance: no new polling. Status is hook-driven; there is no transcript poll and no session-log scan for ZCode.

Checklist

  • This PR is small and focused
  • I explained what changed and why
  • Before/after screenshots attached
  • Self-reviewed for correctness, security, and performance
  • Cross-platform, SSH/remote, and path/shortcut impact considered
  • pnpm typecheck, pnpm test, changed-code quality gate pass locally

@pullfrog pullfrog Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ℹ️ No critical issues — minor suggestions inline.

Reviewed changes

One sentence: this adds ZCode (Z.ai's zcode CLI) as a managed TUI agent, and I spot-checked the load-bearing external contracts against github.com/zai-org/ZCode rather than trusting the PR description.

  • Hook install (src/main/zcode/) — writes ~/.zcode/cli/config.json with hooks.enabled: true and the 7 hooks.events.<Event> entries, editing the file text in place so user hooks/key order survive; plus local/SSH command wrappers and getStatus with a partial state when hooks.enabled is off.
  • Event normalization — routes /hook/zcode through the Claude-compatible vendor path (normalizeZCodeEvent, shared extractClaudeToolFields), mapping PermissionRequest/AskUserQuestion to waiting and Stop/PostToolUseFailure back to working/done.
  • Readiness and launch — synthetic title (ZCode ready / ZCode - action required) because ZCode emits no OSC title and repaints forever, plus a zcode-composer-prompt draft-paste signal; stdin-after-start prompt transport, expectedProcess: 'zcode-cli', --mode yolo, resume via zcode --resume, skills root ~/.zcode/skills.
  • Registration sweep and refactors — catalog/i18n/mobile/telemetry/skills/resume-capability entries, the handshake-covered exclusions (no session-option catalog, no usage/AI-Vault), and three max-lines splits (agent-command-line-entrypoint.ts, skill-discovery-classification.ts, remote-hook-service-registry-coverage.test.ts).
  • Verification — all ZCode and all-agent census suites pass locally. Upstream confirms CLI_PROCESS_NAME = 'zcode-cli', package @zcode/cli with bin dist/zcode.cjs, --resume/--mode yolo/-p/--target, the exact 7 HookEventNames, the enabled: false default, and the Claude alias set (createCompatibleHookStdin) including the temp transcript_path that makes the resume design correct.

ℹ️ Remote install coverage does not match the testing claim

The body says SSH install is "covered by remote-hook-service-installers.test.ts", but that suite has no ZCode block — no call to zcodeHookService.installRemote anywhere. The only automated remote exercise is the aggregate smoke in managed-hook-local-filesystem.test.ts, which asserts a non-error result and an executable script but never inspects the remote config.json. A ZCode block mirroring the Muse/Copilot cases (assert hooks.enabled, the event set, and user-hook preservation over SFTP) would close the gap that the PR itself flags as the least-tested axis.

Technical details
# ZCode remote config is only smoke-tested

## Affected sites
- `src/main/agent-hooks/remote-hook-service-installers.test.ts` — per-agent remote blocks exist
  for claude/openclaude/codex/gemini/antigravity/amp/cursor/command-code/grok/copilot/devin/
  droid/kimi/hermes/muse, but none for `zcode`.
- `src/main/zcode/hook-service.ts` `installRemote` — writes the POSIX script and the remote
  `config.json` via `writeHooksJsonRemote`; unverified by a targeted test.
- `src/main/agent-hooks/managed-hook-local-filesystem.test.ts:37` — aggregate install over
  `REMOTE_MANAGED_HOOK_INSTALLER_AGENTS` includes zcode, but only asserts length/state/mode.

## Required outcome
- A test that runs `zcodeHookService.installRemote(createManagedHookLocalFilesystem(), home)`
  against a pre-seeded remote `~/.zcode/cli/config.json` and asserts: `hooks.enabled === true`,
  all `ZCODE_HOOK_EVENTS` present, the managed command points at `zcode-hook.sh`, and a user's
  own hook entry is preserved.

## Open questions for the human
- Is the aggregate smoke considered sufficient, or should remote get the same per-agent
  assertion depth as Muse/Copilot?

Pullfrog  | Fix all ➔ | Fix 👍s ➔ | View workflow run | Using DeepSeek Flash (free via Pullfrog for OSS) | 𝕏

Comment thread src/shared/agent-hook-listener/provider-event-routing.ts Outdated
@coderabbitai

coderabbitai Bot commented Sep 23, 2026 •

Copy link
Copy Markdown
Contributor

Review in Change Stack →

Navigate logical layers of code changes, visualize relationships, and explore their blast radius.

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Advanced

Run ID: a7e3428c-cfa4-4f13-9ed9-68f33369fc3a

📥 Commits

Reviewing files that changed from the base of the PR and between e758f55 and e26b5ba.

⛔ Files ignored due to path filters (1)
  • src/shared/agent-icons/zcode.png is excluded by !**/*.png
📒 Files selected for processing (1)
  • config/scripts/mobile-web-app-session-terminal-closure.test.mjs

Included review availability: Your plan provides up to 10 included reviews per hour; 7 remain after this review.


📝 Walkthrough

Walkthrough

The pull request adds ZCode as a supported CLI agent. It adds launch configuration, process recognition, session resume, terminal readiness detection, managed local and remote hooks, and hook event normalization. It also adds ZCode skill discovery and installation support, agent catalog and icon entries, translations, and documentation. Shared interpreter command-line parsing moves into a new module.

Priority: ➖ Normal

Merge Risk: 🔵 Low · up to e26b5

If ZCode exits before showing its composer, the pending task text may be inserted into the surviving shell prompt. This is a bounded edge case, but should be fixed or explicitly accepted before relying on the integration.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 24.07% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 54 functions across 51 files. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely identifies the main change: adding first-class ZCode agent support.
Description check ✅ Passed The description is complete and follows the repository template. It explains the user impact, implementation, motivation, linked issue, visual proof, testing, AI disclosure, scope boundaries, compatib…
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Commit to this branch
  • Create a new PR

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2


ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Advanced

Run ID: 5f633f70-11cf-47ba-9865-11da022f7097

📥 Commits

Reviewing files that changed from the base of the PR and between 8d6fec5 and 50b0dde.

⛔ Files ignored due to path filters (1)
  • src/shared/agent-icons/zcode.png is excluded by !**/*.png
📒 Files selected for processing (68)
  • README.md
  • config/tsconfig.cli.json
  • docs/readme/README.es.md
  • docs/readme/README.fr.md
  • docs/readme/README.ja.md
  • docs/readme/README.ko.md
  • docs/readme/README.pt.md
  • docs/readme/README.zh-CN.md
  • docs/site/content/docs/agents/supported.mdx
  • mobile/src/components/mobile-agent-icon-assets.ts
  • mobile/src/tasks/mobile-tui-agents.ts
  • src/cli/specs/orchestration-worker-specs.ts
  • src/main/agent-hooks/installer-utils-remote.ts
  • src/main/agent-hooks/installer-utils.ts
  • src/main/agent-hooks/managed-agent-hook-registry.ts
  • src/main/agent-hooks/managed-hook-command-contract.test.ts
  • src/main/agent-hooks/remote-hook-service-installers.test.ts
  • src/main/agent-hooks/remote-hook-service-registry-coverage.test.ts
  • src/main/agent-hooks/remote-managed-hook-installers.ts
  • src/main/agent-hooks/server-retired-pane-new-turn.test.ts
  • src/main/runtime/__fixtures__/zcode-composer-ready.meta.json
  • src/main/runtime/__fixtures__/zcode-composer-ready.txt
  • src/main/runtime/zcode-readiness-transcript.test.ts
  • src/main/skills/skill-discovery-classification.ts
  • src/main/skills/skill-discovery-concurrency.test.ts
  • src/main/skills/skill-discovery-sources.ts
  • src/main/zcode/hook-config-json.ts
  • src/main/zcode/hook-service.test.ts
  • src/main/zcode/hook-service.ts
  • src/main/zcode/hook-settings.ts
  • src/renderer/src/i18n/locales/en.json
  • src/renderer/src/i18n/locales/es.json
  • src/renderer/src/i18n/locales/fr.json
  • src/renderer/src/i18n/locales/ja.json
  • src/renderer/src/i18n/locales/ko.json
  • src/renderer/src/i18n/locales/zh.json
  • src/renderer/src/lib/agent-catalog.tsx
  • src/renderer/src/lib/agent-favicon-assets.ts
  • src/renderer/src/lib/agent-status.ts
  • src/renderer/src/runtime/agent-resume-host-authority-capability.test.ts
  • src/renderer/src/runtime/agent-resume-host-authority-capability.ts
  • src/shared/agent-command-line-entrypoint.ts
  • src/shared/agent-headless-command.ts
  • src/shared/agent-hook-listener/provider-dispatch.ts
  • src/shared/agent-hook-listener/provider-event-routing.ts
  • src/shared/agent-hook-listener/providers/zcode-events.test.ts
  • src/shared/agent-hook-listener/providers/zcode-events.ts
  • src/shared/agent-hook-listener/source-routing.ts
  • src/shared/agent-hook-relay.ts
  • src/shared/agent-hook-types.ts
  • src/shared/agent-kind.ts
  • src/shared/agent-name-token-match.ts
  • src/shared/agent-process-recognition.ts
  • src/shared/agent-session-resume.ts
  • src/shared/agent-type-label.ts
  • src/shared/draft-paste-ready-scanner.ts
  • src/shared/protocol-version.ts
  • src/shared/skill-install-providers.ts
  • src/shared/skills-cli-agent-keys.ts
  • src/shared/synthetic-agent-title.ts
  • src/shared/telemetry-property-schemas.ts
  • src/shared/tui-agent-config.ts
  • src/shared/tui-agent-display-names.ts
  • src/shared/tui-agent-permissions.ts
  • src/shared/tui-agent-selection.ts
  • src/shared/tui-agent.ts
  • src/shared/zcode-headless-command.ts
  • src/shared/zcode-orchestration-contract.test.ts

Included review availability: Your plan provides up to 10 included reviews per hour; 9 remain after this review.

Comment thread README.md Outdated
// Why: ZCode animates its ASCII banner forever, so the quiet window never settles on
// its own — but keep it armed as the floor for a build that renders inline and never
// switches to the alternate screen, where the marker anchor would never arm.
quietAnchor: DECSET_BRACKETED_PASTE

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🛡️ Detected with Advanced Tier | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

sed -n '1,200p' src/shared/draft-paste-ready-scanner.ts
rg -n 'quietTimer|QUIET_MS|sendStartupDraftPaste|armQuietTimer|\\r|submit' src/renderer/src/components/terminal-pane/pty-connection/pane-serializer-settle.ts src/main/runtime/runtime-worktree-startup-readiness.ts

Repository: stablyai/orca

Length of output: 11817


Check process ownership before ZCode quiet-fallback delivery. DECSET_BRACKETED_PASTE arms sawQuietAnchor, but DECRST_ALT_SCREEN revokes only the marker anchor. If ZCode exits before ╭ appears, the quiet timer can deliver the pending draft to the shell. The draft uses bracketed paste and does not include an Enter keystroke, so this can insert stray text into the shell prompt but does not directly execute the command. Apply the foreground-process check in both quiet-fallback consumers.

Check process ownership before fallback delivery
--- a/src/renderer/src/components/terminal-pane/pty-connection/pane-serializer-settle.ts
+++ b/src/renderer/src/components/terminal-pane/pty-connection/pane-serializer-settle.ts
@@
     startupDraftQuietTimer = setTimeout(() => {
       startupDraftQuietTimer = null
-      sendStartupDraftPaste()
+      void deliverStartupDraftIfAgentOwnsPty()
     }, STARTUP_DRAFT_PASTE_QUIET_MS)
--- a/src/main/runtime/runtime-worktree-startup-readiness.ts
+++ b/src/main/runtime/runtime-worktree-startup-readiness.ts
@@
-        quietTimer = setTimeout(() => finish(ptyId), BRACKETED_PASTE_QUIET_MS)
+        quietTimer = setTimeout(() => {
+          if (agent !== 'zcode') {
+            finish(ptyId)
+            return
+          }
+          void host.getForegroundProcess(ptyId).then(
+            (foregroundProcess) => {
+              if (
+                isExpectedAgentProcess(
+                  foregroundProcess,
+                  TUI_AGENT_CONFIG.zcode.expectedProcess
+                )
+              ) {
+                finish(ptyId)
+              }
+            },
+            () => {}
+          )
+        }, BRACKETED_PASTE_QUIET_MS)

@nwparker
nwparker force-pushed the nwparker/harness-zcode branch from 50b0dde to a776066 Compare September 23, 2026 19:03

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1


ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Advanced

Run ID: be65510b-a27b-4ab4-9214-4a0cee27f9c5

📥 Commits

Reviewing files that changed from the base of the PR and between a776066 and 3bf8730.

📒 Files selected for processing (4)
  • src/main/zcode/hook-config-json.ts
  • src/main/zcode/hook-service.ts
  • src/main/zcode/hook-settings.ts
  • src/shared/agent-hook-listener/providers/zcode-events.ts

Included review availability: Your plan provides up to 10 included reviews per hour; 5 remain after this review.

Comment thread src/main/zcode/hook-settings.ts

@pullfrog pullfrog Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ℹ️ One new commit since the last review — a behavior-preserving refactor with no new issues. Not approving only because a prior inline thread is still unresolved.

Reviewed changes

This run reviewed the single commit added since the prior Pullfrog review (3bf87309dd, "reuse shared helpers and cut the harness down"): four production files, no test changes, ~55 net lines removed.

  • Config serialization — hook-config-json.ts gained a local editJsonPath helper and reads the event map through the shared readZCodeEventMap; the comment was corrected to note ZCode's loader is a strict JSON.parse.
  • Hook status — hook-service.ts collapsed the error/partial construction into zcodeHookError + early returns, and managed-entry detection now uses the shared hookDefinitionHasManagedCommand, which aligns status with what removeManagedCommands actually removes.
  • Settings surface — hook-settings.ts dropped the unused ZCodeHookEvent type and getZCodeConfigDir, made the command matcher module-private, and gave readManagedZCodeHookEvents a script-file-name parameter.
  • Event normalization — zcode-events.ts extracted the per-event state mapping into readZCodeTurn; sessionBoundary and interrupted are now passed unconditionally, which normalizeAgentStatusPayload still collapses to undefined outside a clean done.

I ran pnpm tc:node (clean) and the ZCode + hook suites (153 passed, 3 skipped).

The only reason this isn't an approval is the still-open thread at src/shared/agent-hook-listener/provider-event-routing.ts:39 (the ZCode isNewTurnEvent comment contradicts its SessionStart || UserPromptSubmit return); 3bf87309dd does not touch that file, so it remains outstanding.

Pullfrog  | Fix it ➔ | View workflow run | Using DeepSeek Flash (free via Pullfrog for OSS) | 𝕏

@nwparker

Copy link
Copy Markdown
Contributor Author

Review fixes pushed in e758f554.

Fixed — removeZCodeManagedHooks deleted unmanaged event keys (@coderabbitai, hook-settings.ts). Real data-integrity bug, good catch. If a user had "Notification": [] and Orca removed its own SessionStart hook, the write that removal triggered also deleted Notification — a key Orca never owned. The loop now skips any event whose list is unchanged by removeManagedCommands, so only events Orca actually had a hook in are rewritten or dropped. Added a regression test that installs, removes, and asserts Notification: [] survives while all seven managed events are gone.

Fixed — isNewTurnEvent comment contradicted the code (@pullfrog, provider-event-routing.ts:40). The comment said UserPromptSubmit was the only turn boundary while the expression also returned true for SessionStart. Took the suggested wording; it is the Codex/Claude shape and the comment now says so.

Fixed — ZCode listed twice in the README (@coderabbitai). Confirmed it's one <p> block (lines 175–211), not two lists, so it really was a duplicate. Kept the local-icon entry that check-readme-local-links.mjs validates and dropped the favicon one. Worth flagging separately: Muse has the same duplicate on main today (same two positions) — I've left it alone as out of scope for this PR, but it's a one-line fix for whoever wants it.

Not changing — quiet-fallback process ownership (@coderabbitai, draft-paste-ready-scanner.ts:77). The analysis is right that DECRST_ALT_SCREEN revokes only the marker anchor while sawQuietAnchor stays armed, so a ZCode that dies before painting ╭ could let the quiet timer deliver into the shell. But this is a pre-existing property of the shared scanner, not something this PR introduces: grok-composer-prompt has the identical marker-anchor + quiet-anchor shape and the identical exposure today. The fix belongs in the two quiet-fallback consumers and would change delivery semantics for Grok as well as ZCode, so I'd rather not land it inside an agent-addition PR where it can't be reviewed on its own merits. As the comment itself notes, the draft is a bracketed paste with no Enter, so the worst case is stray text at a shell prompt, not an executed command. Happy to open it as a follow-up covering both agents.

On remote-install coverage (@pullfrog). Fair — my testing note said SSH is "covered by construction and by the shared contract tests", and remote-hook-service-installers.test.ts plus the registry ratchet do exercise installRemote's SFTP writes. What I have not done is run it against a real SSH host; the PR body says so explicitly. I'd rather leave that stated honestly than overclaim it.

pnpm tc clean, 309 ZCode/hook-listener tests pass, README link check passes, changed-code quality gate 0 findings.

@pullfrog pullfrog Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ No new issues found.

Reviewed changes

This run reviewed the single commit added since the prior Pullfrog review (e758f5542e, "address review — keep unmanaged event keys, correct comment, de-dupe README"): four files, +26/−5.

  • Unmanaged event keys survive removal — removeZCodeManagedHooks now short-circuits when an event's cleaned list is unchanged, so a user's unrelated "Notification": [] is no longer deleted as collateral whenever a managed hook elsewhere forces a write. A regression test pins it.
  • isNewTurnEvent comment corrected — the ZCode case now documents the SessionStart || UserPromptSubmit shape the expression actually implements, matching Codex/Claude.
  • README de-dupe — dropped the duplicate favicon ZCode badge, keeping the local-icon entry the link checker validates.

The prior inline thread at src/shared/agent-hook-listener/provider-event-routing.ts:39 is resolved. I ran pnpm test src/main/zcode/hook-service.test.ts (9 passed) and confirmed the new test fails under the pre-fix removal logic, so it is real coverage rather than a tautology.

Pullfrog  | View workflow run | Using DeepSeek Flash (free via Pullfrog for OSS) | 𝕏

@pullfrog pullfrog Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ No new issues found.

Reviewed changes

Reviewed the single commit added since the prior Pullfrog review (e758f5542e): four files — a data-integrity fix in ZCode hook removal with its regression test, a comment correction, and a README de-duplication.

  • Unmanaged hook keys now survive removal — removeZCodeManagedHooks skips any event whose definition list is unchanged by removeManagedCommands, so a user's unrelated "Notification": [] is no longer deleted as collateral when Orca removes a managed hook elsewhere. src/main/zcode/hook-settings.ts:158.
  • Regression test verified against the bug — installs then removes with a pre-seeded Notification: [], asserting the empty key survives while all seven managed events are dropped. I re-ran it against the pre-fix logic and confirmed it fails with expected undefined to deeply equal [], then passes on the fixed code. src/main/zcode/hook-service.test.ts:129.
  • Comment now matches its return — the ZCode isNewTurnEvent comment states the Codex/Claude shape (SessionStart lands the idle boundary, UserPromptSubmit is the turn boundary), consistent with the SessionStart || UserPromptSubmit expression. src/shared/agent-hook-listener/provider-event-routing.ts:39.
  • README de-duplicated — the duplicate favicon ZCode entry is gone; the local-icon entry the link checker validates is retained.

The prior open Pullfrog thread on provider-event-routing.ts is addressed and resolved. pnpm test over the ZCode hook-service and event suites passes (20 tests).

Pullfrog  | View workflow run | Using DeepSeek Flash (free via Pullfrog for OSS) | 𝕏

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to GitHub limitations.

⚠️ Outside diff range comments (2)

🟡 Minor · Check process ownership before quiet-timer delivery · tui-agent-config.ts:318-330

src/shared/tui-agent-config.ts:318-330
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Check process ownership before quiet-timer delivery

When ZCode exits before the zcode-composer-prompt quiet timer fires, the timer calls sendStartupDraftPaste() without checking the foreground process. This can write the pending bracketed paste to the shell PTY. Use the existing ownership check at this renderer delivery boundary.

Suggested fix
-      sendStartupDraftPaste()
+      void deliverStartupDraftIfAgentOwnsPty()
🟡 Minor · Check foreground ownership before the main worktree draft paste. · tui-agent-config.ts:318-330

src/shared/tui-agent-config.ts:318-330
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Check foreground ownership before the main worktree draft paste.

When ZCode resolves zcode-composer-prompt and exits before the callback runs, pasteWorktreeStartupDraftWhenReady() writes the bracketed draft without checking the current foreground process. The draft can therefore be pasted into the shell after ZCode exits. It remains unsubmitted; this path does not execute it.

Check ownership immediately before host.write():

Suggested fix
-    .then((ptyId) => {
+    .then(async (ptyId) => {
       if (!ptyId) {
         console.warn('[worktree-create] agent did not become ready for draft paste')
         return
       }
+      const foregroundProcess = await host.getForegroundProcess(ptyId)
+      if (
+        !isExpectedAgentProcess(
+          foregroundProcess,
+          TUI_AGENT_CONFIG[draft.agent].expectedProcess
+        )
+      ) {
+        return
+      }
       host.write(ptyId, `${BRACKETED_PASTE_BEGIN}${draft.content}${BRACKETED_PASTE_END}`)
     })

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Advanced

Run ID: 1e9e23fe-0901-4f41-93fc-487c70d523ff

📥 Commits

Reviewing files that changed from the base of the PR and between 3bf8730 and e758f55.

📒 Files selected for processing (4)
  • README.md
  • src/main/zcode/hook-service.test.ts
  • src/main/zcode/hook-settings.ts
  • src/shared/agent-hook-listener/provider-event-routing.ts
💤 Files with no reviewable changes (1)
  • README.md
🚧 Files skipped from review as they are similar to previous changes (3)
  • src/shared/agent-hook-listener/provider-event-routing.ts
  • src/main/zcode/hook-settings.ts
  • src/main/zcode/hook-service.test.ts

Included review availability: Your plan provides up to 10 included reviews per hour; 4 remain after this review.

@coderabbitai

coderabbitai Bot commented Sep 23, 2026

Copy link
Copy Markdown
Contributor

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail
git status --short
git show --stat --oneline e758f554 -- | head -70
ast-grep outline src/main/zcode/hook-settings.ts --items all --match 'removeZCodeManagedHooks|applyZCodeManagedHooks'
ast-grep outline src/main/zcode/hook-service.test.ts --items all --match 'remove|Notification'
ast-grep outline src/shared/draft-paste-ready-scanner.ts --items all --match 'createDraftPasteReadyScanner'
ast-grep outline src/shared/agent-hook-listener/provider-event-routing.ts --items all --match 'isNewTurnEvent'
rg -n -C2 'Notification|removeZCodeManagedHooks|sawQuietAnchor|DECRST_ALT_SCREEN|grok-composer-prompt|zcode-composer-prompt|UserPromptSubmit|SessionStart' src/main/zcode/hook-settings.ts src/main/zcode/hook-service.test.ts src/shared/draft-paste-ready-scanner.ts src/shared/agent-hook-listener/provider-event-routing.ts
rg -n 'ZCode|Muse|zcode.z.ai|agent-icons/zcode.png' README.md | head -45

Length of output: 14460


🏁 Script executed:

#!/bin/bash
set -euo pipefail
sed -n '105,183p' src/main/zcode/hook-settings.ts
sed -n '95,154p' src/main/zcode/hook-service.test.ts
sed -n '30,92p' src/shared/draft-paste-ready-scanner.ts
sed -n '140,182p' src/shared/draft-paste-ready-scanner.ts
sed -n '224,280p' src/shared/draft-paste-ready-scanner.ts
git show e758f554 -- src/main/zcode/hook-settings.ts src/main/zcode/hook-service.test.ts src/shared/agent-hook-listener/provider-event-routing.ts README.md | head -195

Length of output: 18068


@nwparker, thanks for the fixes and the clear testing scope. I inspected e758f554. The hook-removal change leaves an unchanged event key intact, and the new regression test checks that Notification: [] survives. The ZCode README entry is no longer duplicated. The isNewTurnEvent comment now matches the code. I did not rerun the checks you reported.

I agree that the quiet-fallback concern also applies to Grok. I will leave that concern open for a separate change covering both agents. Would you like me to open a follow-up issue? Your distinction between SFTP test coverage and validation on a real SSH host is accurate.


✏️ Learnings added
Learnt from: nwparker
URL: https://github.com/stablyai/orca/pull/22464

Timestamp: 2026-09-23T23:18:50.732Z
Learning: In `src/shared/draft-paste-ready-scanner.ts`, `grok-composer-prompt` and `zcode-composer-prompt` both use an alternate-screen marker anchor and a separate bracketed-paste quiet anchor. Leaving the alternate screen revokes the marker anchor but does not clear the quiet anchor. A process that exits before painting its composer marker can therefore leave a quiet-fallback delivery risk for both agents; a follow-up should consider both consumers.

Learnt from: nwparker
URL: https://github.com/stablyai/orca/pull/22464

Timestamp: 2026-09-23T23:18:50.732Z
Learning: The ZCode remote hook installer tests exercise `installRemote` SFTP writes, but the ZCode hook installation in PR `#22464` was not tested against a real SSH host. Do not describe SFTP test coverage as real-host SSH validation.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.


If you found this review helpful, would you consider giving us a shout-out on X?

Thank you for using CodeRabbit!

You are interacting with an AI system.

@nwparker

Copy link
Copy Markdown
Contributor Author

ZCode harness is ready to test — installable builds below

PR #22464 adds ZCode as a first-class Orca agent. Before it's approved and merged I'd like confirmation from people who actually use ZCode that it behaves on real machines — mine is macOS-only, and the hook install touches your real ZCode config.

Once a couple of you confirm it works, we'll approve and merge.

Install (easiest path)

Adhoc dev build: https://github.com/stablyai/orca-adhoc/releases/tag/v1.4.210-adhoc.20260923230858

Platform File
macOS (Apple Silicon) orca-macos-arm64.dmg
macOS (Intel) orca-macos-x64.dmg
Windows orca-windows-setup.exe

macOS is signed and notarized, so it installs normally. Windows is unsigned — SmartScreen will warn; choose More info → Run anyway. This is an unmerged branch build and is auto-deleted after a retention window. If you'd rather not install an unvetted build, check out nwparker/harness-zcode and run from source instead.

You need ZCode's CLI on your PATH (zcode --version should work).

What to check

  1. It appears — New workspace → Agent dropdown → ZCode, with its logo.
  2. Auto-setup writes its config — after first launch, ~/.zcode/cli/config.json should contain "hooks": { "enabled": true, "events": { … } } with seven events. This edits your real ZCode config; it preserves your own hooks and key order, and Settings → Agents can remove it. Back it up first if that matters to you.
  3. Status — send a prompt; the sidebar row should show a spinner and your prompt text while it works.
  4. Questions/approvals — when ZCode asks something or wants to approve a command, the tab should read "ZCode - action required" and the sidebar should show the attention badge.
  5. Done + notification — on finish the tab should read "ZCode ready" with a completion indicator.
  6. Send to agent — type into Orca's terminal input box and confirm the text lands in ZCode's composer.
  7. Resume — reopen a past session and confirm zcode --resume <id> picks it up.

Where I most need help

  • Windows and Linux. I only tested macOS. The config path, the .cmd hook wrapper, and the installer are the risky parts.
  • SSH / remote workspaces. The remote hook install is covered by tests but has never run against a real host.
  • A real Z.ai account. My test machine has no model configured, so I drove status transitions by posting ZCode's real hook payloads rather than running live turns. Nobody has yet watched this work end-to-end through an actual ZCode session — that's the single biggest gap.

Please report back on #22464 with your OS, ZCode version (zcode --version), and what did or didn't work. A "works on Windows" is just as useful as a bug.

Notes on scope

Usage/rate-limit tracking (#21757, #18506) is not in this PR — it's a follow-up. Good news there: now that ZCode is open source, it turns out it records usage locally in ~/.zcode/cli/db/db.sqlite (model_usage, with per-request token columns), so a local provider can avoid the undocumented billing endpoint that thread was wary of. Same for session history in AI Vault.

Credit to the earlier attempts this builds on: #13965 / #10654 (@guanbear, @innocarpe) reached the same hook-install conclusion independently, and #21756 (@sunganhao8-lgtm) the same registration surface.

cc @JWu527 @sunganhao8-lgtm @ArtemBozhenko @guanbear @innocarpe @penghuizhang @blacktoast @christh @Codemaster64 @jerrdasur @justforyoudear @kevinjkinder @liuhaoxh @megastruktur @verylovestars @aspectrr @damarro3 @lucianweber @AmethystLiang @brennanb2025

@nwparker

Copy link
Copy Markdown
Contributor Author

test / tests node 24 8/8 is the known runtime-test leak, not this PR

Flagging this so nobody re-runs it a third time or treats it as a ZCode regression.

The failure is src/main/runtime/orca-runtime.test.ts, and it is the flake #22567 was opened to fix — same file, same assertion, same wandering symptom:

lineage-and-scan-cache-part-03.spec.ts:353
AssertionError: expected "vi.fn()" to be called 3 times, but got 4 times

#22567 describes the cause precisely: RuntimeLegacyWorkerTerminalRecoveryController.armRetry arms a backoff loop that nothing ever satisfies in the test file, so it keeps firing stray listWorktrees calls into whatever test happens to be running. Which assertion it lands on depends on machine speed — which is why it moves around and why every affected file passes in isolation. That PR notes it has been failing on unrelated PRs.

Matching evidence on this branch across three runs:

Run Failing test(s) Count
1 scan cache per repo; caller-terminal lineage expected 3, got 5
2 valid orchestration lineage; cwd lineage fallback —
3 scan cache per repo expected 3, got 4

Different tests, different counts, same file. A deterministic break would fail the same test with the same number every time.

Independently checked:

  • orca-runtime.test.ts passes locally on this branch: 1299 passed, 1 skipped.
  • This branch touches no runtime lineage or scan-cache code. The only src/main/runtime/ entries in the diff are my own transcript fixture and my own readiness test.
  • Shard 8/8 currently passes on other open PRs (e.g. feat(orchestration): let a structured chat run orchestration as itself #22568), so the shard is not broken for everyone — consistent with a load-dependent leak rather than an outage.

I'd rather not paper over it here: loosening the assertion would throw away a real invariant (scanning repo A twice and repo B once is what proves per-repo TTL expiry without sibling coupling). The fix belongs in #22567. Once that lands I'll rebase and this shard should go green without any change to ZCode code.

Everything else on this PR is green: 29 passing, including typecheck, static analysis, the mobile bundle, package (macOS + Windows), cross-version wire compatibility, and the SSH terminal/hooks e2e.

Add ZCode (Z.ai's `zcode` CLI) as a supervised Orca agent: managed lifecycle
hooks on local, SSH and Windows hosts; status, question and approval reporting;
synthetic status titles; session resume; orchestration worker launch options;
and desktop + mobile agent-picker registration.

Written against the newly open-sourced `zai-org/ZCode` (agent CLI 0.16.9), not
against a remembered screen:

- ZCode's hook runner writes a Claude-compatible stdin alias set, so it routes
  through the existing Claude-compatible vendor path while keeping its own
  identity in the sidebar.
- `PermissionRequest` fires only once the approval card is on screen and racing
  the user's answer, so it is proof the pane is blocked, not an auto-approval.
- ZCode's clarification tool is literally `AskUserQuestion` with Claude's
  questions/options shape, so Orca's question card renders it unchanged.
- ZCode's `hooks.enabled` defaults to false, which is why configured hooks were
  reported as never firing; the installer sets it.
- ZCode renames its own process to `zcode-cli`, so the expected foreground
  process cannot be the launch command or dispatch refuses the pane.
- ZCode emits no OSC title in any state and repaints its ASCII banner forever,
  so readiness comes from Orca's synthetic hook title and launch drafts wait on
  the composer box rather than on a quiet render window.

Three files crossed their max-lines limit, so each is split along a real seam:
command-line entrypoint parsing out of agent process recognition, skill
classification out of skill root discovery, and registry coverage out of the
remote hook installer tests.

Refs #10564
… contract

ZCode's CLI exposes no `--model` flag at all, and the session-option launch path
refuses to apply any option until a model id is chosen. A catalog therefore could
not deliver `--mode` per worker, and would have accepted `--model` only to drop
it silently. Take opencode's position instead: no catalog, so `worker-start
--model` is refused with a clear message and ZCode launches with the model from
its own config. `--mode` stays reachable through agent args, which is also how
the yolo default is applied.

Add a contract test covering the parts that make ZCode a usable worker:
dispatchable foreground process, stdin prompt delivery, the prompt staying out
of the launch command, and the composer-gated draft paste.
The mobile agent picker now bundles ZCode's icon, which the session route
reaches through the shared picker. Measured: the only module the closure gains
is `src/shared/agent-icons/zcode.png`, the same single local input Muse's icon
added.
No behaviour change; every ZCode test still passes.

- Use installer-utils' own `hookDefinitionHasManagedCommand` instead of
  re-walking a hook definition by hand, which also drops a local string reader.
- Share one `readZCodeEventMap` instead of keeping the same narrowing in both
  hook-settings and hook-config-json.
- Collapse five identical error returns into one `zcodeHookError` builder, and
  return early from the status branches instead of assigning through `let`.
- Split the event-to-status decision out of `normalizeZCodeEvent` into a pure
  `readZCodeTurn`, so the normalizer reads as decide-then-build and stops
  computing the tool name for events that never look at it.
- Take a script file name in `readManagedZCodeHookEvents` like its siblings,
  which removes a `Parameters<typeof …>` indirection at the call site.
- Drop the unused `ZCodeHookEvent` export and inline a single-use path helper.
- Correct a stale comment: ZCode's loader is a strict `JSON.parse`, so the
  in-place edit preserves key order and indentation, not comments.
…nt, de-dupe README

- `removeZCodeManagedHooks` deleted any event key whose list ended up empty, so an
  unrelated `"Notification": []` the user wrote was removed as collateral whenever a
  managed hook elsewhere made the write happen. Only touch an event Orca actually
  owned something in; covered by a new regression test.
- The `isNewTurnEvent` comment claimed UserPromptSubmit was ZCode's only turn
  boundary while the expression below it also returned true for SessionStart. Say
  what the code does: SessionStart lands the idle boundary, UserPromptSubmit is the
  turn boundary (the Codex/Claude shape).
- ZCode appeared twice in the README's single agent-badge block; keep the
  local-icon entry the link checker validates and drop the favicon duplicate.
@nwparker
nwparker force-pushed the nwparker/harness-zcode branch from e758f55 to e26b5ba Compare September 24, 2026 00:43
@nwparker

Copy link
Copy Markdown
Contributor Author

Rebased past #22567 — CI is fully green (31/31)

test / tests node 24 8/8 now passes. That confirms the diagnosis: the failure was the runtime-test retry leak #22567 fixed, not anything in this PR. orca-runtime.test.ts runs 1301 passing locally on the rebased branch (up from 1299 — #22567 added its own retry coverage).

Three conflicts came up in the rebase, all resolved in this PR's favour rather than by taking either side blindly:

  • README, twice. docs: remove duplicate Muse badge from README #22497 landed on main and removed the duplicate Muse badge — the same wart I flagged earlier in this thread. Since this PR also drops its duplicate ZCode badge, both conflicts resolve to neither duplicate. The block now has exactly one Muse and one ZCode entry.
  • Mobile closure pin. Main moved it 4216 → 4218 (the page safe-area work added two modules). ZCode's icon takes it to 4219, which I measured with the closure test rather than inferring from the arithmetic. Main's safe-area note is kept and ZCode's is appended below it, so the pin's history still reads in order.
  • Locale files auto-merged.

Verification on the rebased branch: pnpm tc clean, README link check passes, 11,946 tests passing across the affected trees, changed-code quality gate 0 findings, and the full CI run green including the mobile bundle, static analysis, package (macOS + Windows), cross-version wire compatibility, and the SSH terminal/hooks e2e.

The adhoc build linked above was cut from the pre-rebase commit; it is unaffected by the rebase (README, a test pin, and locale merges only — no ZCode behaviour changed), so it is still the right build to test against. Testing notes are unchanged: #22464 (comment)

Still waiting on a live-session confirmation before merge — everything automated is green, but nobody has yet run a real ZCode turn with an actual Z.ai account, and Windows/Linux/SSH remain untested on real hardware.

@JWu527

JWu527 commented Sep 24, 2026

Copy link
Copy Markdown

Confirmed a live session on macOS Apple Silicon with a real BigModel / Z.ai Coding Plan account.

Environment

  • Orca: v1.4.210-adhoc.20260923230858 (orca-macos-arm64.dmg)
  • OS: macOS 26.6.2 / arm64
  • zcode --version: zcode-app-cli 3.14.3-27 / zcode-runtime 0.16.9

What worked

  1. ZCode appears in the agent picker with its icon.
  2. First launch wrote managed hooks into ~/.zcode/cli/config.json — hooks.enabled: true and all seven events (SessionStart, UserPromptSubmit, PreToolUse, PostToolUse, PostToolUseFailure, PermissionRequest, Stop) pointing at ~/.orca/agent-hooks/zcode-hook.sh.
  3. After fixing the local CLI (see below), a live ZCode pane in Orca accepts prompts and completes a turn. I also verified headless zcode -p returns a real model reply (pong) with this account.

CLI caveat for other testers
Pointing zcode at the Desktop app’s bundled /Applications/ZCode.app/Contents/Resources/glm/zcode.cjs (node-bundle, sea: no) fails to open the TUI with:

Cannot find package '@zcode/tui' imported from .../zcode.cjs

That blocked Orca until I switched PATH to community zcode-app-cli (packages runtime 0.16.9 + a TUI; docs mention hosts like Orca). Worth calling out so others who only installed ZCode Desktop don’t think the harness is broken.

I did not deeply exercise questions/approvals, send-to-agent, or --resume in this pass — happy to retest those if useful before merge.

Thanks for the adhoc builds and the checklist.

@JWu527

JWu527 commented Sep 24, 2026

Copy link
Copy Markdown

Happy to help with more live testing (questions/approvals, send-to-agent, resume) if useful before merge — and I’d also be glad to contribute code to Orca going forward when there’s a good fit.

…sion

From live testing on #22464: pointing `zcode` at the desktop app's bundled
`glm/zcode.cjs` installs Orca's hooks fine but then fails with
`Cannot find package '@zcode/tui'`, so the pane never opens a session. The
symptom reads as a broken harness when the CLI simply has no TUI. Say which
build to use and how to check before reporting a problem.

Reported-by: JWu527
@nwparker

Copy link
Copy Markdown
Contributor Author

@JWu527 — thank you, this is exactly the confirmation the PR was missing. A live turn on a real Coding Plan account is the one thing I couldn't produce myself, and hooks landing correctly (enabled: true + all seven events) plus a completed turn covers the core of it.

Your @zcode/tui finding is the most valuable part of the report, and I've documented it in 53e9c6f4 rather than leaving it in a comment thread. I hit the identical wall while building this — the desktop app's bundled glm/zcode.cjs starts, Orca's hooks install correctly against it, and then it dies with Cannot find package '@zcode/tui'. Because the hooks do install, the failure reads as a broken harness when it's really a CLI with no TUI. That's a bad first-run experience for anyone who has only installed ZCode Desktop.

The docs page now has a "ZCode: pick a CLI that ships the TUI" section with the exact error, why it's misleading, and a zcode --version check before blaming the harness. I kept the wording distribution-neutral — build from zai-org/ZCode or use a distribution that packages the runtime with the TUI — since Orca integrates with the first-party CLI contract and shouldn't be steering people to a particular package.

Worth noting for the record: your zcode-runtime 0.16.9 matches the version I built from zai-org/ZCode and tested against, so we exercised the same runtime by two different routes.

Yes please to the follow-up pass, if you have the time — questions/approvals, send-to-agent, and --resume are the three I verified through hook payloads and the transcript fixture rather than a live account:

  1. Questions/approvals — get ZCode to ask something or request approval for a command. The tab should read "ZCode - action required" and the sidebar should show the attention badge.
  2. Send to agent — type into Orca's terminal input box at the bottom and confirm the text lands in ZCode's composer.
  3. Resume — reopen a past ZCode session and confirm it picks up (zcode --resume <id>).

Any of the three working is useful; any of them not working is more useful.

And yes — contributions very welcome. The two follow-ups already scoped out of this PR are a local usage provider reading ~/.zcode/cli/db/db.sqlite (model_usage) for #21757/#18506, and ZCode session history in AI Vault off the same DB. Both are self-contained if either appeals.

@nwparker

Copy link
Copy Markdown
Contributor Author

@JWu527 your @zcode/tui finding is now a fix, not just a docs note: #22730.

Orca watches a freshly launched ZCode pane's first output and, when it sees that exact failure, replaces the bare stack trace with an explanation — that Orca's hooks are fine, that this zcode simply cannot open a session, and what to install instead.

Two things I found while building it that are worth knowing:

--version and zcode doctor cannot tell the two builds apart. I have both on this machine and their output is identical in shape — which is precisely why the broken one feels healthy. The only reliable discriminator is what happens when you ask for a session:

Build zcode with stdin closed
Desktop bundle (no TUI) Cannot find package '@zcode/tui'
Built from zai-org/ZCode (has TUI) TUI requires an interactive terminal.

The rule keys on the failure, not the success. Matching TUI requires an interactive terminal. would have been the obvious approach, but ZCode localizes it — TUI 需要交互式终端。 in zh-CN — so it would have silently missed every non-English user. Node's module-resolution error isn't translated.

#22730 is stacked on this PR since it needs zcode to exist as an agent, so this one still merges first.

Still useful if you have time: questions/approvals, send-to-agent, and --resume on a live account. Those three are the remaining items I verified through hook payloads rather than a real session.

This branch has not been deployed

No deployments
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.

2 participants