@kontextmind/kxm 0.7.201 → 0.7.203

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -11,7 +11,7 @@
11
11
  "name": "kxm",
12
12
  "source": "./plugins/kxm",
13
13
  "description": "Durable workflows, peer agents, and kxm tui",
14
- "version": "0.7.201",
14
+ "version": "0.7.203",
15
15
  "category": "development",
16
16
  "tags": ["kxm", "multi-agent", "workflows", "mcp"]
17
17
  }
package/CHANGELOG.md CHANGED
@@ -225,6 +225,26 @@ All notable user-facing changes are documented here. The project follows [Semant
225
225
 
226
226
  ### Changed
227
227
 
228
+ - **OpenRouter GLM 5.3 Flash is $0.15 / $0.50 again.**
229
+ Checked 2026-10-21 against `https://openrouter.ai/api/v1/models`
230
+ (`z-ai/glm-5.3-flash`: prompt `0.00000015`, completion `0.0000005`,
231
+ cache read `0.00000003`). `openrouter/z-ai/glm-5.3-flash` moves from
232
+ $0.047076 input and $0.12 output (cache read $0.006) to $0.15 input and
233
+ $0.50 output (cache read $0.03) per 1M tokens. The coding-plan row
234
+ `zai-coding-cn/glm-5.3-flash` keeps its own tier at $0.15 / $0.50 (cache
235
+ read $0.03). This list price is volatile
236
+ and has flipped between those two tiers for several days. Trust the weekly
237
+ OpenRouter refresh over the static row. The inventory `standard` rates for
238
+ `z-ai/glm-5.3-flash` match. `.kxm/prices.yaml` is dated 2026-10-21.
239
+ - **`kxm vision assert` defaults to the admitted OpenRouter GLM route.**
240
+ The built-in route is `openrouter/z-ai/glm-5.3-flash`, invoked as
241
+ `pi --model openrouter/z-ai/glm-5.3-flash`. The old default
242
+ `zai-coding-cn/glm-5.3-flash` is not allowlisted, and that id no longer
243
+ has a special argv. `kxm init` admits the vision route and does not add
244
+ a model file or a roster entry for it. A live accuracy witness is
245
+ `test/core/vision-accuracy.test.ts`. It calls Pi only when
246
+ `OPENROUTER_API_KEY` is set; otherwise it checks
247
+ `test/fixtures/vision/` and does not use the network.
228
248
  - **`kxm init` starts the writer on Grok 4.7.** The starter model file is
229
249
  `.kxm/models/grok-grok-4-7.yaml` (`grok` / `grok-4.7`, selector
230
250
  `xai/grok-4.7`, alias `grok-default`). The writer roster, the built-in
@@ -17,7 +17,7 @@ Each fix is a small pull request with squash auto-merge. None of them needs a se
17
17
 
18
18
  `kxm suggest` used to treat a keyword as a substring. "guide" and "build" took the feature workflow because they contain `ui`, "author" took vulnerability remediation because it contains `auth`, and "change" took bug-fix because it contains `hang`. Hyphenated keywords such as `circuit-breaker` still match as one token. A prompt with no keyword still selects `feature-implementation` at score 0.
19
19
 
20
- `kxm vision assert` accepted `--route` and then ignored it. The flag now selects that Pi model after admission. An unknown route names `kxm routes admit`. A native-vendor id is refused before `pi` starts. The CLI reference documents the command. The built-in default route is unchanged. See decision 2.
20
+ `kxm vision assert` accepted `--route` and then ignored it. The flag now selects that Pi model after admission. An unknown route names `kxm routes admit`. A native-vendor id is refused before `pi` starts. The CLI reference documents the command. That pull request left the built-in default route unchanged. Decision 2 changes it.
21
21
 
22
22
  `kxm init` outside any Git worktree now says to run `git init` and then `kxm init` again. A directory that is not the worktree root still says to run from that root. `kxm hub view` says to start the hub when nothing is listening on a loopback URL, and names the tunnel and `kxm hub bind` for any other URL. An auth proxy or redirect keeps its existing probe note.
23
23
 
@@ -66,9 +66,7 @@ Observed on 2026-10-21, before the runner split:
66
66
 
67
67
  ### 2. The built-in vision route
68
68
 
69
- `kxm vision assert` still defaults to `zai-coding-cn/glm-5.3-flash` and, for that id only, still runs `pi --provider zai-coding-cn --model glm-5.3-flash`. `.kxm/routes.yaml` removed `zai-coding-cn/*` on 2026-09-27 because that provider is not allowlisted. The admitted replacement in this checkout is `openrouter/z-ai/glm-5.3-flash`. A vision call here with no `--route` still fail-closes, and pull request #412 names `kxm routes admit` in that message. `kxm init` does not admit a vision route.
70
-
71
- Changing the default needs a vision accuracy witness on the admitted id. This pass kept the verified default argv.
69
+ Approved 2026-10-21 in [pull request #419](https://github.com/kontextmind/kxm/pull/419). `kxm vision assert` defaults to `openrouter/z-ai/glm-5.3-flash` and runs `pi --model <id>` for every route, including that default. `zai-coding-cn/glm-5.3-flash` no longer has a special argv. `kxm init` admits the vision selector and does not write a model file for it. The accuracy witness is `test/fixtures/vision/` and `test/core/vision-accuracy.test.ts`. That test calls Pi only when `OPENROUTER_API_KEY` is set.
72
70
 
73
71
  ### 3. The starter writer model
74
72
 
@@ -2,7 +2,7 @@
2
2
  title: "Decisions"
3
3
  description: "Decisions of medium significance or higher made by automated KXM work in this repository. One entry per decision: the options evaluated with their weights, and why the selected option won. Recorded the turn the decision is taken."
4
4
  audience: "agents and maintainers"
5
- updated: "2026-10-11"
5
+ updated: "2026-10-21"
6
6
  ---
7
7
 
8
8
  # Decisions
@@ -229,3 +229,26 @@ Evidence: `plugins/kxm/src/cli/plugins.ts`, `plugins/kxm/src/session-work.ts`, a
229
229
  unverified gate.
230
230
  Evidence: `plans/implementation-plan.md`, `plans/plan-python-migration.md`,
231
231
  `python/kxm-control/tests/test_mg1.py`, and `.github/workflows/ci.yml`.
232
+
233
+ ## 2026-10-21 — How `kxm init` admits the vision route (routing)
234
+
235
+ - Context: Eddie approved moving `kxm vision assert` to
236
+ `openrouter/z-ai/glm-5.3-flash` and asked `kxm init` to admit that route
237
+ when the change stays small. The gate checks `.kxm/routes.yaml` only.
238
+ Workforce lint treats an admitted selector with no model file as a warning
239
+ (`admitted_unrouted`), and a model file that no roster names as an error
240
+ (`route_unused`).
241
+ - Options:
242
+ - Admit the selector and skip the model file — weight 0.9. Vision works
243
+ on a fresh project, and dispatch rosters stay the writer and the planner.
244
+ The warning does not fail init or `workforce lint`.
245
+ - Add a model file and put it on a roster — weight 0.3. Clears the warning,
246
+ but it also makes GLM a dispatch candidate for whichever role receives it.
247
+ - Leave init unchanged — weight 0.2. A new project still fail-closes on the
248
+ new default until someone admits the route by hand.
249
+ - Selected: admit the selector only. No model file, no roster entry.
250
+ - Solid recommendation: when a vision role exists, give the route its own
251
+ read-only model file and roster so the warning goes away without sharing
252
+ the writer.
253
+ - Interim recommendation: the admitted selector is enough for `kxm vision assert`.
254
+ Evidence: `plugins/kxm/src/template.ts`, `plugins/kxm/src/workforce-names.mjs`, and `test/core/vision-gate.test.ts`.
@@ -76,7 +76,7 @@ On the capture machine, `kxm harness list` showed `claude` detected but logged o
76
76
 
77
77
  ### Price catalog and inventory
78
78
 
79
- `.kxm/prices.yaml` is dated `2026-10-20`. A run on that date can record a list estimate. Any other day records `providerMetadata.priceCatalogStale: true` and no list estimate. The list prices below come from `.kxm/models/inventory.yaml`. OpenRouter and Nous were fetched at `2026-10-17T13:45:00.000Z`. `pi`, `grok`, and `agy` were not installed for that refresh, so models that exist only in those CLIs stay, including the previous `agy` rows, and the Gemini 4 Argon and Claude 5.5 `agy` rows were added by hand from `agy models`. Prices are USD per 1M tokens. The checkout has no routing records yet, so none of the examples has recorded latency; for latency, run a bounded side-by-side experiment and compare p50 and p95 in `kxm routing report`.
79
+ `.kxm/prices.yaml` is dated `2026-10-21`. A run on that date can record a list estimate. Any other day records `providerMetadata.priceCatalogStale: true` and no list estimate. The list prices below come from `.kxm/models/inventory.yaml`. OpenRouter and Nous were fetched at `2026-10-17T13:45:00.000Z`. The `z-ai/glm-5.3-flash` standard tier was set from the OpenRouter models API on 2026-10-21 ($0.15 input, $0.50 output, $0.03 cache read). Its Nous discount tier is still the 2026-10-17 rate. `pi`, `grok`, and `agy` were not installed for that refresh, so models that exist only in those CLIs stay, including the previous `agy` rows, and the Gemini 4 Argon and Claude 5.5 `agy` rows were added by hand from `agy models`. Prices are USD per 1M tokens. The checkout has no routing records yet, so none of the examples has recorded latency; for latency, run a bounded side-by-side experiment and compare p50 and p95 in `kxm routing report`.
80
80
 
81
81
  ## The developer roster
82
82
 
@@ -182,7 +182,7 @@ The code differs from that decision: `.kxm/routes.yaml` admits `google/gemini-3.
182
182
  |---|---|---|---|
183
183
  | Qwen3.8 Flash | `qwen-token-plan/qwen3.8-flash`: removed from the admitted list on 2026-09-27; `qwen-token-plan` is not allowlisted | `openrouter/qwen/qwen3.8-flash`: admitted, route `pi-qwen3-8-flash-openrouter`, read-only fallback for planner and reviewer-cli | `prices.yaml` keeps both rows. |
184
184
  | Qwen3.7 Plus | none | `openrouter/qwen/qwen3.7-plus`: route `pi-qwen3-7-plus-openrouter`, writer #2 | The only admitted Pi writer: exact model, `edit` permission. OpenRouter list on 2026-10-17 is $0.32 input and $1.28 output per 1M tokens, 1,000,000 context, tools supported. |
185
- | GLM 5.3 and 5.3 Flash | `zai-coding-cn/glm-5.3` and `…/glm-5.3-flash`: removed from the admitted list on 2026-09-27; `zai-coding-cn` is not allowlisted. The flash coding-plan row keeps $0.15 / $0.50 (cache read $0.03). `zai-coding-cn/glm-5.3` output is $1.40 | `openrouter/z-ai/glm-5.3-flash`: admitted, route `pi-glm-5-3-flash-openrouter`, read-only fallback for reviewer-arch. Its own tier, verified 2026-10-20: $0.047076 input, $0.12 output, $0.006 cache read | Z.ai has no native harness. The two flash rows do not share a YAML anchor. |
185
+ | GLM 5.3 and 5.3 Flash | `zai-coding-cn/glm-5.3` and `…/glm-5.3-flash`: removed from the admitted list on 2026-09-27; `zai-coding-cn` is not allowlisted. The flash coding-plan row keeps $0.15 / $0.50 (cache read $0.03). `zai-coding-cn/glm-5.3` output is $1.40 | `openrouter/z-ai/glm-5.3-flash`: admitted, route `pi-glm-5-3-flash-openrouter`, read-only fallback for reviewer-arch. Its own tier, verified 2026-10-21: $0.15 input, $0.50 output, $0.03 cache read. This list price is volatile and has flipped with $0.047076 / $0.12 / $0.006 for several days; trust the weekly refresh over the static row | Z.ai has no native harness. The two flash rows do not share a YAML anchor. |
186
186
  | DeepSeek V4.1 Flash | `qwen-token-plan/deepseek-v4.1-flash`: removed from the admitted list on 2026-09-27 | `deepseek/deepseek-v4.1-flash` in the OpenRouter feed | Bills a DeepSeek model through Alibaba's plan. `deepseek` is a native-vendor Pi brake, so this is not a route. |
187
187
 
188
188
  The developer runner is stricter. Its only Pi writer is `openrouter/qwen/qwen3.7-plus`, and it does not allowlist `qwen-token-plan` or `zai-coding-cn`. Today OpenRouter is the right answer here for Qwen3.7 Plus, Qwen3.8 Flash and GLM 5.3 Flash. `qwen/qwen3-coder-plus` is retired.
@@ -922,13 +922,13 @@ kxm vision assert --image <path> --question <text> [--route <id>] [--timeout-ms
922
922
  |---|---|---|---|
923
923
  | `--image` | `<path>` | required | Screenshot file. A missing file fails closed with `image unreadable` |
924
924
  | `--question` | `<text>` | required | Yes/no assertion, trimmed to 500 characters inside the prompt |
925
- | `--route` | `<id>` | `zai-coding-cn/glm-5.3-flash` | Admitted route id. The default keeps the verified Pi command `--provider zai-coding-cn --model glm-5.3-flash`. Any other id is `pi --model <id>` after the native-vendor brake |
925
+ | `--route` | `<id>` | `openrouter/z-ai/glm-5.3-flash` | Admitted route id. Pi is invoked as `pi --model <id>` after the native-vendor brake |
926
926
  | `--timeout-ms` | `<n>` | `90000` | Integer from 5000 through 300000 |
927
927
  | `--expect` | `true` or `false` | none | Exit 0 only when the verdict equals this value |
928
928
 
929
929
  - Global options apply, except `--dry-run`, which exits 2 with `dry_run_unsupported` because the command calls Pi.
930
- - The route must be listed under `admitted` in `.kxm/routes.yaml`. Otherwise the command exits 1 and the `divergence` names the id and `kxm routes admit --model <id>`.
931
- - This repository does not admit the default `zai-coding-cn/glm-5.3-flash` (`zai-coding-cn` is not an allowlisted harness). Pass `--route openrouter/z-ai/glm-5.3-flash`, which is admitted here, or admit the route you pass.
930
+ - The route must be listed under `admitted` in `.kxm/routes.yaml`. Otherwise the command exits 1 and the `divergence` names the id and `kxm routes admit --model <id>`. `kxm init` admits the default. This repository already admits `openrouter/z-ai/glm-5.3-flash`.
931
+ - A live accuracy witness for that id is `test/fixtures/vision/manifest.json` plus `test/core/vision-accuracy.test.ts`. It calls Pi only when `OPENROUTER_API_KEY` is set. Otherwise it checks the fixture set and does not call the network. Run it with `node --disable-warning=ExperimentalWarning --experimental-strip-types --test --test-force-exit test/core/vision-accuracy.test.ts`.
932
932
  - A native-vendor id such as `xai/grok-4.7` or `anthropic/fable` is refused before Pi starts, even when that id is admitted. The `divergence` is the Pi brake message.
933
933
  - Exit 0 when the verdict resolves and, if `--expect` is set, matches it. Exit 1 for a fail-closed divergence. Exit 2 when `--image` or `--question` is missing, `--expect` is not `true` or `false`, or `--timeout-ms` is not an integer.
934
934
  - JSON keys: `ok`, `command` (`vision assert`), `verdict` (`true`, `false`, or `null`), `route`, `raw`, `divergence` when the gate fails closed, and `expect` when `--expect` is set.
@@ -398,8 +398,10 @@ resultSchema: kxm.assignment-result.v1
398
398
 
399
399
  `kxm init` creates `planner` (`role: planner`, alias `coordinator`) and `writer`
400
400
  (`role: writer`, alias `implementer`) without `harness` or `model`, and admits
401
- `anthropic/fable` and `xai/grok-4.7` in `.kxm/routes.yaml`. The model files are
402
- `claude-fable` and `grok-grok-4-7`. An interactive `kxm init` can add
401
+ `anthropic/fable`, `xai/grok-4.7`, and the vision route
402
+ `openrouter/z-ai/glm-5.3-flash` in `.kxm/routes.yaml`. The model files are
403
+ `claude-fable` and `grok-grok-4-7`. The vision route has no model file and is
404
+ not a roster entry. An interactive `kxm init` can add
403
405
  workflow-guide agents for reviewed pairs whose harness is authenticated, and
404
406
  admits their selectors too, but skips Google guide candidates because the
405
407
  Runtime cannot reach the `antigravity` Pi provider yet. `kxm run` and the
@@ -258,7 +258,7 @@ The file is a `kxm.workflow.v1` definition. `coordinator` names the agent that o
258
258
 
259
259
  ### Why start with `first` and not `default`
260
260
 
261
- `kxm init` also writes a `default` workflow. Its coordinator plans on `claude` / `anthropic/fable`, its implementer writes the checkout on `grok` / `xai/grok-4.7`, and a `test` gate runs `npm test`; `kxm init` admits exactly those two models in `.kxm/routes.yaml`. You can drive it, but even a simulated drive runs `npm test` in your checkout, and a live drive spends both subscriptions and passes the implement step only when Grok changes the checkout. `first` calls no model and runs no command, so it is the safer first run. [Steps the Runtime does not execute yet](../reference/config-reference.md#steps-the-runtime-does-not-execute-yet) lists every setting that makes a drive hand the run off.
261
+ `kxm init` also writes a `default` workflow. Its coordinator plans on `claude` / `anthropic/fable`, its implementer writes the checkout on `grok` / `xai/grok-4.7`, and a `test` gate runs `npm test`. `kxm init` admits those two models and the vision route `openrouter/z-ai/glm-5.3-flash` in `.kxm/routes.yaml`. The vision id has no model file and is not on a roster. You can drive it, but even a simulated drive runs `npm test` in your checkout, and a live drive spends both subscriptions and passes the implement step only when Grok changes the checkout. `first` calls no model and runs no command, so it is the safer first run. [Steps the Runtime does not execute yet](../reference/config-reference.md#steps-the-runtime-does-not-execute-yet) lists every setting that makes a drive hand the run off.
262
262
 
263
263
  ### Two kinds of runs
264
264
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kontextmind/kxm",
3
- "version": "0.7.201",
3
+ "version": "0.7.203",
4
4
  "description": "KXM local-first multi-agent orchestration and operator dashboard",
5
5
  "type": "module",
6
6
  "author": "KontextMind",
@@ -2,7 +2,7 @@
2
2
  "$schema": "https://json.schemastore.org/claude-code-plugin-manifest.json",
3
3
  "name": "kxm",
4
4
  "displayName": "KXM",
5
- "version": "0.7.201",
5
+ "version": "0.7.203",
6
6
  "description": "Headless multi-agent orchestration, durable workflows, and a live operator dashboard for Pi and Claude Code",
7
7
  "author": {
8
8
  "name": "KontextMind",
@@ -18825,7 +18825,7 @@ function coreTemplate(projectId, projectName, variant) {
18825
18825
  files.set(".kxm/routes.yaml", {
18826
18826
  schema: "kxm.routes.v2",
18827
18827
  updatedAt: "2026-10-21T00:00:00.000Z",
18828
- admitted: ["anthropic/fable", "xai/grok-4.7"],
18828
+ admitted: ["anthropic/fable", "xai/grok-4.7", "openrouter/z-ai/glm-5.3-flash"],
18829
18829
  disabled: []
18830
18830
  });
18831
18831
  files.set(".kxm/models/grok-grok-4-7.yaml", {
@@ -28904,9 +28904,6 @@ function parseVisionVerdict(raw) {
28904
28904
  return match ? match[1] === "true" : null;
28905
28905
  }
28906
28906
  function visionPiArgs(route) {
28907
- if (route === VISION_GATE_DEFAULT_ROUTE) {
28908
- return { ok: true, args: ["--provider", "zai-coding-cn", "--model", "glm-5.3-flash", "-p", "--no-session"] };
28909
- }
28910
28907
  const validation = validateHarnessModelPair("pi", route);
28911
28908
  if (!validation.valid) {
28912
28909
  return { ok: false, divergence: validation.message ?? "route_not_pi" };
@@ -28959,7 +28956,7 @@ var init_vision_gate = __esm({
28959
28956
  "use strict";
28960
28957
  init_harness();
28961
28958
  init_routes();
28962
- VISION_GATE_DEFAULT_ROUTE = "zai-coding-cn/glm-5.3-flash";
28959
+ VISION_GATE_DEFAULT_ROUTE = "openrouter/z-ai/glm-5.3-flash";
28963
28960
  QUESTION_MAX_CHARS = 500;
28964
28961
  RAW_MAX_CHARS = 2e3;
28965
28962
  }
@@ -58732,7 +58729,7 @@ function createProgram(ctx, result, argv) {
58732
58729
  }
58733
58730
  const visionCmd = addGlobalOptions(program2.command("vision").description("Judge captured UI screenshots through the admitted vision gate"));
58734
58731
  visionCmd.helpCommand("help", "Show vision help");
58735
- addGlobalOptions(visionCmd.command("assert").description("Assert a UI state on a screenshot (deterministic vision gate)")).requiredOption("--image <path>", "Path to the captured screenshot").requiredOption("--question <text>", "Yes/no assertion to verify on the screenshot").option("--route <id>", `Vision route (default ${"zai-coding-cn/glm-5.3-flash"})`).option("--timeout-ms <n>", "Transport timeout in milliseconds (5000-300000)").option("--expect <bool>", "Exit 0 only when the verdict matches true|false").action(async function visionAssertAction(options) {
58732
+ addGlobalOptions(visionCmd.command("assert").description("Assert a UI state on a screenshot (deterministic vision gate)")).requiredOption("--image <path>", "Path to the captured screenshot").requiredOption("--question <text>", "Yes/no assertion to verify on the screenshot").option("--route <id>", `Vision route (default ${"openrouter/z-ai/glm-5.3-flash"})`).option("--timeout-ms <n>", "Transport timeout in milliseconds (5000-300000)").option("--expect <bool>", "Exit 0 only when the verdict matches true|false").action(async function visionAssertAction(options) {
58736
58733
  result.code = await cmdVisionAssert(runtimeFrom(ctx, this), options ?? {});
58737
58734
  });
58738
58735
  const harnessCmd = addGlobalOptions(program2.command("harness").description("Detect coding-agent harnesses and authentication"));
@@ -25462,7 +25462,7 @@ function sessionTokenFixHint(policy) {
25462
25462
  }
25463
25463
 
25464
25464
  // plugins/kxm/src/mcp-server.ts
25465
- var VERSION = "0.7.201";
25465
+ var VERSION = "0.7.203";
25466
25466
  var CONFIGURE_PLUGIN = "/plugin configure kxm@kxm";
25467
25467
  var inbox = /* @__PURE__ */ new Map();
25468
25468
  var notifiedInbox = /* @__PURE__ */ new Set();
@@ -15973,7 +15973,7 @@ function coreTemplate(projectId, projectName, variant) {
15973
15973
  files.set(".kxm/routes.yaml", {
15974
15974
  schema: "kxm.routes.v2",
15975
15975
  updatedAt: "2026-10-21T00:00:00.000Z",
15976
- admitted: ["anthropic/fable", "xai/grok-4.7"],
15976
+ admitted: ["anthropic/fable", "xai/grok-4.7", "openrouter/z-ai/glm-5.3-flash"],
15977
15977
  disabled: []
15978
15978
  });
15979
15979
  files.set(".kxm/models/grok-grok-4-7.yaml", {
@@ -15946,7 +15946,7 @@ function coreTemplate(projectId, projectName, variant) {
15946
15946
  files.set(".kxm/routes.yaml", {
15947
15947
  schema: "kxm.routes.v2",
15948
15948
  updatedAt: "2026-10-21T00:00:00.000Z",
15949
- admitted: ["anthropic/fable", "xai/grok-4.7"],
15949
+ admitted: ["anthropic/fable", "xai/grok-4.7", "openrouter/z-ai/glm-5.3-flash"],
15950
15950
  disabled: []
15951
15951
  });
15952
15952
  files.set(".kxm/models/grok-grok-4-7.yaml", {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-plugin",
3
- "version": "0.7.201",
3
+ "version": "0.7.203",
4
4
  "private": true,
5
5
  "type": "module",
6
6
  "engines": {
@@ -106,5 +106,5 @@ kxm vision assert --image .kxm/artifacts/browser/shot.png \
106
106
  ```
107
107
 
108
108
  - Exit 0 only when the verdict resolves and matches `--expect`; fail-closed divergences (`route_not_admitted`, `image unreadable`, `verdict unreadable`, or the Pi native-vendor brake) exit 1.
109
- - The route must be admitted in `.kxm/routes.yaml`. The built-in default is `zai-coding-cn/glm-5.3-flash` (the verified Pi invocation `--provider zai-coding-cn --model glm-5.3-flash`). This checkout does not admit that id. Pass `--route openrouter/z-ai/glm-5.3-flash`, which is admitted here. `--route` selects the Pi model; a native-vendor id is refused before Pi starts.
109
+ - The route must be admitted in `.kxm/routes.yaml`. The built-in default is `openrouter/z-ai/glm-5.3-flash`, invoked as `pi --model openrouter/z-ai/glm-5.3-flash`. `kxm init` admits that id, and this checkout already admits it. `--route` selects the Pi model; a native-vendor id is refused before Pi starts.
110
110
  - The gate is a supplement to deterministic Playwright assertions, never a replacement.
@@ -757,7 +757,7 @@ function createProgram(ctx: CliContext, result: { code: number }, argv: readonly
757
757
  addGlobalOptions(visionCmd.command("assert").description("Assert a UI state on a screenshot (deterministic vision gate)"))
758
758
  .requiredOption("--image <path>", "Path to the captured screenshot")
759
759
  .requiredOption("--question <text>", "Yes/no assertion to verify on the screenshot")
760
- .option("--route <id>", `Vision route (default ${"zai-coding-cn/glm-5.3-flash"})`)
760
+ .option("--route <id>", `Vision route (default ${"openrouter/z-ai/glm-5.3-flash"})`)
761
761
  .option("--timeout-ms <n>", "Transport timeout in milliseconds (5000-300000)")
762
762
  .option("--expect <bool>", "Exit 0 only when the verdict matches true|false")
763
763
  .action(async function visionAssertAction(this: Command, options: { image?: string; question?: string; route?: string; timeoutMs?: string; expect?: string }) { result.code = await cmdVisionAssert(runtimeFrom(ctx, this), options ?? {}); });
@@ -11,7 +11,7 @@ import { deliverInboxNotification } from "./inbox.ts";
11
11
  import type { HubEvent, MessageRecord } from "./protocol.ts";
12
12
  import { sessionTokenFixHint } from "./session-token-hint.ts";
13
13
 
14
- const VERSION = "0.7.201";
14
+ const VERSION = "0.7.203";
15
15
  const CONFIGURE_PLUGIN = "/plugin configure kxm@kxm";
16
16
  const inbox = new Map<string, MessageRecord>();
17
17
  const notifiedInbox = new Set<string>();
@@ -209,14 +209,16 @@ function coreTemplate(projectId: string, projectName: string, variant: KxmTempla
209
209
  });
210
210
  files.set(".kxm/workflows/default.yaml", { ...workflow, coordinator: "planner", limits, steps });
211
211
  }
212
- // The two models the template names, and nothing else. A fresh project can
213
- // be driven without falling through to an unadmitted default model.
212
+ // The two roster models the template names, plus the vision selector the
213
+ // built-in gate defaults to. That selector has no model file and is not a
214
+ // roster route. A fresh project can be driven without falling through to
215
+ // an unadmitted default model.
214
216
  // Route ids follow <harness>-<model-slug>. grok-default and fable-default
215
217
  // remain aliases so an existing --route flag still resolves.
216
218
  files.set(".kxm/routes.yaml", {
217
219
  schema: "kxm.routes.v2",
218
220
  updatedAt: "2026-10-21T00:00:00.000Z",
219
- admitted: ["anthropic/fable", "xai/grok-4.7"],
221
+ admitted: ["anthropic/fable", "xai/grok-4.7", "openrouter/z-ai/glm-5.3-flash"],
220
222
  disabled: [],
221
223
  });
222
224
  files.set(".kxm/models/grok-grok-4-7.yaml", {
@@ -10,7 +10,7 @@ import { isRouteAdmitted } from "./routes.ts";
10
10
  * `divergence` reason. Transport is injectable so tests run without
11
11
  * network. */
12
12
 
13
- export const VISION_GATE_DEFAULT_ROUTE = "zai-coding-cn/glm-5.3-flash";
13
+ export const VISION_GATE_DEFAULT_ROUTE = "openrouter/z-ai/glm-5.3-flash";
14
14
  const QUESTION_MAX_CHARS = 500;
15
15
  const RAW_MAX_CHARS = 2000;
16
16
 
@@ -54,13 +54,9 @@ export function parseVisionVerdict(raw: string): boolean | null {
54
54
  }
55
55
 
56
56
  /** Pi argv for an admitted vision route, without the prompt.
57
- * The verified default keeps `--provider zai-coding-cn --model glm-5.3-flash`.
58
- * Every other id is `pi --model <id>` after the native-vendor brake, so
59
- * `--route` selects the model instead of only the admission check. */
57
+ * Every id, including the default, is `pi --model <id>` after the
58
+ * native-vendor brake, so `--route` selects the model. */
60
59
  export function visionPiArgs(route: string): { ok: true; args: readonly string[] } | { ok: false; divergence: string } {
61
- if (route === VISION_GATE_DEFAULT_ROUTE) {
62
- return { ok: true, args: ["--provider", "zai-coding-cn", "--model", "glm-5.3-flash", "-p", "--no-session"] };
63
- }
64
60
  const validation = validateHarnessModelPair("pi", route);
65
61
  if (!validation.valid) {
66
62
  return { ok: false, divergence: validation.message ?? "route_not_pi" };