The Agent Built
for Kimi & GLM
A lean, native Rust agentic CLI for Moonshot.ai (Kimi) and z.ai (Zhipu 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 | 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.
Version-aware context windows
Juggler carries a per-model context table (kimi-k2.6 and kimi-k2.7-code 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 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 — 74 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 74 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
Override the model with --model and the endpoint with --base-url. Any other --provider value is rejected at startup.
--provider kimiMoonshot.ai
kimi-k2-0711-previewhttps://api.moonshot.cn/v1KIMI_API_KEY--provider glmz.ai (Zhipu)
glm-5.1https://api.z.ai/api/coding/paas/v4ZAI_API_KEY--provider openaiAny OpenAI-compatible endpoint
gpt-4o-minihttps://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.
Configure once, or not at all
Juggler reads ~/.config/juggler/config.toml or .juggler.toml in the current directory. CLI arguments override environment variables, which override file values.
provider = "glm"
model = "glm-5.1"
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.0.0
One universal binary per platform. No package managers, no build-from-source.
Better together
Juggler drives the model. Devscriptor gives it a map of your codebase. Install both and the agent starts with 74 code-intelligence tools already on the table.