Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,13 @@ This file is the public-facing release log for the npm package `zagent`.

## Unreleased

- fix(cli): seed the kernel's personal provider config from the cli config before
app-server backed runs (`-p --model/--effort`, `commit-msg`, `models test`) —
the 3.14.x app-server registry never imports the legacy cli config itself, so
a fresh host answered every model-bearing `session/create` with
model-not-found until the kernel's own `-p` path happened to migrate it.
`doctor` now flags a configured model the runtime registry cannot resolve.

## 0.0.238 — 2026-09-21

- Live-user-testing fixes: `quota reset use` checks credentials before its
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@
"packages/driver/memory.mjs",
"packages/driver/offpeak.mjs",
"packages/driver/permissions.mjs",
"packages/driver/personal-provider.mjs",
"packages/driver/plugins.mjs",
"packages/driver/provider-errors.mjs",
"packages/driver/providers.mjs",
Expand Down
3 changes: 2 additions & 1 deletion packages/cli/test-cli-ux.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,8 @@ writeFileSync(path.join(catalogDir, 'models_catalog_1.json'), JSON.stringify({
mkdirSync(path.join(home, '.zcode', 'cli'), { recursive: true });
writeFileSync(path.join(home, '.zcode', 'cli', 'config.json'), JSON.stringify({
model: { main: 'zai/glm-5.3', lite: 'zai/glm-5.3-flash' },
provider: { zai: { kind: 'anthropic', options: { baseURL: 'https://api.z.ai/api/anthropic/', apiKey: 'fixture' } } },
provider: { zai: { kind: 'anthropic', options: { baseURL: 'https://api.z.ai/api/anthropic/', apiKey: 'fixture' },
models: { 'glm-5.3': {}, 'glm-5.3-flash': {} } } },
}));

const env = {
Expand Down
477 changes: 477 additions & 0 deletions packages/cli/test-personal-provider.mjs

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions packages/cli/zagent-commit-msg.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -154,8 +154,10 @@ if (effort) {
}

const { ZCodeProtocolClient } = await import('../driver/zcode-protocol.mjs');
const { provisionPersonalProviderConfig } = await import('../driver/personal-provider.mjs');
let client, code = 0;
try {
try { provisionPersonalProviderConfig(); } catch {} // fresh-host registry seed (see personal-provider.mjs)
client = new ZCodeProtocolClient({ cwd: process.cwd() });
await client.ready;
// The registry is GUI-pushed (provider/updateAccountConfig); a headless spawn
Expand Down
2 changes: 2 additions & 0 deletions packages/cli/zagent-models.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -139,8 +139,10 @@ if (process.argv[2] === 'test') {
[[providerId, modelId]] = hits;
}
const { ZCodeProtocolClient } = await import('../driver/zcode-protocol.mjs');
const { provisionPersonalProviderConfig } = await import('../driver/personal-provider.mjs');
let client, code = 0;
try {
try { provisionPersonalProviderConfig(); } catch {} // fresh-host registry seed (see personal-provider.mjs)
client = new ZCodeProtocolClient({ cwd: process.cwd() });
await client.ready;
// The registry is GUI-pushed (provider/updateAccountConfig); a headless
Expand Down
12 changes: 10 additions & 2 deletions packages/cli/zagent-print.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import { ZCodeProtocolClient, runTurn, sessionSid, currentAnswer, extractUsage }
import { setMode, MODES } from '../driver/session-control.mjs';
import { autoAllow } from '../driver/permissions.mjs';
import { modelReasoningLevels } from '../driver/providers.mjs';
import { provisionPersonalProviderConfig } from '../driver/personal-provider.mjs';
import { KERNEL_VALUE_FLAGS, KERNEL_LIST_FLAGS } from './commands.mjs';

const VALUE_FLAGS = new Set(['--model', '--effort', '--mode', '--cwd', '--output-format', '--locale']);
Expand Down Expand Up @@ -151,7 +152,11 @@ async function openPrintClient(cwd) {
// --prompt" and zagentd does the same. A user-chosen --mode still applies via
// session/setMode (kernel-side enforcement); plan approval prompts hit the
// client's default decline handler, so headless plan mode produces the plan.
export async function runPrintOnce(sel, { client, createClient = openPrintClient, timeoutMs = 600_000, catalog, providerConfig } = {}) {
// `provision` seeds the kernel's personal provider config from the legacy cli
// config before the app-server spawn — the app-server registry never runs the
// kernel's own legacy import (see personal-provider.mjs), so without this a
// fresh host answers every model-bearing session/create with model-not-found.
export async function runPrintOnce(sel, { client, createClient = openPrintClient, timeoutMs = 600_000, catalog, providerConfig, provision = provisionPersonalProviderConfig } = {}) {
const t0 = Date.now();
const cwd = path.resolve(sel.cwd ?? process.cwd());
const key = path.normalize(cwd);
Expand Down Expand Up @@ -185,7 +190,10 @@ export async function runPrintOnce(sel, { client, createClient = openPrintClient
params.thoughtLevel = sel.effort;
}
const own = !client;
if (own) client = await createClient(cwd);
if (own) {
try { provision(); } catch {} // best-effort registry seed; a failure must not block the run
client = await createClient(cwd);
}
let sid = null;
try {
const created = await client.call('session/create', params);
Expand Down
16 changes: 16 additions & 0 deletions packages/cli/zagent.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import { findRuntime, kernelEnv, kernelResolves } from '../driver/runtime.mjs';
import { runtimeCapabilities, capabilityLine } from '../driver/runtime-info.mjs';
import { buildLaunchArgs, tuiPreference, tuiNodeSupported, TUI_NODE_FLOOR, nodeSqliteSupported, NODE_SQLITE_FLOOR } from '../driver/tui-launch.mjs';
import { provisionStandaloneAccounts } from '../driver/account-provider.mjs';
import { modelResolutionCheck, personalProviderConfigPath } from '../driver/personal-provider.mjs';
import { explainProviderError, formatProviderError } from '../driver/provider-errors.mjs';
import { nodeLine, doctorCredential, extensionCounts, hooksLine, diskLine, logDirLine, displayPath, displayText } from '../driver/doctor.mjs';
import { snapshotStatus, snapshotLine } from '../driver/snapshot-guard.mjs';
Expand Down Expand Up @@ -621,6 +622,21 @@ if (args[0] === 'doctor' || !rt) {
const cred = doctorCredential({ config: cfgJson, hasConfig: existsSync(cfg) });
console.log(`credential: ${cred ?? 'NONE'}`);
if (!cred && existsSync(cfg)) warnings.push('no Coding Plan credential in any source — turns will stop at the sign-in card');
// The credential can be fine while the model still cannot resolve: the
// app-server registry reads personal providers from v2/provider_config.json
// only, so a config whose selected provider/model is absent there (and not
// derivable from the cli config) fails every -p run with model-not-found.
// Doctor reports that class ahead of the turn instead of exiting 0.
if (cfgJson) {
const res = modelResolutionCheck({ config: cfgJson });
if (res) {
console.log(`model: ${res.detail}${res.ok ? '' : ' — NOT RESOLVABLE'}`);
if (!res.ok) {
warnings.push(`${res.detail} — cannot resolve in the runtime registry; fix the provider/model list in the cli config or in ${displayPath(personalProviderConfigPath())}`);
unhealthy = true;
}
}
}
const ext = extensionCounts({ config: cfgJson });
console.log(`plugins: ${ext.plugins} installed`);
console.log(hooksLine(ext));
Expand Down
Loading
Loading