Skip to content

zcode-cli 3.8.1-32

Latest

Choose a tag to compare

@xhqing xhqing released this 09 Sep 15:15
· 1 commit to main since this release

Fixes the 3.8.1-31 known issue: while signed out (custom-provider config only, no stored login), picking any model from the /model list connected to nothing. Model switching now resolves credential-less official-slot references to the same provider's custom-env slot. The model pickers also drop the internal env- prefix, the signed-out list is restricted to custom.env models, and the custom-provider config file is renamed to custom.env with automatic legacy migration.

Fix: signed-out /model selection was unusable (3.8.1-31 known issue)

Why: In 3.8.1-31 the deduplication kept the official entry and dropped its env-slot twin. The official bigmodel slot is managed by /login and carries no credential while signed out, so setModel("bigmodel/glm-5.3") reached the runtime with no API key — while the credentialed env-bigmodel entry was exactly the one deduplication removed.

What changed:

  • src/identity.ts adds resolveModelSlotRef(): an official slot ref (<provider>/<model>) is returned unchanged when the provider has a vault login or an official-slot key; with neither, it falls back to the env-<provider> slot when that slot carries a key and declares the model. The fallback is judged per provider (signing in to zai does not affect picking a bigmodel model). Env-slot refs, models not declared in the env slot, and providers without a keyed env slot pass through unchanged.
  • All three model-switching entry points in the TUI — switchTransientModel (shared by the /model list, manually typed /model <provider>/<model>, and the quick cycle toggle) and the /settings → Model providers session apply — pass through resolveModelSlotRef() before sending to the bridge. /settings persistence writes the resolved slot ref, so a signed-out save points directly at the usable env slot (the post-login switchModelBlockToOfficialProvider migration is unchanged).
  • selectors.ts current marking now matches both forms (the internal slot id env-<provider>/<model> and its prefix-free display form), fixing the lost "current" marker and /settings preselection that 3.8.1-31 introduced.

Compatibility: The 3.8.1-31 dedup direction (each model listed once, official entry wins) is unchanged — the fix does not touch withoutEnvSlotTwins(); it extends the fact that the env slot is the only usable path while signed out from the display layer to the switching layer. Env-only entries and signed-in behavior are identical to 3.8.1-31; the 3.8.1-26 "the env file is the model-block authority while signed out" semantics are unaffected.

Verification: tsc --noEmit passes; 7 new identity unit tests (signed-out fallback, undeclared model passes through, no keyed env slot passes through, official-slot key passes through, vault token passes through, per-provider independence, env ref and no-slash alias pass through) and 2 new selector unit tests (current marker matches the env-slot form of an official twin in the flat picker and the provider cascade); full bun test green; all 5 TUI smoke tests pass.

Model pickers: no more env- prefix; signed-out list shows only custom.env models

Why: The internal env-<provider> slot ids leaked into the display layer — env-only entries showed up prefixed in the flat /model list, and /settings → Model providers split one provider into duplicate official + env groups. After the 3.8.1-31 incident the list layer is also tightened: while signed out, only entries that actually carry credentials are listed, because nothing else can connect.

What changed:

  • modelPicker() / providerModelPicker() gain a third signedIn?: boolean parameter: undefined keeps the full list (backwards compatible), false keeps only env-slot entries, true shows everything. Entry values, labels, and generated /model commands all go through displayModelRef(); cascade grouping keys switch to displayProviderId(), merging official and env slots of the same provider into one group with twin-deduplicated entries; group-name fallbacks drop the prefix; current marking and preselection match both forms. With signedIn === false, both the flat and cascade views filter out non-env-slot entries.
  • All three call sites (showModelPicker, showModelProviderSettings, the quick cycle switchModel) pass the sign-in state from readSignedInProvider(). An empty /model list no longer opens a silent empty picker — it explains per sign-in state (signed out: "sign in (/login) or configure ~/.zcode/cli/custom.env").
  • Selection still resolves through resolveModelSlotRef() to a credentialed slot, so every listed entry is actually usable.

Verification: the acceptance group (11 cases, including selection-path guards R7/R8 reproducing the 3.8.1-31 incident and signed-out filtering R9/R10) is green; tsc --noEmit passes.

Config file rename: custom-provider.env → custom.env (automatic migration)

Why: Per the project naming convention, provider in custom-provider.env is a redundant modifier — the name simplifies to custom.env. Existing machines had the old file, so automatic migration was required.

What changed:

  • src/env-config.ts: the filename constant is renamed to customEnvFileName with value custom.env (single source of truth). migrateLegacyEnvFile() grows from one legacy layer (.env) to a two-layer chain — custom-provider.env first, .env as fallback; migration is skipped when custom.env already exists (coexisting old files are kept untouched) or when ZCODE_ENV_FILE overrides; at most one rename per launch.
  • The migration notice in src/launcher.ts reports the actual destination path instead of hardcoding .env; the signed-out hint in src/identity.ts uses the new name.
  • The template is renamed custom-provider.env.example → custom.env.example; docs/CONFIGURATION.md and the install sections of all three READMEs are updated.
  • Tests: path assertions use the new name; the migration suite covers four cases (long-name migration, direct .env migration, coexistence prefers the long name and keeps .env, override skips).

Verification: the acceptance group (5 cases) is green; tsc --noEmit passes.

v3.8.1-31 marked as pre-release

The previous release carried the signed-out selection defect above; it was marked pre-release so the Latest pointer falls back to 3.8.1-30, and a known-issue warning was added at the top of its notes pointing to the fixed version. The release itself is kept for history. With this release published as Latest, latest/download links point at the fixed version again.

Install links are now tag-pinned

All three READMEs and docs/RELEASING.md switched from releases/latest/download/... to tag-pinned URLs — the latest pointer moves on every publish while the asset name carries the version, so historical latest/download links go 404. Tag-pinned URLs always resolve to that version's asset.

Tests & CI

  • Two acceptance-case groups from Hopper (TestEngineerAgent) were synced, implemented against, and archived: model-picker-env-prefix (11 cases) and custom-env-rename (5 cases).
  • Regression-net gap filling: six new test files with 46 cases covering previously untested modules (tool-group-view, command, protocol-part-view, renderable, plugin-protocol, clipboard-text); one known defect is locked with test.failing (captureCommand throws ERR_STREAM_PREMATURE_CLOSE instead of returning {code: 1, stderr} when the target binary is missing — affects the update fallback for users without gh and the browser-opening fallback in the OAuth flows).
  • CI gate slimmed: the validate job dropped the release build (90% of its runtime) in favor of a vendor-runtime cache keyed on zcode-runtime.lock.json + scripts/sync-runtime.ts, and no longer re-runs on pushes to main; timeout 45 → 20 minutes; cold-cache full run ~3.5 minutes.
  • Housekeeping: subproject paths in .claude/CLAUDE.md updated from ~/Documents/Projects/ to ~/Developer/ after the project directory migration.

Install

npm install -g https://github.com/xhqing/zcode-cli/releases/download/v3.8.1-32/zcode-cli-3.8.1-32.tgz