The Agent Built
for Kimi & GLM
A lean, native Rust agentic CLI for Moonshot.ai (Kimi) and Z.ai (GLM) — with first-class Devscriptor code intelligence over MCP. Not another OpenAI-compatible shim: a harness that speaks each vendor's actual wire protocol.
Frontier models, lowest-common-denominator plumbing
Kimi and GLM are among the strongest coding models available — and almost every agent talks to them through a generic OpenAI-compatible interface. That interface is a lossy adapter: it silently discards the capabilities these models were built around. Juggler was written to close that gap.
Reasoning is thrown away every turn
A generic client drops the model’s reasoning content between turns — the model re-derives what it already worked out.
Preserved Thinking replays each stored reasoning block back to the provider verbatim, which is exactly what both vendors require.
Reasoning effort is sent to models that reject it
`reasoning_effort` is a GLM-5.2-and-newer parameter. Moonshot’s schema has no such property.
Juggler routes it only to models that accept it, and never sends it to a Kimi model.
Temperature is injected where it does not exist
The Kimi K2 request schemas have no `temperature` property at all — only the legacy `moonshot-v1-*` models do.
Juggler deliberately omits it for K2, and clamps + sets `do_sample: false` for GLM when temperature ≤ 0.
Real tool schemas get rejected
Moonshot’s validator is stricter than OpenAI’s. Realistic MCP tool schemas — with `$ref`s and `$defs` — bounce.
Juggler normalizes every schema: dereferences `$ref`s, preserves circular refs, prunes orphaned definitions, repairs `enum`/`const` vs `type` conflicts.
Open weights mean you can own the whole stack
Kimi and GLM are both released as open-weight models. That is not a footnote — it is the whole strategic case. The same flagship model you reach over the API can be downloaded and served on hardware you own, inside a network you control, with no third party in the path.
For a team that genuinely cannot send source code to an external provider, the economics are worth doing honestly: a one-time hardware purchase — on the order of a single senior engineer's annual salary — buys a permanently in-house, frontier-class coding model. No per-token billing, no rate limits, no data-processing agreement, and nothing leaving the building. Set against a recurring per-seat API spend, that capital cost stops looking exotic and starts looking like procurement.
Juggler is built for that end state. Point --base-url at your own OpenAI-compatible server and the agent runs against your self-hosted weights. Pair it with Devscriptor — which is already 100 % local, with no cloud backend — and every layer of the stack sits inside your perimeter: the model, the code index, and the agent that drives them.
Preserved Thinking
Carry a turn's reasoning into the next one instead of discarding it. Juggler replays each stored reasoning block back to the provider verbatim — which is what both vendors require. The two vendors spell it differently, with inverted polarity. Juggler translates.
| Kimi (Moonshot) | GLM (Z.AI) | |
|---|---|---|
| Wire field | thinking.keep | thinking.clear_thinking |
| Enabled | "all" | false |
| Disabled | field omitted | true |
| Supported by | kimi-k2.6 and newer, plus the kimi-for-coding aliases | every thinking model |
| Default when unset | on, whenever thinking is on | the endpoint decides |
Why it matters: a reasoning model that loses its own reasoning every turn re-derives the same conclusions on the next one — burning tokens and latency to arrive back where it started. Precedence is --thinking-keep → KIMI_MODEL_THINKING_KEEP → thinking_keep in the config file → the model's own default. Models whose schema lacks the field never receive it.
What “provider-native” actually means
Not marketing. These are the concrete things lm-bridge does that a shared OpenAI-compatible client structurally cannot.
Kimi K3 — the 1M-context flagship
Full support for Moonshot’s K3 flagship: 1,048,576-token context, image and video input, always-on reasoning, and low/high/max effort mapped onto --reasoning-effort. On the Coding Plan, K3 is its own model id (k3) — separate from the kimi-for-coding alias, which stays K2.7 at 262k.
Juggler defaults to kimi-for-coding (K2.7), matching the official Kimi Code CLI and avoiding K3’s slower, costlier max-effort default. Select K3 explicitly with `juggler --model k3` (or `export KIMI_MODEL_NAME=k3`, or `model = "k3"` in your config).
Version-aware context windows
Juggler carries a per-model context table — k3 and glm-5.2 at 1,048,576; kimi-for-coding, kimi-k2.7-code and kimi-k2.6 at 262,144; kimi-k2.5 and kimi-k2-0711-preview at 256,000 — instead of one conservative default.
A new minor version has never lowered a model’s window, so an uncatalogued glm-5.3 inherits glm-5.2’s 1M window rather than collapsing to a 128k default — which would make the agent compact its context far more often than it needs to.
Moonshot schema normalization
A dedicated pass rewrites JSON Schemas so Moonshot’s validator accepts them.
Dereferences local $ref pointers (#, #/$defs/X, #/definitions/X), detects and preserves circular references, drops definition buckets that are no longer referenced, injects missing type fields, and repairs conflicting enum/const vs. type.
Native Z.AI endpoints
Standalone Z.AI /web_search, /tools, and /reader HTTP resources, mirroring the official z.ai SDK — not bolted on through chat completions.
Juggler’s default GLM base URL is Z.AI’s Coding Plan endpoint, which preserves reasoning by default (the standard API endpoint does not). Leaving thinking_keep unset lets that endpoint default stand.
Kimi builtin_function & prompt caching
Kimi’s builtin_function tool type is passed through natively, and prompt_cache_key is set so Moonshot can reuse cached prefixes across turns.
Tool-call IDs are sanitized and deduplicated, and streaming tool-call deltas are routed in parallel so multiple concurrent calls assemble correctly.
Devscriptor, wired in on first run
Juggler auto-detects an installed Devscriptor and registers it as the devscriptor MCP server — 75 tools, zero configuration.
Discovery & startup
- 1
$DEVSCRIPTOR_BIN— an explicit path - 2
$PATH - 3Platform install directories (per-OS)
devscriptor mcp with a 60 s startup timeout and a 180 s per-tool timeout. An explicit [[mcp_servers]] entry named devscriptor always wins over auto-detection. Run juggler doctor to confirm it connects.A permission policy that fits
Without a policy, an agent would prompt on nearly every Devscriptor call — defeating the point of an indexed code-intelligence server. Juggler ships one tuned for it:
lsp_arch_setup · rename_symbol · prune_context · start/stop/restart_server
Everything else — scan, analyze, search, navigate, context notes, watcher. Path sandboxing is enforced independently, so an allow rule can never escape allowed_paths.
Registered as dynamic tools. Devscriptor's 75 tools would swamp a tool table on their own. Juggler announces them as one-line summaries and lets the model pull in what it needs with select_tools — so the initial context stays small and the full toolkit is still one call away.
Scaffolding that earns its keep
A good model still needs somewhere good to run. agent-core is the turn loop, the scheduler, the context manager, and the guardrails.
Conflict-aware parallel tools
The scheduler builds a read/write/read-write tree over tool arguments and runs everything it can in parallel — without letting two writers collide on the same path.
Progressive tool disclosure
Large MCP servers do not flood the tool table. Dynamic tools are announced as one-line summaries; the model calls select_tools to load what it actually needs.
Two-stage context compaction
Proactive compaction fires at 90 % of the token budget; an emergency pass catches a real overflow, including GLM’s 1261 context-length error. Genuine user messages are protected with a head/tail split.
Repeated-call guard
Identical calls inside one step run once. A call repeated across steps draws an escalating reminder at streaks of 3, 5 and 8 — and the turn is stopped outright at 12.
Sub-agents & swarms
agent and agent_swarm delegate work. A headless run holds the turn open until sub-agents reach a terminal state and feeds their results back, instead of exiting and killing them.
Rule-based permissions
Three modes (default, accept_edits, bypass_permissions) plus ordered rules where the first match wins. Path sandboxing is enforced independently, so an allow rule can never escape allowed_paths.
Background work & scheduling
Background tasks outlive the turn that started them. task_list / task_output / task_stop manage them; cron_create schedules recurring work.
Three front-ends
Headless (-p) for scripting and CI, an interactive REPL, and an optional TUI behind a Cargo feature. Sessions persist as JSONL records and export to a ZIP archive.
26 built-in tools
What a fresh agent advertises before a single MCP server is attached. MCP tools are namespaced mcp__<server>__<tool>.
Filesystem
read_filewrite_fileedit_filelist_dirglobgrepExecution
bashWeb
fetchweb_searchPlanning
todo_listenter_plan_modeexit_plan_modeDelegation
agentagent_swarmBackground work
task_listtask_outputtask_stopScheduling
cron_createcron_listcron_deleteGoals
create_goalget_goalInteraction
ask_userask_user_questionMedia
read_mediaTool disclosure
select_toolsupdate_goal and set_goal_budget are also registered but stay hidden until a goal is active. The whole table is pinned by a snapshot test — adding or renaming a tool fails the build until the documented list is updated.
Three providers, one binary
Out of the box you get each vendor's flagship. Override the model with --model and the endpoint with --base-url. Any other --provider value is rejected at startup.
--provider kimiMoonshot.ai
kimi-for-coding262,144https://api.kimi.com/coding/v1KIMI_API_KEY--provider glmz.ai (Zhipu)
glm-5.21,048,576https://api.z.ai/api/coding/paas/v4ZAI_API_KEY--provider openaiAny OpenAI-compatible endpoint
gpt-4o-mini—https://api.openai.com/v1OPENAI_API_KEYSelf-hosting? The openai provider speaks generic /chat/completions, so pointing --base-url at your own inference server runs Juggler entirely against weights you host yourself.
Two keys that look identical, and only one of them works
Both vendors sell the same models twice — a Coding Plan subscription and pay-per-token API credits — on separate endpoints. Each endpoint rejects the other's key with invalid_authentication_error, which reads like a typo and sends people off checking their clipboard. juggler init asks which key you hold and routes it to the door that opens.
One command
$ juggler init
Which provider?
1) Kimi (Moonshot) — kimi-for-coding / K2.7, 262k context
2) GLM (z.ai) — glm-5.2, 1M context
3) OpenAI-compatible endpoint
Which key do you have? (they are not interchangeable)
1) Coding Plan subscription
2) API credits (pay-per-token)
Paste your API key: ••••••••
checking… key accepted
using kimi-for-coding on https://api.kimi.com/coding/v1The key is verified against the endpoint before anything is written, and never echoed. The config is saved mode 600. Re-run it any time to switch provider or rotate a key — it edits the file rather than replacing it, so your permission rules and MCP servers stay put.
Where each key goes
| --billing | Model | Endpoint |
|---|---|---|
kimi subscriptiondefault | kimi-for-coding | https://api.kimi.com/coding/v1 |
kimi api | kimi-k2.7-code | https://api.moonshot.ai/v1 |
glm subscriptiondefault | glm-5.2 | https://api.z.ai/api/coding/paas/v4 |
glm api | glm-5.2 | https://api.z.ai/api/paas/v4 |
--region china selects the mainland deployments (api.moonshot.cn, open.bigmodel.cn). Kimi's Coding Plan has a single endpoint, so the region does not apply to it. kimi-for-coding and kimi-k2.7-code are the same K2.7 generation under the two names each endpoint serves it as.
If the endpoint rejects your key, Juggler offers the other plan rather than leaving you to guess — re-verifies against it, and saves whichever one actually worked. The same scoping holds afterwards: model, base_url, and api_key belong to the provider they were configured for, so switching provider re-derives them instead of sending a Kimi key to Z.AI.
Configure once, or not at all
juggler init writes this for you. Juggler reads ~/.config/juggler/config.toml or .juggler.toml in the current directory. CLI arguments override environment variables, which override file values.
provider = "glm"
api_key = "zai-..."
# Which quota the key is billed against: subscription (default) | api.
# This picks the endpoint, and with it the default model.
billing = "subscription"
region = "global" # global (default) | china
# Both optional: leaving them out lets provider + billing choose the
# flagship and its endpoint, so a newer flagship is picked up without
# editing anything. Set them to pin a specific one.
# model = "glm-5.2"
# base_url = "https://api.z.ai/api/coding/paas/v4"
# Sampling / budget
thinking = true
thinking_keep = false # Preserved Thinking
reasoning_effort = "high" # GLM-5.2 and newer only
max_tokens = 8192
# Filesystem and context
allowed_paths = ["/srv/data"]
additional_dirs = ["../sibling-repo"]
project_context_files = ["NOTES.md"]
project_context_budget_tokens = 2048
permission_mode = "accept_edits"
# Devscriptor is auto-detected — no entry needed here.
[[mcp_servers]]
name = "http-tools"
url = "http://localhost:8000/mcp"
# Permission policy; first matching rule wins.
[[permissions.rules]]
name = "allow-reads"
tool = "read_file"
action = "allow"Download Juggler 1.1.1
One universal binary per platform. No package managers, no build-from-source.
Free forever for personal and non-commercial work. Commercial use is free today too — see licensing.
Better together
Juggler drives the model. Devscriptor gives it a map of your codebase. Install both and the agent starts with 75 code-intelligence tools already on the table.