Repository navigation
Releases: xhqing/zcode-cli
Release list
zcode-cli 3.8.1-32
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.tsaddsresolveModelSlotRef(): 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 theenv-<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/modellist, manually typed/model <provider>/<model>, and the quick cycle toggle) and the/settings → Model providerssession apply — pass throughresolveModelSlotRef()before sending to the bridge./settingspersistence writes the resolved slot ref, so a signed-out save points directly at the usable env slot (the post-loginswitchModelBlockToOfficialProvidermigration is unchanged). selectors.tscurrent marking now matches both forms (the internal slot idenv-<provider>/<model>and its prefix-free display form), fixing the lost "current" marker and/settingspreselection 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 thirdsignedIn?: booleanparameter:undefinedkeeps the full list (backwards compatible),falsekeeps only env-slot entries,trueshows everything. Entry values, labels, and generated/modelcommands all go throughdisplayModelRef(); cascade grouping keys switch todisplayProviderId(), 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. WithsignedIn === false, both the flat and cascade views filter out non-env-slot entries.- All three call sites (
showModelPicker,showModelProviderSettings, the quick cycleswitchModel) pass the sign-in state fromreadSignedInProvider(). An empty/modellist 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 tocustomEnvFileNamewith valuecustom.env(single source of truth).migrateLegacyEnvFile()grows from one legacy layer (.env) to a two-layer chain —custom-provider.envfirst,.envas fallback; migration is skipped whencustom.envalready exists (coexisting old files are kept untouched) or whenZCODE_ENV_FILEoverrides; at most one rename per launch.- The migration notice in
src/launcher.tsreports the actual destination path instead of hardcoding.env; the signed-out hint insrc/identity.tsuses the new name. - The template is renamed
custom-provider.env.example→custom.env.example;docs/CONFIGURATION.mdand 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
.envmigration, 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) andcustom-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 withtest.failing(captureCommandthrowsERR_STREAM_PREMATURE_CLOSEinstead of returning{code: 1, stderr}when the target binary is missing — affects theupdatefallback for users withoutghand the browser-opening fallback in the OAuth flows). - CI gate slimmed: the
validatejob dropped the release build (90% of its runtime) in favor of a vendor-runtime cache keyed onzcode-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.mdupdated 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
zcode-cli 3.8.1-30
The TUI footer no longer falls back to the literal "default" for the model name: startup now resolves the configured <provider>/<model_id> form.
Why: A user reported (with a screenshot) that after running zcode, the footer showed ◇ default — ◉ yolo — … and asked for the model name to always display <provider>/<model_id>. Root cause: the official runtime's login gate only checks the official zai/bigmodel slots, so boots with model access configured in an env-file slot (env-<id>, custom-provider.env) are marked loginRequired and receive no startup model metadata. The TUI constructor fell back to the literal "default" for a missing initialModel, and the run() startup sequence had no config-based fallback — the existing readConfiguredModelAccess() fallback in handleResult only triggered after the first message was submitted, so the footer showed "default" from first paint until the first message.
What changed:
- The TUI
run()startup sequence gainsresolveStartupModel()(awaited before the first frame is drawn): whenthis.modelis "default" (the marker for missing startup metadata), it first consultsreadConfiguredModelAccess()— on a hit (the slot pointed to by model.main declares the model and carries a key) it backfillsdisplayModelRef(access.model)for display and corrects loginRequired to false under the existing semantics (consistent with the re-check semantics ofhandleResult/handleLocalLogin); on a miss it falls back to the newreadConfiguredMainModel()(src/model-access.ts, which only readsmodel.mainfromconfig.jsonwithout requiring a key in the slot) — so loginRequired boots without configured access also show the configured model (e.g. the defaultzai/glm-5.2) instead of "default". - Display goes through
displayModelRef(), which uniformly strips theenv-prefix: the env slotenv-bigmodel/glm-5.3displays asbigmodel/glm-5.3. - Smoke tests (scripts/smoke-tui.ts): the main flow gains an assertion that before login (pristine HOME, no key) the footer already shows
◈ zai/glm-5.2(covering the main-fallback path), plus a new standaloneverifyEnvSlotModelDisplay()section that pre-writes an env- slot + key + model.main config to replicate the user's scenario and asserts the footer showsbigmodel/glm-5.3with no◈ defaultanywhere (covering the access-hit path); the lenient◈ defaultfallback branch of the/mode planassertion is tightened to a concrete model form so regressions cannot hide. - model-access unit tests gain two
readConfiguredMainModelcases: main is readable even without a key; missing / blank / unreadable JSON returns null. - Version bumped to 3.8.1-30 (VERSION, package.json, test/update.test.ts, version badges and install URLs in all three READMEs).
Compatibility assessment: 3.8.1-25 established the re-check semantics for loginRequired (an env slot with configured access is not treated as unconfigured; the warning was removed in 3.8.1-28 but the re-check semantics stayed in handleResult). This change simply moves that re-check from "after the first message" to "at startup" — an addition, not a weakening. The identity semantics of 3.8.1-27 (env-slot access counts as "not signed in"; the banner identity only recognizes official slots / OAuth) are untouched — in the env-slot scenario the banner still shows "Not signed in" while the footer shows the model; the two coexist without contradiction (sign-in identity and model availability are separate concerns). The existing handleResult fallback and modelLabel() behavior are unchanged; normally signed-in boots (official slot / OAuth) still get model metadata directly from the runtime and never go through the new path.
Verification: tsc --noEmit passes; full bun test 693 pass / 0 fail (78 files); all 5 TUI smoke tests pass (smoke-tui with the two new assertions and the env-slot section; features / clear / pressure / widths without regression); after build:tui the vendored @zcode/tui copy was synced manually following the same steps as installLocalTui (no runtime changes).
Install
npm install -g https://github.com/xhqing/zcode-cli/releases/latest/download/zcode-cli-3.8.1-30.tgz
zcode-cli 3.8.1-29
Always show the sign-in state in the TUI welcome banner
- Always show the sign-in state in the TUI welcome banner: every boot state now renders an identity status line, no more blank space (packages/zcode-tui/src/index.ts, packages/zcode-tui/src/login-identity.ts, packages/zcode-tui/src/welcome-banner.ts, test/welcome-banner.test.ts, scripts/smoke-tui.ts; version bumped to 3.8.1-29 across VERSION, package.json, test/update.test.ts, and the three READMEs' badges and install URLs).
- Why: the user reported that running
zcodeshowed no sign-in information in the welcome banner — you could not tell whether the session was Signed in or Not signed in. Signed-in (OAuth / key) and custom-provider boots already had their identity line (the system built in 3.8.1-26 / 27); only the unconfigured-model-accessloginRequiredboot was blank — the identity was hard-set to undefined in that state so the banner rendered no identity line, and after 3.8.1-28 removed the persistent warnings there was nothing left at all. - What changed: the TUI adds
readBannerIdentity()— whenloginRequired=true(the unauthenticated boot where the runtime skips model loading) or the identity snapshot read fails / returns undefined (nothing configured at all), it falls back to asignedOutidentity so the banner shows "Not signed in"; both the first paint (run()) and the identity refresh (refreshLoginIdentity()) now go through it. Status-bar metadata behavior is unchanged (signedOutstill emits no user / key fields); the snapshot functionreadLoginIdentitySnapshot()and its undefined semantics stay untouched (other callers such aszcode identityare unaffected — the undefined fallback exists only at the banner layer). - Traceability & regression assessment: what 3.8.1-28 removed was guiding warning copy ("Model access is not configured. / Run /login..."), while this change adds a factual state line ("Not signed in" / "Signed in as ..." / "API key ...") — different in nature, and this was an explicit user request ("please add the default sign-in state"), so it is not a regression; the identity priority established in 3.8.1-26 / 27 (OAuth account name > key mapping name > masked key > signedOut) is fully unchanged, only adding the display fallback for the undefined case.
- Verification:
tsc --noEmitpasses; fullbun test691 pass / 0 fail (78 files; welcome-banner adds two cases for the signedOut identity line in wide / compact layouts); TUI smoke all 5 green (smoke-tui's first screen gains a "Not signed in" assertion — a pristine HOME's loginRequired boot shows the line from the very first paint); afterbuild:tuithe vendored@zcode/tuicopy was synced manually following theinstallLocalTuistep (the runtime itself is unchanged).
- Why: the user reported that running
Install
npm install -g https://github.com/xhqing/zcode-cli/releases/download/v3.8.1-29/zcode-cli-3.8.1-29.tgzzcode-cli 3.8.1-28
Remove the persistent two-line model-access warning from the TUI banner
- Remove the persistent two-line "Model access is not configured. / Run /login, or configure a custom provider in ~/.zcode/cli/config.json." warning from the top of the TUI (packages/zcode-tui/src/index.ts; version bumped to 3.8.1-28 across VERSION, package.json, test/update.test.ts, and the three READMEs' badges and install URLs).
- Why: the user ruled this hint out. With model access unconfigured, two yellow warning lines sat permanently under the welcome banner — long-standing screen space whose guidance duplicated the /login wizard.
- What changed: the
loginWarning/loginHelpText components, their layout mounts, and theupdateLoginWarning()method are deleted; theloginRequiredflag stays untouched (it still drives runtime behaviors such as skipping model loading, Goal / Session usage refresh, and identity refresh timing). The "Model access is not configured yet." status note inside the first-run /login guide dialog is unaffected — that is in-wizard interactive copy, not the persistent banner. - Traceability & regression assessment: the warning had shipped in 3.8.1-25 with compensating logic (when
loginRequired=true, re-check viareadConfiguredModelAccess()and hide the warning when access exists) guarding against the false positive "warning shown although an env slot is configured" — with the warning removed wholesale, that false positive can no longer occur, so this is a superset rather than a regression; the trade-off is that a genuinely unconfigured user no longer gets any guidance hint after startup (the first-run wizard remains, with its in-wizard status note visible) — the user-ruled intended behavior. - Verification:
tsc --noEmitpasses; fullbun test689 pass / 0 fail (78 files); TUI smoke all 5 green (smoke-tui / features / clear / pressure / widths); afterbuild:tuithe vendored@zcode/tuicopy inside the locked runtime was synced manually (that daysync:lockedcould not run the full chain due to a CDN download interruption; the runtime itself was unchanged — only the new dist was copied, equivalent to theinstallLocalTuistep).
Install
npm install -g https://github.com/xhqing/zcode-cli/releases/download/v3.8.1-28/zcode-cli-3.8.1-28.tgzzcode-cli 3.8.1-31
⚠️ Known issue in this version: while signed out (no stored login, custom-provider.env only), picking a model from the/modellist fails to connect to the upstream — the deduplicated list keeps official-slot entries that carry no credential in that state. Fixed in 3.8.1-32; until then, use 3.8.1-30.
The /model picker no longer lists env-file slot entries (env-<provider>/<model>) when the official prefix-free twin (<provider>/<model>) is also listed.
Why: A user reported (with a screenshot) that the /model list showed six entries: the three prefixed ones (env-bigmodel/glm-5.3, env-bigmodel/glm-5-turbo, env-bigmodel/glm-5.3-flash) duplicated the three prefix-free ones (bigmodel/glm-5.3, …), pointing at the same models; the user ruled the prefixed variants redundant. Root cause: the user was signed in through the official bigmodel slot (API key) while the same provider was also configured in custom-provider.env — the launcher writes the latter into the env-bigmodel slot of config.json, and the upstream runtime's listModels() reports both slots, so every model appeared twice in the picker.
What changed:
selectors.tsgainsofficialTwinId()(computes the prefix-free twin id of an env-file slot entry) andwithoutEnvSlotTwins()(drops env-file slot entries whose official twin is listed; env-only entries without a twin stay — they are the only selectable path while signed out with just a custom provider).- Both
modelPicker()(shared by the/modellist and the quick cycle toggle) andproviderModelPicker()(the/settings → Model providerscascade) pass through this filter; in the cascade, a provider group whose env-slot models are all shadowed disappears entirely. - The dual-slot config.json structure, the sign-in / sign-out flows (
switchModelBlockToOfficialProvideretc.), and runtime behavior are untouched. - Version bumped to 3.8.1-31 (VERSION, package.json, test/update.test.ts, version badges and install URLs in all three READMEs).
Compatibility assessment: Since 3.8.1-26, displayProviderId() / displayModelRef() have only affected the display of the current model name (status bar, banner); /model list entries still used the raw ids. This change extends the same "the env- prefix is configuration plumbing, not for users to see" principle to the picker — same direction, not a regression. Env-only entries (no official twin) are kept, so the /model list of signed-out custom-provider users is unaffected (pinned by a unit test). The existing same-id dedup of modelPicker and the per-group dedup of providerModelPicker are unchanged — entries are deduplicated first, then twins filtered.
Verification: tsc --noEmit passes; three new selector unit tests (dual-slot listing drops env entries with the current marker on the official entry, env-only entries are kept, a fully shadowed env group disappears from the cascade); full bun test 696 pass / 0 fail (78 files); all 5 TUI smoke tests pass (after build:tui, the vendored @zcode/tui copy was synced manually following the same steps as installLocalTui).
Install
npm install -g https://github.com/xhqing/zcode-cli/releases/download/v3.8.1-31/zcode-cli-3.8.1-31.tgz
zcode-cli 3.8.1-27
Treat official-slot API key sign-in as login (vault token first, key second), clear official-slot keys on logout, and force-collect a user name on BigModel login bound to the key mapping
- API key sign-in counts as signed-in: sign-in detection upgrades from "vault OAuth token only" to a two-layer check (vault token first, official-slot key second), and logout clears official-slot keys as well (src/identity.ts, src/launcher.ts, packages/zcode-tui/src/index.ts, test/identity.test.ts, test/login-identity.test.ts, README.md, README_zh_hans.md, README_zh_hant.md, docs/CONFIGURATION.md).
- Why: 3.8.1-26 anchored sign-in state solely to the vault
oauth:<provider>:access_token. A user who completed /login by pasting an API key (the wizard reported success, the key landed in the official slot, the model switched to the official slot) still saw a "Not signed in" banner and no identity field in the status bar — the login flow said success while the identity display said signed out, a contradiction; it was also a display regression versus 3.8.1-25 (an official slot holding a key already showed a key identity) and violated the 3.8.1-26 principle that "a user action must produce correct feedback". The product ruling: a key pasted via /login is a login; only access through a custom-provider file (env-slots) counts as signed out. - What changed: (1) layered sign-in detection (new
readSignedInProvider()in src/identity.ts): a vault OAuth token wins; with no token, a key in the officialzai/bigmodelslots also counts as signed in (the official slotmodel.mainpoints to is checked first, otherwise scan zai-first); (2) key identity restored for key sign-ins (readLoginIdentitySnapshot()): a key sign-in shows the key identity while an OAuth sign-in still shows the account name with absolute priority;signedOutnarrows toenv--slot-only access; (3) mapping names never impersonate account identity (a second same-day ruling): names inbigmodel-users.jsonare user-chosen key aliases and two accounts may share a name, so the "Signed in as " phrasing is reserved for OAuth account sign-ins (the account name the system actually read); key sign-ins always use the "API key" phrasing — with a mapping:API key <name> (<masked key>)(the snapshot carries a newkeyMaskedfield), without:API key <masked key>— switching accounts (changing keys) necessarily changes the display, with one consistent reading across the banner / status bar /zcode identity/ thezcode loginalready-signed-in notice; (4) the launcher adopts the new detection (two places in src/launcher.ts): the startup-syncskipModelBlock(a signed-in user'smodelblock is not taken over by the env file) and the barezcode login"already signed in" gate both cover key sign-ins — after pasting a key and restarting, the model is no longer pinned back to anenv-slot; (5) logout clears official-slot keys (clearOAuthLoginCredentials()extended): beyond the vault it also clears the config official slots' apiKey (env-slots are kept to serve the signed-out path) — a key sign-out now truly returns to signed-out instead of "logged out but the identity lingers"; (6) TUI status bar fix (index.ts): asignedOutidentity no longer emits a status-bar field (the old logic rendered a stray emptyuserlabel); a named identity's prefix changes fromusertokeyand carries the masked key; (7)zcode identity setis refused in a key sign-in state and points to thebigmodel-users.jsonmapping (a key's display name belongs to the mapping file; only OAuth account names belong to identity set); (8) BigModel login force-collects a user name (user ruling: "however you sign in, running /login first forces a user-name entry"): any TUI /login that goes through a BigModel option (OAuth or pasting a key) — whether picked from the plain /login menu or typed as the command — pops a name input before the login runs (non-empty enforced, Esc cancels the login; the placeholder previews the key's existing mapping name); on success the name is automatically upserted intobigmodel-users.jsonbound to the landed key (newwriteBigmodelUserName()in src/bigmodel-users.ts; hand-edited and login-collected entries are equivalent and freely mixable), and the display immediately refreshes toAPI key <name> (<masked key>). Z.AI sign-ins are not asked (the OAuth flow itself writes back the account name); a failed or cancelled login writes nothing; (9) docs synced (the sign-in permission sections of all three READMEs, the Sign-in identity and custom-provider sections, CONFIGURATION.md's login definition, logout behavior, and key-mapping sections). - Behavior map: /login with a pasted key → the key lands in the official slot + the model switches to the official slot + the banner immediately shows "API key ()" or "API key " + the status bar shows a
key <name> (<masked>)field; restarting in a key sign-in state keeps the model on the official slot (the env file no longer takes over); /logout → vault + official-slot keys fully cleared, the banner returns to "Not signed in", and the env file takes the model back on the next start. OAuth sign-ins keep showing "Signed in as " exactly as in 3.8.1-26. - Verification:
tsc --noEmitpasses; fullbun test689 pass / 0 fail (78 files; identity gains 4 key-sign-in identity + 3 readSignedInProvider + 1 identity-set-in-key-state + 2 writeBigmodelUserName cases, the logout cases now assert official-slot keys are cleared, login-identity gains 3 key sign-in + 2 shouldPromptForLoginUserName cases); the release build's TUI smoke (scripts/smoke-tui.ts) adds live coverage of the user-name prompt — in the BigModel paste-key flow it waits for the name prompt after the key enter, typessmoketo continue the login, and adds a bannerAPI key smoke (<masked>)assertion plus abigmodel-users.jsonbinding assertion (the mapping file is addressed by the raw key, permission 0600, sits beside config.json as a sanctioned key store, joins the smoke leak whitelist, and is positively asserted). - Upstream research (2026-09-05, no code changes): confirmed upstream 3.11.2 (the official stable, three minors ahead of this project's locked 3.8.1) still fails to obtain the BigModel login user name — reversing the 3.11.2 runtime shows the shared credential store is structurally identical to 3.8.1 (only zai has a user_info slot) and
loginBigmodelCodingPlanis structure-for-structure isomorphic (authorize → exchangeCode → exchange key → write config, never fetching a user name); the official changelog corroborates (the only login-related entries after 3.8.1 are three stability fixes: expired sign-in state / authorization callback failure / mcp oauth failure). Incidental finding: during the key exchange the runtime callsGET bigmodel.cn/api/biz/customer/getCustomerInfo, whose response carries account / organization info but only the ID is kept and then discarded, and the accessToken is never persisted nor callable outside the flow — this spawned two follow-ups (T3: evaluate upgrading the runtime to 3.11.2; T4: research the key → account info API).
- Why: 3.8.1-26 anchored sign-in state solely to the vault
Install
npm install -g https://github.com/xhqing/zcode-cli/releases/download/v3.8.1-27/zcode-cli-3.8.1-27.tgzzcode-cli 3.8.1-26
Make sign-in state first-class: instant identity switch after /login, custom-provider.env as the signed-out fallback with automatic hand-off
- Make sign-in state first-class: after /login completes browser OAuth the banner and status bar switch to the signed-in account immediately, and the custom-provider env file is repositioned as the signed-out-only channel with automatic hand-off in both directions (src/identity.ts, src/env-config.ts, src/launcher.ts, packages/zcode-tui/src/login-identity.ts, packages/zcode-tui/src/index.ts, test/identity.test.ts, test/login-identity.test.ts, test/env-config.test.ts, README.md, README_zh_hans.md, README_zh_hant.md, docs/CONFIGURATION.md,
.env.examplerenamed tocustom-provider.env.example).- Why: after completing OAuth in the TUI (/login), the banner kept showing the masked API key from the custom-provider file — a successful login with zero visible feedback. The root cause sits in the 3.8.1-25 decoupling design: identity display followed the
model.mainprefix, which the custom-provider file pinned to itsenv-<id>slot on every start, so the official slots and the credential vault written by OAuth never got a turn; and under the old design, seeing your login account required deleting the file and re-creating it after logout — unusable as a product. The ruling product principle: a user action must produce correct feedback — signed in shows the signed-in account, signed out shows Not signed in, and the two states must hand off automatically without manual file juggling. - What changed: (1) sign-in detection (new
readStoredOAuthLogin()in src/identity.ts): the presence ofoauth:<provider>:access_tokenin the credential vault~/.zcode/v2/credentials.jsonis the sign-in state (theoauth:active_providermarker takes priority; a stale marker falls back to scanning both providers' tokens); (2) login-first identity display (readLoginIdentitySnapshot()rewritten): signed in → show the login provider's account identity (vaultuser_infosnapshot → BigModel key-name mapping → masked key), regardless of which slotmodel.mainpoints to — visible on the refresh right after login; signed out with model access → new kindsignedOut, banner / status bar show "Not signed in" and the provider displays the declared value with theenv-prefix stripped; no access at all → the sign-in wizard warning stays. The TUI side (login-identity.ts) no longer mirrors the logic — it reuses the src snapshot functions (single implementation, no duplicate drift); (3) automatic model-ownership switch (env-config.ts): while signed in, the startup sync setsskipModelBlock(refresh only theenv-slot data, never rewrite themodelblock); a leftovermodelblock pointing at anenv-slot is switched to the login provider's official slot by the newswitchModelBlockToOfficialProvider()(model ID kept if the official slot already declares it, otherwise its first declared model); after logout the next start is signed out and the file takes themodelblock back — both directions seamless, the file never needs manual removal or restore; (4) file rename.env→custom-provider.env(template renamed tocustom-provider.env.examplewith its header rewritten to the new semantics): the first start automatically renames a legacy~/.zcode/cli/.envand prints one notice line (no migration whenZCODE_ENV_FILEpins an explicit path); (5) prefix-stripped display (newdisplayProviderId()/displayModelRef()in env-config.ts): the TUI model display, thezcode identityProvider line, and<provider-id>/<model-id>all show the value declared in the file (env-bigmodel/glm-5.3→bigmodel/glm-5.3); theenv-prefix lives only in config.json internal slot names (the isolation mechanism is unchanged); (6) the barezcode logingate now blocks only when already signed in: signed in it prints "Already signed in as " and exits (--oauthforces a re-login), signed out it lets OAuth proceed even when a custom-provider file is configured — "running login means the user wants to log in"; (7)zcode identity setis refused while signed out (display names follow the login account; nothing to set when signed out); (8)zcode identitywhile signed out printsIdentity: not signed in (model access via custom provider). - Behavior map (login / logout feedback loop): signed out + file → model via the
env-slot, banner "Not signed in", model shown as<declared id>/<model>;/loginOAuth completes → vault + official slot written, TUI refreshes instantly to "Signed in as ", next start the model block belongs to the official slot;/logout→ vault cleared, banner flips back to "Not signed in" instantly, next start the file takes the model back over. - Migration note: the first start after upgrading renames
~/.zcode/cli/.env→custom-provider.envautomatically (one console notice); setZCODE_ENV_FILEto the old path to opt out. Signed-in users'model.mainis moved fromenv-<id>/...back to the official slot on the next start. - Verification:
tsc --noEmitpasses; fullbun test674 pass / 0 fail (78 files; identity gains 3 sign-in-detection + 3 snapshot cases, login-identity re-signs signedOut semantics + 2 new cases, env-config gains 2 migration + 1 display + 1 skipModelBlock + 3 model-block-switch cases).
- Why: after completing OAuth in the TUI (/login), the banner kept showing the masked API key from the custom-provider file — a successful login with zero visible feedback. The root cause sits in the 3.8.1-25 decoupling design: identity display followed the
Install
npm install -g https://github.com/xhqing/zcode-cli/releases/download/v3.8.1-26/zcode-cli-3.8.1-26.tgzzcode-cli 3.8.1-25
Add BigModel key-name mapping, fix logout leaving BigModel credentials, and isolate env-file config into env- slots
- Add a BigModel API key → display-name mapping file
~/.zcode/cli/bigmodel-users.json: per-key custom sign-in display names (a user name, a key name, or any label — purely local display text), so account switches no longer need re-pinning; a hint is printed after login when no mapping exists yet (newsrc/bigmodel-users.ts,src/identity.ts,src/launcher.ts,packages/zcode-tui/src/login-identity.ts,packages/zcode-tui/src/index.ts,test/identity.test.ts,test/login-identity.test.ts,README.md,README_zh_hans.md,README_zh_hant.md,docs/CONFIGURATION.md).- Why: BigModel logins (OAuth and API-key variants) only write the exchanged / pasted API key into config.json and never learn the account name, so the identity display falls back to the masked key; multi-account users cannot tell who is signed in after a switch, and the existing
zcode identity setmechanism is a provider-level snapshot that must be manually re-pinned on every switch — miss it and the wrong name shows. With a per-key mapping, every key carries its own display name and switches are correct by construction. - What changed: (1) new
src/bigmodel-users.ts—bigmodelUsersPath()(~/.zcode/cli/bigmodel-users.json, owned by the BigModel login channel and deliberately kept out of the model-access.env),readBigmodelUserNames()(fault-tolerant read: a missing file, bad JSON, or a non-object all yield an empty map; non-string and blank entries are skipped),resolveBigmodelUserName(); (2) identity resolution (readLoginIdentitySnapshotinsrc/identity.tsand its TUI mirrorreadLoginIdentityinlogin-identity.ts): priority is vaultuser_infosnapshot → BigModel key lookup in the mapping (a hit returns the new kindnamed; the banner / status bar showSigned in as <name>, status-bar prefixuser) → masked-key fallback; the mapping only applies to providerbigmodel(zai / custom providers are not looked up); (3) hints in three places:readBigModelKeyNameHint()detects "bigmodel + identity resolved to the masked key + no mapping"; the TUI prints it viasuggestBigModelKeyName()after the non-attributable/loginvariants (bigmodel-coding-plan and the two api-key variants), the bare CLIzcode loginprints it viaprintBigModelKeyNameHint()after success, andzcode identityappends a Tip line when showing a masked key; hints contain only the masked key and the file path, never the full key; (4) 13 new test cases (8 src-side: named resolution, oauth priority, provider restriction, read fault tolerance, hint positive/negative cases, identity-command Tip presence; 5 TUI-side: named / not covered / bad file / non-bigmodel / oauth priority, plus aloginIdentityTextassertion for the named wording). - Scope note: the mapped value is entirely up to the user — a user name, a key remark, an account name, or any custom label works; neither the implementation nor the documentation constrains its semantics (purely local display text, no effect on authentication or requests). The
.envchannel'senv-bigmodelslot is not looked up (the established boundary keeping it decoupled from/login). Thezcode identity setmechanism is kept unchanged (it suits renaming within the same account; the mapping suits multiple keys / multiple accounts — the two complement each other).
- Why: BigModel logins (OAuth and API-key variants) only write the exchanged / pasted API key into config.json and never learn the account name, so the identity display falls back to the masked key; multi-account users cannot tell who is signed in after a switch, and the existing
- Fix
/logoutfailing to remove BigModel credentials: intercepted on both the CLI and TUI sides with a complete deletion list (zai + bigmodel + shared markers) (src/identity.ts,src/launcher.ts,packages/zcode-tui/src/index.ts,test/identity.test.ts).- Why: users reported that after
/logoutin the TUI the model still worked and the account username kept showing at the bottom. Reverse engineeringvendor/zcode.cjsconfirmed the root cause — the runtime's logout ultimately callsclearZaiLoginCredentials(), hardcoded to delete only 4 fixed keys (oauth:zai:access_token/oauth:zai:refresh_token/oauth:zai:user_info/zcodejwttoken, plusoauth:active_provideronly when it decrypts tozai), while the keys written by the BigModel OAuth flow —oauth:bigmodel:access_token,oauth:bigmodel:user_info,oauth:login_attribution— are not on the list: the CLIzcode logoutreally prints "Logged out from Z.AI" and the credential file is indeed written, yet not a single vault entry is removed. The direct cause of the lingering username isoauth:bigmodel:user_infonever being deleted (the TUI identity display reads it first). - What changed: (1)
src/identity.tsaddsclearOAuthLoginCredentials()— the deletion list covers both providers' full credential sets plus shared markers (the zai trio, bigmodel access/refresh/user_info,oauth:login_attribution,oauth:active_provider,zcodejwttoken), preserving unrelated entries (e.g. thezcodefeedbackclientidtelemetry ID), idempotent (a missing or already-empty vault both succeed);runLogoutCommand()/isLogoutInvocation()form the CLI command entry; (2)launcher.tsinterceptszcode logoutafter the identity routing instead of passing it through to the runtime (the runtime's deletion list is a subset of this implementation, so the intercepted behavior is a superset); (3) the TUIsubmit()intercepts/logoutlocally (same layer as the suspended/login zai-coding-plan) with the newhandleLocalLogout(): clear credentials → notice feedback → re-check the login state viareadConfiguredModelAccess()(the loginRequired warning is only set when no model access is configured;.env/ manual-config users keep model access after logout, matching the semantic boundary that an API key is model-access configuration, not a login state) →setLoginRequiredtriggers an inline identity refresh (user_info is gone, the display falls back to the masked key or disappears). - Scope note: logout only clears vault login credentials, not the API key in config.json (consistent with official runtime semantics — the OAuth-exchanged key is an independently valid credential; to cut off model access, delete
.envor edit config).
- Why: users reported that after
.envconfiguration now writes its ownenv-<provider-id>slot, fully decoupled from the/login//logoutOAuth system (src/env-config.ts,packages/zcode-tui/src/index.ts,test/env-config.test.ts).- Why: the intent of
.envis a custom-provider channel (universal for z.ai / bigmodel.cn / deepseek or any provider, without affecting normal login/logout), but the old implementation wrote the.envkey directly into the config slot of the declared ID (ZCODE_PROVIDER_ID=bigmodelwroteprovider.bigmodel) — the same official slot OAuth logins write to — creating a three-way tangle: the.envkey was treated as an official login artifact (the TUI showed the OAuth username), an OAuth login would overwrite the.envconfiguration, andzcode loginreported "no login needed" because the official slot had a key. The default was traced back to a deliberate design (borrowing the official slot to satisfy the upstream login gate — the runtime'shasConfiguredCodingPlanApiKeyonly recognizes the zai/bigmodel slots), but the cost was exactly the tangle above, conflicting with.env's positioning as a universal channel for any provider. - What changed: (1)
buildProviderConfig()in env-config.ts now always outputs theenv-<declared id>slot (ZCODE_PROVIDER_ID=bigmodel→ writesprovider["env-bigmodel"],model.main = "env-bigmodel/glm-5.2"; the declared id is still used for the base-URL default table and the provider display name); theenvProviderSlotPrefixconstant is exported; the official zai/bigmodel slots now belong exclusively to the OAuth flow —.envand/loginnever overwrite each other; (2) the TUI'shandleResult()re-checks a runtime-pushedloginRequired=truewithreadConfiguredModelAccess()(slot-agnostic: only checks whether the provider pointed to by model.main has a non-empty key) and suppresses the "Model access is not configured" warning when configured — compensating for the runtime login gate only recognizing the two official slots;env-*slots and manually configured custom providers both benefit; (3) the identity display needed no changes and degrades automatically (login-identity.ts only consults the vault's user_info for zai/bigmodel;env-*slots show the masked key). - Migration note: on the first start after upgrading, the
.envsync switches to theenv-*slot; keys left behind in the old official slots are kept (harmless residue an OAuth login can overwrite normally). Existing users will see the model identifier change frombigmodel/glm-5.3toenv-bigmodel/glm-5.3(semantically more accurate: this is.env-channel configuration). - Verification:
tsc --noEmitpasses; fullbun test644 pass / 0 fail (78 files; env-config slot assertions updated plus a strengthened "official slots are never touched by.env" case; 5 new identity logout cases).
- Why: the intent of
Install
npm install -g https://github.com/xhqing/zcode-cli/releases/download/v3.8.1-25/zcode-cli-3.8.1-25.tgzzcode-cli 3.8.1-24
Show the workspace directory beside the turn timer in the TUI status footer
-
Show the workspace directory beside the turn timer in the TUI status footer (tell concurrently open sessions apart) (
packages/zcode-tui/src/turn-status.ts,packages/zcode-tui/src/index.ts,packages/zcode-tui/src/welcome-banner.ts,test/turn-status.test.ts).- Why: the current directory was previously shown only in the welcome banner; once a long session scrolls the banner off screen, several concurrently open zcode TUI instances can no longer be told apart. The directory now lives on the turn status line (the line that carries the turn timer), always visible right next to the timer and refreshing along with it.
- What changed: (1)
welcome-banner.tsexports the home-prefix-abbreviatingdisplayWorkspace()logic asabbreviateWorkspaceDirectory()(banner and timer line now share one abbreviation rule); (2)turn-status.tsaddsturnStatusDirectoryText()-- the workspace directory abbreviated to~form, and when it exceeds the 24-column budget (TURN_STATUS_DIRECTORY_MAX_COLUMNS) it is truncated from the head while keeping the tail (the distinguishing part of concurrently open sessions is mostly the project name at the end of the path); the 24-column budget leaves room for the right-side Goal status on 80-column terminals (measured: timer + directory + compact Goal coexist at 80 columns; narrower terminals prioritize timer + directory); (3)index.tsupdateTurnStatus()appends the directory text (muted style,─separator) to the right of the timer text, and the directory shows alone when there is no timer content; the directory text is sanitized viasanitizeTerminalTextand cached (cwd does not change within a process lifetime,??=computes it once); (4) 3 new test cases (same abbreviation as the banner, head-truncation keeping the tail over budget, wide-character directory truncated without breaking characters). - Verification: full
bun test639 pass / 0 fail (78 files);tsc --noEmitpasses; multi-width rendering measured at 100 / 80 / 60 columns confirms the expected layout; after thebuild:tuirebuild, the@zcode/tuicopy inside vendor is refreshed in sync (matches the build output).
-
Fix the TUI showing the previous account's username after switching accounts: the identity display now refreshes unconditionally after login, and logins whose identity cannot be attributed clear stale snapshots when the API key changes (
src/identity.ts,packages/zcode-tui/src/index.ts,test/identity.test.ts,test/login-flow.test.ts,README.md,README_zh_hans.md,README_zh_hant.md).- Why: users reported that after switching accounts and logging in again, the TUI banner and status bar still showed the old account's username. Investigation confirmed two root causes -- (1) session layer:
setLoginRequiredrefreshed the identity display only whenloginRequiredflipped from true to false; when switching accounts while already logged in, that state stays false, sorefreshLoginIdentitynever fired and the old name persisted until the TUI was restarted; (2) data layer: the display name is read from the vault'soauth:<provider>:user_infosnapshot; the Z.AI OAuth login flow rewrites that snapshot with the new account's user object on every login (runtimesaveZaiLoginCredentials, confirmed by reverse engineering), but BigModel OAuth and the two API-key login variants only write the new API key into config.json and never touch the snapshot (reverse engineering confirmed the runtime has zero references tooauth:bigmodel:*keys; that entry in the local vault is a leftover from the uninstalled ZCode Desktop) -- after switching to another BigModel account, the name in the snapshot belongs to the old account and a restart does not fix it, and the CLI has no way to obtain the new account's username (the token exchange happens entirely inside the runtime process, and the live fetch endpoint is blocked by a WAF, see the previous entry). - What changed: (1)
src/identity.tsaddsreadProviderApiKeySnapshot()(snapshots the zai / bigmodel providers' config API keys before login) andclearIdentitiesWithChangedKeys(before)(after login, a provider whose key changed has itsuser_infosnapshot deleted because it can no longer be attributed to the current account; returns the cleared list); (2) the TUI'ssetLoginRequirednow callsrefreshLoginIdentity()on every invocation (no longer only on state flips -- switching accounts while logged in also refreshes), andrefreshLoginIdentitycompares values (early-returns when kind + label are unchanged, avoiding a redraw on every result); (3) newisLoginWithoutIdentityRefresh()detecting the three non-attributable login commands (/login bigmodel-coding-planand the two-api-keyvariants; Z.AI OAuth excluded because it rewrites the snapshot itself);submit()snapshots the keys beforesubmitPromptfor such commands and calls the newclearStaleIdentityAfterLogin()afterhandleResult: key changed -> clear the snapshot, refresh the display, and hint thatzcode identity set <name>can pin the new name; key unchanged (re-login to the same account) -> keep the current name; (4) 7 new test cases (5 in identity: snapshot only includes vault-backed providers, a key change clears the snapshot while preserving neighboring entries, an unchanged key keeps it, a zai key change also clears, no snapshot is a no-op; 2 in login-flow: positive and negative cases of the predicate); fullbun test636 pass / 0 fail (78 files),typecheckpasses; rebuilt withbuild:launcher+build:tui, and the@zcode/tuicopy inside vendor is refreshed in sync. - Scope note: this fix guarantees "the old account's name no longer shows after a switch" (a Z.AI switch shows the new name directly; a BigModel / API key switch falls back to the masked API key with a hint to pin the new name manually) -- obtaining the new BigModel username during login is a structural limitation of the upstream runtime (the token never touches disk); showing it automatically requires upstream support and is out of scope for this fix.
- Why: users reported that after switching accounts and logging in again, the TUI banner and status bar still showed the old account's username. Investigation confirmed two root causes -- (1) session layer:
-
New
zcode identitysubcommand: view / manually sync the signed-in identity display name (fixes the TUI showing a stale username after a rename) (newsrc/identity.ts,src/usage.ts,src/launcher.ts, newtest/identity.test.ts).- Why: the user renamed their account on bigmodel.cn, but the TUI banner and status bar kept the old name. Investigation confirmed the root cause -- the display name is read from the encrypted
oauth:bigmodel:user_infosnapshot (containing username / displayName) in the machine-wide shared credential vault~/.zcode/v2/credentials.json; that snapshot was written when ZCode Desktop (which shares the vault with the CLI) logged in via OAuth, and nothing has refreshed it since: Desktop is uninstalled, and the official runtime's bigmodel login flow (reverse engineered from vendor/zcode.cjs) only writes the exchanged API key into~/.zcode/cli/config.jsonand never the vault's user_info -- so even re-runningzcode logincannot clear the old name. Fetching the latest username from the server in real time is not dependable: the primary endpointGET bigmodel.cn/api/biz/customer/getCustomerInfo(the same one used by the web profile page and the runtime login chainGhr->Uhr) is currently blocked by an Aliyun WAF challenge from this machine both directly and via proxy (200 + empty body +acw_tccookie), and the monitor domain has no user-info endpoint. Hence a manual sync command as the only reliable refresh path. - What changed: (1)
src/usage.tsaddsencryptCredential()-- the dual ofdecryptCredential()(enc:v1:AES-256-GCM, random 12-byte IV, 16-byte tag, three base64url segments, key derivation identical to the runtime), letting the CLI write back credentials the runtime can read; (2) newsrc/identity.ts--zcode identityshows the active provider's identity (OAuth account name or masked API key, with the same read priority as the TUI's login-identity.ts mirror; the package dependency direction forbids importing it the other way, hence the standalone implementation);zcode identity set <name>rewrites the active provider's user_info snapshot username + displayName (theoauth:active_providermarker first, falling back to the config.model.main prefix, default bigmodel), preserving other fields such as id / avatarUrl and neighboring vault entries (login_attribution etc.), and creating the entry when no snapshot exists;zcode identity cleardeletes the snapshot and falls back to the API-key display; the name is capped at 64 characters,setis rejected for non-OAuth providers, and the vault is written back with 0600 permissions; (3)launcher.tswires the identity route after the stats route; (4) newtest/identity.test.tswith 13 cases (encrypt/decrypt round trip + random IV, command argument dispatch, the three snapshot read priorities, set preserving fields / creating an entry / two rejection classes, show / clear / no-op / missing state). Rebuilt withbun run build:launcher; fullbun test630 pass / 0 fail (78 files). Live check:zcode identityoutputsProvider: bigmodel / Identity: signed in as <old name>(reproducing the issue); the vault is shared globally, and afterseta new TUI session picks it up immediately. - Scope note:
identity setis a local display-name sync and does not change the server-side account; if the WAF ever allows the live endpoint, automatic refresh could be filed separately (not filed now -- live fetching is unusable in production today, and automatic refresh would silently fail and fall back to the stale snapshot, adding complexity for nothing). - Version note: these changes were originally recorded under the 3.8.1-23 entry, but v3.8.1-23 had already been published as a GitHub Release (2026-09-04, whose notes do not include these two items); af...
- Why: the user renamed their account on bigmodel.cn, but the TUI banner and status bar kept the old name. Investigation confirmed the root cause -- the display name is read from the encrypted
zcode-cli 3.8.1-23
Enable workspace-hook trust system in the TUI session factory
-
TUI session factory now enables the workspace-hook trust system: bootstrap passes
workspaceHookTrustEnabled: true(TODO T1 completed) (scripts/sync-runtime.ts,scripts/check-runtime.ts,vendor/zcode.cjs,test/sync-runtime.test.ts,TODO.md, newTODO-archive.md).- Why: the runtime already ships a complete project-level trust system for workspace hooks (
pending_trust -> trusted_persistentstate machine, declarationsha256+ bundle digest binding, persistent trust store at~/.zcode/security/workspace-hook-trust-v1.json, and thezcode hooks trust status/grant/revokecommand family); the headless protocol path (--prompt) already setsworkspaceHookTrustEnabled:!0; only zcode-cli's TUI session factory never passed it, so the runtime side was permanentlyfalseand project-level hooks were disabled as a whole (a CLI grant had no effect, and every session loggedworkspace_hook.feature_disabled). The consequence was that DayTradingAgent had to fall back to two security hooks under the user-level~/.zcode/cli/config.json, and on 2026-09-04 that path was measured to be overwritten wholesale by a stale snapshot when the client saves settings -- the project-level single source is the stable state. - What changed: (1)
sync-runtime.tsgainspatchRuntimeWorkspaceHookTrust(runtime)-- it anchors on the unique anchor of the TUI session factory (...,onWorkflowEvent:b.onWorkflowEvent})) and injects,workspaceHookTrustEnabled:!0; idempotent (returns the input unchanged when already patched) and throws withincompatiblewhen the anchor is missing; wired into theinstallTuiBridgepatch chain (betweenpatchRuntimeHttpNoContentandpatchRuntimeAgentAutoBackground). (2) The idempotent check chain incheck-runtime.tsgained the matching line. (3) The patch was actually written intovendor/zcode.cjs(net +29 bytes, at offset ~12365683) andnode --checkpasses. (4) New cases intest/sync-runtime.test.ts(injection assertion, idempotence,incompatiblethrow; the fixture cannot be executed withnew Function, so these are pure string assertions). - End-to-end verification (
tmp/smoke-hook-trust.ts, reproducible, gitignored): temporary HOME plus a project-levelSessionStarthook through the whole flow -- before granting, a new session log contains noworkspace_hook.feature_disabled(patch effective), config load logsconfig.project_hooks.pending_trust, and the hook is blocked by the trust gate (no marker file); afterzcode hooks trust grant(CLI path, status becomesworkspace_hooks_trusted_persistent) the hook actually runs in a new session (marker file appears) and the log still contains nofeature_disabled. Two findings worth keeping:SessionStarthooks run on the first turn (the first user input triggersrunSessionStartHooks("startup")), not at TUI startup, so a self-test must send a message; on macOS the/tmp -> /private/tmpsymlink makes theworkspaceIdentityrecorded by the CLI differ from the one the TUI session resolves from cwd (the grant never reaches the session), so smoke sandboxes must live on a symlink-free path (under the project'stmp/). Real workspace paths are stable and unaffected. - Verification:
bun test test/sync-runtime.test.ts26 pass; fullbun test617 pass / 0 fail across 77 files;bun run checkpasses (zcode-cli 3.8.1-23 / zcode-runtime 0.16.3). Follow-up on the DayTradingAgent side (its TODO T140 2 and 3): rerun the credential probe in a new ZCode session (should still be blocked, now by the project-level hook), then withdraw the two user-level hooks and switch back to the project-level single source. - Follow-up closure: T1 is archived into the newly created
TODO-archive.md; the same-origin risk (the client saving settings rewritesconfig.jsonwholesale from an in-memory stale snapshot, wiping external edits to thehookssection) is filed as TODO T2 (orange urgency).
- Why: the runtime already ships a complete project-level trust system for workspace hooks (
-
Added the project-root
TODO.mdand registered T1 (session bootstrap should passworkspaceHookTrustEnabled: true) (newTODO.md).- Why: verification on the DayTradingAgent side (2026-09-03~04) found that zcode-cli never passes this parameter when constructing a session, so the project-level workspace hooks trust system already built into the runtime was disabled by the host capability switch (the CLI grant was in place and the trust store persisted -- only this switch was missing). Per the cross-project split, this work item was transferred from DayTradingAgent TODO T140 (1) and registered here.
- What changed: created
TODO.md(a four-level urgency section framework plus a numbered timestamp convention) and registered T1 (orange) -- task description, threevendor/zcode.cjsoffset references (construction site ~11703354, bootstrap consumer ~11762913,!0sample ~11922028), post-completion verification criteria (rerun the credential probe in a new DayTradingAgent session, nofeature_disabledin the log, then withdraw the user-level fallback and switch to the project-level single source), and a note on the same-origin risk (measured on 2026-09-04: the client saving settings rewritesconfig.jsonwholesale from a stale snapshot, wiping external edits to thehookssection -- whether to file it separately was left undecided). This project had noTODO-archive.mdyet (first item, nothing to archive); it was created along with the first archive.
Install
npm install -g https://github.com/xhqing/zcode-cli/releases/download/v3.8.1-23/zcode-cli-3.8.1-23.tgz