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
1 change: 1 addition & 0 deletions .github/PULL_REQUEST_TEMPLATE.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@

## Checklist

- [ ] This PR adds a new layer/module/abstraction — it names or deletes the layer it replaces
- [ ] Updated docs or comments as needed
- [ ] Added or updated tests where relevant
- [ ] Verified TUI behavior manually if UI changes
Expand Down
6 changes: 3 additions & 3 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@ jobs:
;;
esac
case "${path}" in
crates/workflow/*|workflows/rlm_cache_change.star|.github/workflows/ci.yml)
crates/workflow/*|.github/workflows/ci.yml)
workflow=true
;;
esac
Expand Down Expand Up @@ -484,8 +484,8 @@ jobs:
with:
cache-bin: false
save-if: ${{ github.ref == 'refs/heads/main' }}
- name: Run RLM cache workflow mock/replay tests
run: cargo test -p codewhale-workflow --locked rlm_cache_change
- name: Run workflow crate tests
run: cargo test -p codewhale-workflow --locked

test:
name: Test
Expand Down
7 changes: 7 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
# Build artifacts
/target
/target-*
.entire/
.cursor/
.plans/
.codewhale-worktrees/
docs/superpowers/
codewhale-inference/
/extensions/vscode/out/
*.pdb
*.exe
Expand Down
4 changes: 4 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@ instructions or memory. The nearest scoped `AGENTS.md` adds path-specific rules.

- Inspect status and existing consumers before editing. Preserve unrelated,
dirty, and untracked work.
- Before adding a module named `model_*`, `*_config`, `provider_*`, or
anything that "bridges", "mirrors", or "stages" an existing thing, grep
for the existing thing and edit it. A new layer must name the predecessor
it replaces in the module doc; otherwise edit the original.
- Prefer the simplest implementation that preserves observable contracts. A
rewrite is acceptable when justified by product intent and observed behavior,
not as a shortcut around understanding existing code.
Expand Down
23 changes: 23 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,20 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Added

- Z.ai `GLM-5.3-Flash` and OpenRouter `z-ai/glm-5.3-flash` are first-class
picker rows (`/model GLM-5.3-Flash`). Flash is the faster/explore sibling
of `GLM-5.3`; the Z.ai default stays `GLM-5.3`. List price is $0.15/$0.50
per 1M (durable; the 50% promo through 2026-09-09 is not the catalog row).
- Baseten, Groq, and Cerebras are bundled OpenAI-compatible setup templates
(`[providers.<id>] kind = "openai-compatible"`), not new `ProviderKind`
variants. `/provider` fills URL, model, and env from one catalog row.
- MCP manager copy now names the server, the failure, and one recovery
command (`The X MCP server requires OAuth reauthentication. Run /mcp login X`).
- Settings Advanced exposes clickable MCP Connect, Reconnect, and Diagnose
actions, while Extensions and Problems route recovery through the existing
`/mcp login`, `/mcp reload`, `/mcp validate`, and `/plugin validate` commands
(#5643, #5655).

- Added `/import-claude` (#5557): reads `~/.claude.json` and
`~/.claude/settings.json` read-only and renders an explicit, reviewable
migration plan plus a written report. MCP servers route through the
Expand Down Expand Up @@ -72,6 +86,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Changed

- Idle session metrics omit zero facts (`0 turns`, `LLM 0s`) until the
runtime has evidence. Working chrome says `in the current` instead of a
generic `working`.
- Deleted nine uncompiled `runtime_contract/` staging files. Live contracts
remain `model.rs` and `termination.rs`.

- The first #5587 dead-code sweep converts audited test-only helpers to
`#[cfg(test)]`, keeping production builds free of test-only APIs without
changing runtime behavior.
- `/plugin reload` is now discoverable when on-disk plugin bundles change: the
next send and `/plugin list` nudge once with `Run /plugin reload to apply`
instead of silently keeping the stale catalog (#5579). Trust is unchanged;
Expand Down
15 changes: 9 additions & 6 deletions config.example.toml
Original file line number Diff line number Diff line change
Expand Up @@ -46,12 +46,14 @@ base_url = "https://api.deepseek.com/beta"
# z-ai/glm-5.2 — OpenRouter Z.AI GLM 5.2
# z-ai/glm-5.3 — OpenRouter Z.AI GLM 5.3 (live on Z.ai since 2026-08-13;
# metadata inherited from 5.2, unpriced)
# z-ai/glm-5-turbo — OpenRouter Z.AI GLM 5 Turbo (scout fast sibling)
# z-ai/glm-5.3-flash — OpenRouter Z.AI GLM 5.3 Flash (1M multimodal; $0.15/$0.50 list)
# z-ai/glm-5-turbo — OpenRouter Z.AI GLM 5 Turbo (scout fast sibling of 5.2)
# GLM-5.3 — default direct Z.AI Coding Plan model (live since 2026-08-13;
# metadata inherited from 5.2, unpriced)
# GLM-5.3-Flash — direct Z.AI GLM 5.3 Flash (faster/explore sibling of 5.3)
# GLM-5.2 — direct Z.AI GLM 5.2 (previous default; explicit selections keep it)
# GLM-5.1 — direct Z.AI smaller model
# GLM-5-Turbo — direct Z.AI fast model (scout fast sibling)
# GLM-5-Turbo — direct Z.AI fast model (scout fast sibling of 5.2)
# step-3.7-flash — default direct StepFun / StepFlash model ID
# kimi-k3 — direct Moonshot K3 model ID (1M context)
# kimi-k2.7-code — default direct Moonshot/Kimi K2.7 model ID
Expand Down Expand Up @@ -674,12 +676,13 @@ max_subagents = 10 # optional (default 64, clamped to 1-128)
# base_url = "https://api.z.ai/api/coding/paas/v4"
# # General API endpoint, if you are not using the Coding Plan:
# # base_url = "https://api.z.ai/api/paas/v4"
# model = "GLM-5.3" # default; GLM-5.2 is the previous default, GLM-5.1 the smaller model, GLM-5-Turbo the fast sub-agent sibling
# model = "GLM-5.3" # default; GLM-5.3-Flash is the fast sibling, GLM-5.2 the previous default, GLM-5.1 the smaller model, GLM-5-Turbo the 5.2 fast sibling
# # GLM-5.3 is live on the Z.ai Coding Plan (2026-08-13). Its catalog metadata
# # (limits, reasoning options) is inherited from GLM-5.2 until Z.ai publishes
# # distinct 5.3 numbers, and it carries no price. An explicit model = "GLM-5.2"
# # keeps sending GLM-5.2; only the default moved. Accounts not provisioned for
# # 5.3 can still see a 429 with entitlement code 1311.
# # distinct 5.3 numbers, and it carries no price. GLM-5.3-Flash (2026-08-26)
# # is the 1M multimodal picker row (`model = "GLM-5.3-Flash"`). An explicit
# # model = "GLM-5.2" keeps sending GLM-5.2; only the default moved. Accounts
# # not provisioned for 5.3 can still see a 429 with entitlement code 1311.

# StepFun / StepFlash direct OpenAI-compatible endpoint (https://platform.stepfun.ai)
[providers.stepfun]
Expand Down
24 changes: 24 additions & 0 deletions crates/agent/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -516,6 +516,13 @@ impl Default for ModelRegistry {
supports_tools: true,
supports_reasoning: true,
},
ModelInfo {
id: "z-ai/glm-5.3-flash".to_string(),
provider: ProviderKind::Openrouter,
aliases: vec!["glm-5.3-flash".to_string(), "zai-glm-5.3-flash".to_string()],
supports_tools: true,
supports_reasoning: true,
},
ModelInfo {
id: "z-ai/glm-5-turbo".to_string(),
provider: ProviderKind::Openrouter,
Expand All @@ -535,6 +542,18 @@ impl Default for ModelRegistry {
supports_tools: true,
supports_reasoning: true,
},
ModelInfo {
id: "GLM-5.3-Flash".to_string(),
provider: ProviderKind::Zai,
aliases: vec![
"glm-5.3-flash".to_string(),
"glm-5-3-flash".to_string(),
"zai-glm-5.3-flash".to_string(),
"zai-glm-5-3-flash".to_string(),
],
supports_tools: true,
supports_reasoning: true,
},
// The first Z.ai row is the provider default. Keep this ordering
// aligned with `DEFAULT_ZAI_MODEL` in codewhale-config.
ModelInfo {
Expand Down Expand Up @@ -2321,6 +2340,10 @@ mod tests {
("glm-5.3", "GLM-5.3"),
("glm-5-3", "GLM-5.3"),
("zai-glm-5-3", "GLM-5.3"),
("GLM-5.3-Flash", "GLM-5.3-Flash"),
("glm-5.3-flash", "GLM-5.3-Flash"),
("glm-5-3-flash", "GLM-5.3-Flash"),
("zai-glm-5.3-flash", "GLM-5.3-Flash"),
("GLM-5-Turbo", "GLM-5-Turbo"),
("glm-5-turbo", "GLM-5-Turbo"),
("zai-glm-5-turbo", "GLM-5-Turbo"),
Expand Down Expand Up @@ -2555,6 +2578,7 @@ mod tests {
("glm-5.1", "z-ai/glm-5.1"),
("glm-5.2", "z-ai/glm-5.2"),
("glm-5.3", "z-ai/glm-5.3"),
("glm-5.3-flash", "z-ai/glm-5.3-flash"),
("minimax-m3", "minimax/minimax-m3"),
("minimax-2.7", "minimax/minimax-m2.7"),
("openrouter-mimo-v2.5-pro", "xiaomi/mimo-v2.5-pro"),
Expand Down
26 changes: 25 additions & 1 deletion crates/config/assets/models_dev.bundled.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,9 @@
"curated": "qwen3.8-max (GA) is curated ahead of upstream Models.dev, which as of 2026-08-03 lists only qwen3.8-max-preview; facts verified against the owner's Token Plan console (2026-08-03): ~1M context, 128K output, image understanding, always-on reasoning. deepseek-v4-flash-0731 keeps the console/in-repo wire id for the row upstream serves as deepseek-v4-flash. Coding Plan rows for qwen3.8-max-preview, deepseek-v4-pro, deepseek-v4-flash-0731, and glm-5.2 are curated from the Token Plan upstream entries (upstream alibaba-coding-plan does not list them yet); the in-repo route layer already offers the same model set on both plans. Upstream provider ids alibaba-token-plan(-cn) / alibaba-coding-plan(-cn) were merged onto the CodeWhale provider ids (live refresh normalizes them via ProviderKind aliases; the -cn regional variants stay upstream-id browse rows until Codewhale ships China endpoints).",
"qwen_3_8_flash_2026_08_26": "OpenRouter qwen/qwen3.8-flash recorded 2026-08-26 against https://models.dev/api.json: release_date 2026-08-26, 1,000,000 context / 131,072 output, text+image+video input / text output, reasoning true, tool_call true, family qwen. Durable list prices (no promo annotation): input 0.16, output 0.47, cache_read 0.016, cache_write 0.20 per 1M. Alibaba first-party lists only qwen3.8-max (already curated, unpriced on Token Plan); this seed curates the OpenRouter row only and does not invent a first-party flash id. Live refresh supersedes on (provider, wire_model_id) identity. The flash suffix is not a GA family default, so DEFAULT_MODELSTUDIO_TOKEN_PLAN_MODEL stays qwen3.8-max and this row is not default: true.",
"pending_release_metadata": "GLM-5.3 is live on the Z.ai Coding Plan (docs.z.ai/devpack/overview and docs.z.ai/devpack/latest-model, recorded 2026-08-13) and is the default direct Z.ai model (DEFAULT_ZAI_MODEL); explicit GLM-5.2 selections keep their own id. First-party wire id is GLM-5.3; OpenRouter mirror is z-ai/glm-5.3. Capability/limit/dialect values still inherit from GLM-5.2 until Z.ai publishes distinct 5.3 numbers. Pricing stays absent: Coding Plan publishes credit multipliers, not a USD PAYG row we can stand behind. Z.ai may auto-route GLM-5.2/GLM-5.1 requests to GLM-5.3 on their side; Codewhale still sends the selected picker id. Do not send a [1m] suffix. Scope stays first-party Z.ai plus the OpenRouter mirror; add third-party gateway rows only against that gateway's own published roster.",
"glm_5_3_flash_2026_08_26": "GLM-5.3-Flash recorded 2026-08-26 against https://docs.z.ai/guides/overview/pricing: natively multimodal (text/image/video), 1,000,000 context / 131,072 output, reasoning + tools. First-party wire id GLM-5.3-Flash; OpenRouter mirror z-ai/glm-5.3-flash. Durable list prices (not the 50% promo ending 2026-09-09 UTC+8): input 0.15, output 0.50, cache_read 0.03 per 1M. Flash is a picker row and the faster/explore sibling of GLM-5.3; DEFAULT_ZAI_MODEL stays GLM-5.3.",
"currency_sweep_2026_08_17": "Rows re-verified against official pages on 2026-08-17 (#5470 follow-up): gpt-5.6-terra 2.00/12.00 (cache read 0.20, write 2.50) and gpt-5.6-luna 0.20/1.20 (0.02/0.25) per developers.openai.com model pages; claude-sonnet-5 2.00/10.00 (0.20/2.50) is now Anthropic's standard price (the 2026-09-01 increase was cancelled) and claude-opus-5 5.00/25.00 (0.50/6.25) was added; kimi-k3 3.00/15.00 (0.30) and kimi-k2.7-code-highspeed 1.90/8.00 (0.38) per platform.kimi.ai; MiniMax-M2.7-highspeed 0.60/2.40 (0.06/0.375) per platform.minimax.io; grok-4.5 (500K) and grok-4.3 (1M) carry limits only because xAI doubles their rates past 200K (same rule as grok-4.6); OpenRouter dots-studio/dots-3-note-preview:free carries limits only (its single free endpoint publishes $0, which this seed does not restate as a price).",
"coverage": "20 providers, 90 model rows (offline seed only)."
"coverage": "20 providers, 92 model rows (offline seed only)."
},
"models": {
"deepseek-v4-pro": {
Expand Down Expand Up @@ -104,6 +105,18 @@
"modalities": { "input": ["text"], "output": ["text"] },
"limit": { "context": 1000000, "output": 131072 }
},
"GLM-5.3-Flash": {
"id": "GLM-5.3-Flash",
"name": "GLM-5.3-Flash",
"family": "glm",
"attachment": true,
"reasoning": true,
"reasoning_options": [{ "type": "effort", "values": ["high", "max"] }],
"tool_call": true,
"modalities": { "input": ["text", "image", "video"], "output": ["text"] },
"limit": { "context": 1000000, "output": 131072 },
"cost": { "input": 0.15, "output": 0.50, "cache_read": 0.03 }
},
"glm-5.1": {
"id": "glm-5.1",
"name": "GLM-5.1",
Expand Down Expand Up @@ -1099,6 +1112,17 @@
"modalities": { "input": ["text"], "output": ["text"] },
"limit": { "context": 1000000, "output": 131072 }
},
"z-ai/glm-5.3-flash": {
"id": "z-ai/glm-5.3-flash",
"name": "GLM-5.3-Flash (OpenRouter)",
"family": "glm",
"attachment": true,
"reasoning": true,
"tool_call": true,
"modalities": { "input": ["text", "image", "video"], "output": ["text"] },
"limit": { "context": 1000000, "output": 131072 },
"cost": { "input": 0.15, "output": 0.50, "cache_read": 0.03 }
},
"dots-studio/dots-3-note-preview:free": {
"id": "dots-studio/dots-3-note-preview:free",
"name": "Dots Studio Dots3-Note Preview (OpenRouter, free endpoint)",
Expand Down
23 changes: 19 additions & 4 deletions crates/config/src/catalog.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
//! [`ProviderCatalogCache`] tests).

use std::collections::BTreeMap;
use std::sync::OnceLock;
use std::time::{SystemTime, UNIX_EPOCH};

use serde::{Deserialize, Serialize};
Expand Down Expand Up @@ -186,16 +187,30 @@ impl CatalogOffering {
/// honesty rule on omitted pricing (`UnknownOrStale`, never a fabricated zero).
pub const BUNDLED_MODELS_DEV_JSON: &str = include_str!("../assets/models_dev.bundled.json");

/// Parse-once cache for the committed bundled Models.dev snapshot.
///
/// The bundled asset is compile-time constant (`include_str!`), so its parsed
/// form is immutable and safe to share process-wide. Before this cache, every
/// call site parsed the full snapshot independently — the client route path,
/// pickers, provider lake, and fleet identity each paid a full serde parse of
/// ~50KB on their own first use (perf-attributed during the 0.9.x perf
/// gauntlet: `ModelsDevCost` serde frames in startup profiles).
static BUNDLED_MODELS_DEV_CATALOG: OnceLock<ModelsDevCatalog> = OnceLock::new();

/// Parse the committed bundled Models.dev snapshot.
///
/// The first call parses; later calls return the shared parsed catalog.
///
/// # Panics
/// Panics only if the committed asset is not valid Models.dev JSON. The
/// `tests::bundled_asset_parses` guard makes that a build-time failure, so this
/// never panics in shipped builds.
#[must_use]
pub fn bundled_models_dev_catalog() -> ModelsDevCatalog {
ModelsDevCatalog::parse_json(BUNDLED_MODELS_DEV_JSON)
.expect("committed bundled Models.dev asset must be valid JSON")
pub fn bundled_models_dev_catalog() -> &'static ModelsDevCatalog {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve the catalog helper's public return type

External users of the published codewhale-config crate that consume bundled_models_dev_catalog() as an owned ModelsDevCatalog will stop compiling because this performance change alters the public signature to &'static ModelsDevCatalog. Keep the existing owned-returning API and add an internal or additive borrowed cached accessor instead, so parse memoization does not introduce a source-breaking change in a patch release.

AGENTS.md reference: AGENTS.md:L15-L17

Useful? React with 👍 / 👎.

BUNDLED_MODELS_DEV_CATALOG.get_or_init(|| {
ModelsDevCatalog::parse_json(BUNDLED_MODELS_DEV_JSON)
.expect("committed bundled Models.dev asset must be valid JSON")
})
}

/// Bundled-layer [`CatalogOffering`] rows from the offline snapshot (#4188).
Expand All @@ -205,7 +220,7 @@ pub fn bundled_models_dev_catalog() -> ModelsDevCatalog {
/// rows override these on `(provider, wire_model_id)` when available.
#[must_use]
pub fn bundled_catalog_offerings() -> Vec<CatalogOffering> {
bundled_offerings_from_models_dev(&bundled_models_dev_catalog())
bundled_offerings_from_models_dev(bundled_models_dev_catalog())
}

/// Hydrate bundled [`CatalogOffering`] rows from a parsed Models.dev catalog.
Expand Down
25 changes: 22 additions & 3 deletions crates/config/src/catalog/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -648,7 +648,7 @@ fn bundled_asset_parses() {
"bundled asset must carry provider rows"
);
// The helper returns the same parsed catalog.
assert_eq!(bundled_models_dev_catalog(), catalog);
assert_eq!(*bundled_models_dev_catalog(), catalog);
}

#[test]
Expand Down Expand Up @@ -851,9 +851,11 @@ fn bundled_asset_pricing_is_honest() {

// GLM-5.3 is live on the Coding Plan, but Z.ai has published no USD PAYG
// rate for it. Coding Plan credit multipliers are not USD, so every
// glm-5.3 row stays unpriced rather than inheriting glm-5.2's rates.
// glm-5.3 row *except Flash* stays unpriced rather than inheriting
// glm-5.2's rates. GLM-5.3-Flash has a published list (2026-08-26).
for row in &rows {
if row.wire_model_id.to_ascii_lowercase().contains("glm-5.3") {
let wire = row.wire_model_id.to_ascii_lowercase();
if wire.contains("glm-5.3") && !wire.contains("flash") {
assert!(
row.cost.is_none(),
"{}/{}: glm-5.3 must stay unpriced until Z.ai publishes rates",
Expand All @@ -863,6 +865,23 @@ fn bundled_asset_pricing_is_honest() {
}
}

let glm53_flash = find(&rows, "zai", "GLM-5.3-Flash");
let cost = glm53_flash
.cost
.as_ref()
.expect("GLM-5.3-Flash must ship priced at durable list rates");
assert_eq!(cost.input, Some(0.15));
assert_eq!(cost.output, Some(0.50));
assert_eq!(cost.cache_read, Some(0.03));
assert_eq!(
glm53_flash.limit.as_ref().and_then(|l| l.context),
Some(1_000_000)
);
assert!(
!glm53_flash.default_for_provider,
"GLM-5.3-Flash is a picker row, not the Z.ai default"
);

// OpenRouter qwen3.8-flash lists durable (non-promo) rates on models.dev
// as of 2026-08-26. Unlike GLM-5.3-Flash's explicit 50% promo, this row
// must ship priced. It is not a family default.
Expand Down
Loading
Loading