Add Kimi as fourth swarm model family - #59
Open
gering wants to merge 16 commits into
Open
Conversation
1 task
- transport full review prompts over ACP stdio instead of argv - validate Kimi findings locally and reject approval-gated tools - add model-aware readiness, jail isolation, telemetry, and tests Co-Authored-By: Claude <noreply@anthropic.com>
- count Kimi as the independent Moonshot model family - include Kimi in cluster and per-lens external voice topology - keep backend registries, status skill, and jail posture synchronized Co-Authored-By: Claude <noreply@anthropic.com>
- Append and validate Kimi's schema output contract - Harden ACP policy, readiness, telemetry, and status reporting - Document the fourth model family and bump swarm to 0.11.0 Co-Authored-By: Claude <noreply@anthropic.com>
Make repository and Git paths OS-write-denied for every external voice, run Kimi from an ephemeral HOME that holds only a credentials copy, and decode ACP as UTF-8. Recalculate the run-path probe budget after the 0.10.7 rebase. Co-Authored-By: Claude Code <noreply@anthropic.com>
The first four-family self-review lost Moonshot on every cluster (rc=10) and grok at `high` blew the 540 s wall on a ~190 KiB cluster prompt. - Deny the ambient ~/.kimi-code store entry by entry, sparing bin/ (and the resolved $KIMI_BIN directory): the stock installer keeps the executable at ~/.kimi-code/bin/kimi, so the whole-directory deny blocked the exec of every jailed run while the unjailed readiness probes kept reporting Kimi live. Verified with the real sandbox-exec profile; regression-tested against a fake store for every backend. - Project a filtered config.toml into the isolated HOME (default_model, providers, models, services, thinking — never hooks, mcp, permission mode): kimi-code 0.32 offers a kimi-code/* model over ACP only when it is declared under [models], so the credentials-only HOME failed session/new with "does not offer model value". Live ACP smoke: rc=0. - Run grok at `medium` on the normal profile; `high` is --max only. - Test harness: subshells that own run_kimi's exit now own the EXIT trap too, so the ephemeral kimi HOME (a credential copy) no longer leaks into TMPDIR on every test run. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01V7xY8Y8DVgrmcyRYfnWRSu
… HOME Fifteen findings from the first four-family self-review, plus the regression the fix round itself exposed live: - Protected roots: memoized, bash-realpathed, sorted ancestors-first, and covering every linked worktree. A descendant bound before its ancestor made bwrap abort on --remount-ro (smoke probe: "jail-less") on every Linux linked-worktree review; sibling checkouts stayed writable. - Kimi ACP tool gate is an allowlist (read/search/fetch/think): unsafe kinds are sticky, any run — in progress, completed, or failed without a rejected approval — kills the session on first sight, an unsafe tool left pending fails as unsettled. The signal handler SIGKILLs the kimi process group outright; the adapter passes --kimi-bin explicitly. - The OS jail is part of Kimi's ready_check (hint names it); no skill re-derives it in prose. Repo-local .kimi-code/.kimi/.mcp.json denied. - Assembled prompt and ephemeral Kimi HOME live next to the caller's prompt file via one shared scratch helper (SIGKILL-safe cleanup). - One _backend_bin, one parametrized _probe_degraded, dead guard removed. - Link the host's credentials/ and oauth/ dirs into the ephemeral HOME instead of copying the token: Moonshot rotates refresh tokens, and a refresh inside the private copy invalidated the host's token and logged the operator out (kimi-code 0.32.0, 2026-09-04). Readiness now requires a non-empty token — kimi-code blanks the file in place after a failed refresh, and `-s` alone advertised a dead voice. - Docs: repository-write boundary, residuals named. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01V7xY8Y8DVgrmcyRYfnWRSu
gering
force-pushed
the
task/add-kimi-swarm-voice
branch
from
September 5, 2026 09:44
50446c6 to
075ab5e
Compare
…tool gate
22 findings from the rebased three-family review. The critical one was
the previous round's own scratch helper: it unlinked run_kimi's INPUT
(the lens-assembled prompt) before the cat, so Kimi received the output
contract alone and answered `{"findings":[]}` with rc 0.
- Drop the previous scratch AFTER the write, fail the assembly on cat,
regression-test the chained subcmd_run → run_kimi case.
- Project only the managed (`managed:*`) provider, its models, services
and thinking; a third-party api_key never reaches Kimi's runtime config.
- ACP client: --deny-path prefixes abort any tool call whose locations
resolve under the ephemeral HOME or the host store; a missing tool kind
stays missing (updates can only escalate); close(force) is a plain
conditional.
- Kimi deny list masks the repo's AGENTS.md/agents.md/KIMI.md (loaded as
system-prompt instructions from cwd); the $KIMI_BIN carve-out spares any
ancestor of the binary's dir; siblings get the whole store, no walk.
- No $TMPDIR fallback for scratch/HOME placement; absolutize prompt paths
once in subcmd_run; memoize the credential check; _line_in_list reuse;
header/date/declaration tidy-ups; kimi-acp.py executable.
- pr-post names unknown backends instead of dropping them; its label map
is covered by test_backend_sync.
- Docs: links not copies everywhere; grok medium→high in the pipeline
knowledge; sandboxed() comment states that Kimi readiness builds the jail.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01V7xY8Y8DVgrmcyRYfnWRSu
The model is the adapter's on both profiles now; only the effort is a profile knob (medium normally, xhigh under --max). Verified live through the adapter (telemetry: gpt-5.6-sol, medium, rc 0). Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01V7xY8Y8DVgrmcyRYfnWRSu
The profile matrix settled on haiku@medium for the gate on every profile: since 0.7.0 a lens the gate prunes is reviewed by nobody. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01V7xY8Y8DVgrmcyRYfnWRSu
…annels 17 findings (three families; grok's breakage cluster hit the wall). - Deny the repository's .agents/ to Kimi: kimi-code discovers skills, subagents and a whole-prompt agent override there, outside the diff fence. - ACP --deny-path scans every string in a tool call's rawInput and title, not only the optional `locations` (that alone was fail-open); both output scrubbers redact JWT-shaped tokens. - Config projection: tomllib parse + re-serialize of the allowlisted tables with every secret-shaped scalar dropped (single-quoted TOML and header-looking lines inside strings no longer fool it); hardened line-based fallback below python 3.11. - _kimi_has_acp detects ACP positively (commander-style CLIs print top-level help with rc 0 for unknown subcommands); rc 1 is a clean no. - KIMI_CREDENTIALS_FILE must be named kimi-code.json (the directory is linked, kimi reads the file by name); test fixtures follow. - The ephemeral HOME refuses to sit under a write-denied root; the prompt decodes with replacement characters instead of failing as a config error; already-effective ACP options are not re-set; the protected-roots memo is primed in the main shell so process substitutions reuse it. - Docs: header says links not copies, model id referenced not re-typed, knowledge updatedAt. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01V7xY8Y8DVgrmcyRYfnWRSu
The secret strip also dropped `[providers.*.oauth] key`, the reference to the credential-store entry, and the stock `api_key = ""` — kimi-code then reported "no provider configured" at session start (rc 10). A secret-shaped key is dropped only when it carries a value. Live ACP smoke: rc 0. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01V7xY8Y8DVgrmcyRYfnWRSu
…mi's diagnosis 18 findings from the first run with Kimi live (three Kimi answers rejected by the gate with no visible cause; the fourth call hit Moonshot's 5-hour quota, which nothing surfaced). - ACP deny check resolves relative tool paths and repo symlinks against the session cwd (commonpath), passes the peer's JSON-RPC message up, extracts the JSON object from fenced/prose-wrapped answers before strict schema validation, and caps the buffered answer at 8 MiB. - The adapter keeps the client's stderr and appends its last lines (scrubbed) to the backend error; a policy abort reports rc 13 in telemetry instead of "answered". - Host write-deny: shell rc files, ~/.claude, ~/.claude.json, ~/.config, ~/.local/bin, sibling agent stores, ~/.kimi-code/bin + config + hooks and the resolved $KIMI_BIN dir (sandbox-exec deny file-write*, bwrap ro-bind). - git worktree list --porcelain -z; readiness validates the model against the projected catalogue, refuses a TMPDIR inside the checkout, separates "python3 missing" from "not logged in", treats empty acp --help stdout as inconclusive; the lens-assembly scratch falls back to $TMPDIR audibly. - Workflow returns balance.participants for the PR footer; knowledge pluginVersion is the knowledge-system's; bwrap ordering comment fixed. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01V7xY8Y8DVgrmcyRYfnWRSu
The first four-family review lost every Kimi cluster: two aborted because
Kimi auto-ran an `execute` tool under the ACP `default` mode (kimi-code
0.41 treats some shell commands as safe) and the policy gate killed the
session on first sight; three hit Moonshot's 5-hour quota. grok at
`medium` still timed out `breakage` at the 540 s wall on a ~290 KiB
cluster prompt (threat at 99 %).
- kimi-acp.py selects Kimi's read-only `plan` session mode ("no tool
execution"): shell/edit tools are not offered to the model at all, so a
review cannot lose its voice to the gate. A Kimi without `plan` fails
closed. Verified live through the jail: file reads work, `ls` reports
"no shell in this session", rc 0.
- The appended output contract tells Kimi the session is read-only;
SWARM_CAP_HEADROOM grows to 8192 so the threshold still covers lens
instruction + contract (test_lens_sync pins it).
- Profiles: grok `low` (normal) / `medium` (--max); Kimi `low` / `high`.
Kimi's k3 thinking ladder is low|high|max, so `medium` maps to `low`.
test_backend_sync pins the exact ternary arms for both backends.
- De-flake the ACP "unsafe-in-progress" fake: pause after the tool_call
frame so the client's kill lands before prompt_done can be logged
(failed 1 in ~8 runs; 8/8 after).
- Date the 0.11.0 entry 2026-09-05 (its bullets landed that day).
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01V7xY8Y8DVgrmcyRYfnWRSu
`plan` mode removed Kimi's shell wholesale — and with it `git log/show/ blame` and grep pipelines, which Kimi has no native tool for. Replace it with a read-only command policy in the ACP client, back in `default` mode: - `_read_only_command`: positive allowlist of programs (git read subcommands only — branch/tag/stash/remote/config/worktree need a listing flag; grep/rg/find/ls/cat/head/tail/wc/sort/… pipelines), no chaining, redirection, substitution, `git -c`/`--output`, `find -exec`, `rg --pre`, `tail -f`; awk/sed/xargs/shells/interpreters off. - The gate vets an `execute` as soon as its command is readable and aborts only if a disallowed one reached execution; one still being composed is left to the permission handler, which approves an allowlisted command once and rejects everything else. Shell tokens go through the deny-path check, and an ANCESTOR of a deny path (`/`, `$HOME`, the scratch parent) is now denied like the store. - kimi-code 0.41 streams the argument JSON as cumulative text snapshots and asks for permission BEFORE the rawInput frame, so the client parses the snapshot buffer; `in_progress` no longer means "ran" for execute (edit/delete still abort on sight). Traced via the new `KIMI_ACP_TRACE=<file>` diagnostic; the generic abort now names the rawInput keys and title. - Verified live through the jail: `git log --oneline -3` and `git blame` approved and run, rc 0, correct head reported. 38 ACP tests. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01V7xY8Y8DVgrmcyRYfnWRSu
Writes are denied everywhere first, then re-allowed only for the scratch dir, the per-user temp/cache dirs, /dev and the backend's own auth state, with the repository/Git roots and host config surfaces denied again on top (sandbox-exec last-match-wins; bwrap read-only root + writable binds). With that boundary a shell for git log/show/blame and grep pipelines is affordable for codex, grok and Kimi alike. - grok: run_terminal_command joins --tools, headless --permission-mode dontAsk plus --deny prefix rules (egress/destructive verbs; grok pre-approves every listed tool whatever the mode, deny rules are honoured), and an ephemeral HOME/GROK_HOME with neutral Claude settings and only auth.json linked. grok 1.0 otherwise loads the operator's Claude settings (rules AND hooks), plugins/MCP servers and the repo's CLAUDE.md/rules as instructions; a merely denied settings file made its permission engine ask, which dontAsk answers by cancelling the turn — grok had never used a tool under the jail. ~/.grok denied to grok entry by entry, ~/.claude wholesale. - codex: -s danger-full-access -a never --ignore-user-config --ignore-rules under the jail. A nested sandbox-exec fails against any outer deny rule (sandbox_apply: Operation not permitted), which had silently killed every codex shell command — and its file reads — since 0.6.0. -s read-only stays the jail-less posture. - _host_write_deny_paths emits missing rc files and codex's config surfaces; readiness of every backend refuses a TMPDIR inside the checkout; SWARM_GROK_TRACE diagnostics. - Verified live through the jail: grok and Kimi run git log/blame with the repo write probe denied; codex verified for the write denial only (the shell path needs the user's go — see the session notes). Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01V7xY8Y8DVgrmcyRYfnWRSu
…flags Round 4 (22 findings; the first with grok's shell live and a claude+moonshot consensus). Applied 21, declined the double config projection (~100 ms, deliberate). - Policy: reject any long option whose name says it takes a program (--open-files-in-pager, -O, --compress-program, --hostname-bin, --pre*, --pager), any --x=value naming a network client, shell or interpreter, and for branch/tag/remote/config/stash/worktree require every flag to be a listing flag with no mutating verb (`git branch -a -D x` had been approved). Quoted pipes parse (`grep -E 'a|b'` killed the voice). A changed rawInput under a vetted id is re-vetted. The LAST fenced JSON object wins over a decoy quoted from the diff. - codex: drop `-a never` — `codex exec` has no -a, so every codex call in round 4 failed with rc 2 in 0 s. - Jail: bwrap binds file roots (grok's auth.json) and skips a writable root that contains a repository (its recursive bind would break the final --remount-ro); `worktree list` falls back to the newline form on git < 2.36; a failed config projection is reported, not read as "no models"; the secret filter matches plural/compound keys; grok's --deny list widens and is documented as best-effort; comments/docs drop the "host HOME stays writable" and "rejects every request" wording; dead alias and unreachable hint branch removed; protected-roots memo is primed in the main shell; a sync test pins the Kimi tool brief to the policy constants. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01V7xY8Y8DVgrmcyRYfnWRSu
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
moonshotconsensus family--maxswarmto 0.11.0 and update marketplace metadata, READMEs, changelog, pipeline docs, and project knowledgeSupersedes #55 (closed when #54's branch was deleted after merge). Rebased onto
mainat swarm 0.10.14.Validation
python3 plugins/swarm/scripts/test_kimi_acp.pypython3 plugins/swarm/scripts/test_kimi_models.pypython3 plugins/swarm/scripts/test_sandbox_deny.pypython3 plugins/swarm/scripts/test_backend_sync.pypython3 plugins/swarm/scripts/test_lens_sync.pypython3 plugins/swarm/scripts/test_telemetry_report.pypython3 plugins/swarm/scripts/test_pr_post.pypython3 scripts/check-structure.py(0 errors, one pre-existing pr-flow description warning)git diff --checkBefore merge
🤖 Generated with Claude Code