fix(cli): seed personal provider config so -p --model works on a fresh host - #7
Conversation
The 3.14.x app-server builds its Provider Registry without the standalone options (runZCodeProtocolAgent -> Ykt(env)): personal providers come only from ZCODE_PERSONAL_PROVIDER_CONFIG_FILE (default ~/.zcode/v2/provider_config.json) and the legacy cli config is never imported there. A host with only ~/.zcode/cli/config.json therefore answered every model-bearing session/create from -p --model with model-not-found, while the kernel's own -p path worked because IT runs the legacy import and persists the migrated file. Mirror that migration in zagent: convert the cli config exactly like the kernel importer (NHa/MHa/FHa/LHa parity incl. the builtin:zai and builtin:bigmodel family mapping and the apiKeyRequired:false refusal) and write it only when the target file does not exist — a desktop-written or kernel-migrated file always wins. Atomic 0600 write under the shared interprocess lock, best-effort, before the app-server spawn on the -p selection path, commit-msg, and models test. doctor now resolves the configured model against the same effective source (existing file wins, else the derivation) and exits 1 when it cannot resolve, instead of reporting a healthy credential-only setup.
|
agent-review (grok) at review我先读 PR 元数据、完整 diff 和提交说明,再按文件核对正确性、安全和测试。提交说明里没有 AI 署名。接下来按文件读 diff 和实现,并跑仓库自带的检查。Memory flush started. Findings
VERDICT: REQUEST_CHANGES |
Harden the provisioning module: - Never seed a permanent empty-key config. NHa stores a custom provider's apiKey verbatim (xz ApiKeyAccessConfig, offset 547578, keeps "" through toJSON), and ensureConfig writes apiKey:'' during the OAuth window — a create-if-missing seed of that state could never be repaired (the kernel's import skips existing files and the OAuth backfill reads this very file). The converter keeps the kernel parity, and the provisioner now refuses to persist when the SELECTED provider's key is unusable (empty/whitespace/absent); the next run after the key lands seeds correctly. - Make the source/target root rule explicit. The kernel's legacy import reads the cli config from the REAL home (U7, offset 4090234) even when ZCODE_DATA_BASE_DIR relocates the personal target (dQi, offset 1068307) — measured live on 3.14.4: HOME's key lands in the data-root file and a data-root cli config is never consulted. That asymmetry is now a documented, named helper (legacyCliConfigPath) with pinning tests instead of an implicit join. - Skip null/primitive model-map entries like deleted ones instead of throwing, and make modelResolutionCheck's refusal reasons honest (expressibility vs malformed input). - Treat a structurally wrong existing provider_config.json (non-array providerRules / provider rule config / personalModelIds) as a NOT-RESOLVABLE verdict — the kernel's strict parser does the same — so doctor prints a model: line instead of crashing. - Drop an unused chmodSync import in the test. Each class has a unit test that fails on the pre-fix module and passes now.
|
agent-review (grok) at review我先按独立评审来看这份 PR:读评审规范、PR 元数据和完整 diff,再对照仓库自身的检查命令。评审范围已钉在 PR #7 的 head。接下来读仓库检查命令、提交说明和完整 diff。Memory flush started.
VERDICT: REQUEST_CHANGES |
planPersonalProviderConfig is now the single answer to "would the -p
path make this host's selection resolvable, and what would it write";
provisioning and doctor's modelResolutionCheck both consume it, so
doctor can never report ok for a state the -p path refuses to seed
(the OAuth/ZAI_API_KEY empty-key states: credential lines look fine,
yet no registry file can be created).
The plan also refuses to seed a document that would not resolve the
selected model — a usable key with no models map used to leave a
rule without personalModelIds behind forever (nothing rewrites an
existing personal file), turning a wait-for-the-config-to-grow state
into a permanent miss on the first failed run. Dangling selections
are the same class. Family rules (templateId) resolve through the
builtin template's model list and stay seedable.
Wrong-typed fields (name:{}, apiKey:5, baseURL:7) now degrade to
absent for that entry alone instead of aborting the whole import.
The kernel's zod layer (QAn -> RHa) is strict — one bad field
rejects the entire config — but it also has a desktop to repair the
file; zagent's create-if-missing seed does not, so per-entry
leniency is the safer failure mode for the converter.
doctor's exit contract changes accordingly for keyless configs: a
selection the -p path cannot seed is NOT RESOLVABLE and unhealthy
(exit 1), matching what provisioning refuses — previously a
credential-less config kept exit 0 while -p --model failed.
|
agent-review (grok) at review我先读 PR 元数据、完整 diff 和仓库检查约定,再按文件核对正确性、安全性和测试。PR 是个人 provider 配置的种子写入。我对照 diff、提交说明和仓库自检命令逐项核对。提交说明里没有 AI 署名。接下来核对种子写入、doctor 判定,以及相关测试是否真的覆盖这些路径。Memory flush started. Findings
VERDICT: APPROVE |
Summary
zagent -p "..." --model glm-5.3failed withProvider Registry 中不存在 Model: zai/glm-5.3(exit 1) on any host that has only~/.zcode/cli/config.json(no desktop-written~/.zcode/v2/provider_config.json), whilezagent doctorreported the credential as healthy. Reproduced on ZCode runtimes 3.14.3 and 3.14.4 with a fresh HOME; not a 3.14.4 regression.Root cause: the 3.14.x app-server (which the
--model/--effortselection path,commit-msgandmodels testspawn) builds its Provider Registry without the standalone options, so it never imports the legacy cli config. Personal providers come only from~/.zcode/v2/provider_config.json. The kernel's own-ppath does run that import and persists the file, which is why a run without--model"fixed" later runs.Change
packages/driver/personal-provider.mjs: converts the cli config exactly like the kernel's legacy importer and writesprovider_config.jsononly when it does not exist (a desktop-written or kernel-migrated file always wins). Atomic 0600 write under the shared interprocess lock, best-effort, never throws.-pselection runs,commit-msgandmodels test.doctorprints amodel:line and exits 1 when the configured model cannot resolve in the runtime registry.package.jsonfilesincludes the new module; CHANGELOG Unreleased entry.Verification
node packages/cli/test-personal-provider.mjs: 29 checks pass; the seed-before-spawn check fails on the pre-fixzagent-print.mjs(ENOENT onprovider_config.json).cli/config.json, first run,-p --model glm-5.3:Provider Registry 中不存在 Model: zai/glm-5.3, exit 1okexit 0, 3.14.4okexit 0;provider_config.jsoncreated with mode 600make check: PASS, and the new module is in the packed payload.scripts/test-all.mjsneedsscripts/discover-tests.mjs, which lands with feat: port remaining zcode-cli source modules and test tooling #6.