# kimi-wire Agent Guide
This file contains agent-level conventions for the `kimi-wire` crate — a
standalone Rust library for the Kimi Code CLI Wire Protocol.
## Contents
- [Meta Principle](#meta-principle)
- [Behavioral Guidelines](#behavioral-guidelines)
- [Library Contract Rules](#library-contract-rules-hard-constraints)
- [Rust Safety Rules](#rust-safety-rules-hard-constraints)
- [Error Handling Doctrine](#error-handling-doctrine-hard-constraints)
- [Wire Protocol Compatibility](#wire-protocol-compatibility-hard-constraints)
- [Build & Test](#build--test)
- [Clippy Lint Policy](#clippy-lint-policy)
- [Editing Rules](#editing-rules)
## Meta Principle
Before applying any rule or refactor, ask: **what problem does this solve?**
A newtype, a refactor, or an abstraction is justified only if it prevents a concrete bug,
clarifies an invariant, or removes a footgun. If the answer is "it looks better" — revert.
Decoration is not engineering.
## Behavioral Guidelines
- **State assumptions explicitly.** If uncertain, ask before implementing.
- **Minimum code.** No speculative abstractions. No features beyond the request.
If you write 200 lines and it could be 50, rewrite.
- **Surgical changes only.** Touch only what you must. Match existing style.
Remove imports/variables made unused by *your* changes; don't delete pre-existing
dead code unless asked.
- **Goal-driven execution.** Every task needs verifiable success criteria and a brief plan
(`Step → verify: check`).
- **Prefer `?` over `unwrap`/`expect` even in tests** where it keeps the test readable.
## Library Contract Rules (Hard Constraints)
These rules protect the crate's most fragile contracts: the public API surface,
serde correctness, and Wire protocol compatibility.
1. **Public API is opt-in.** Prefer `pub(crate)`. New `pub` items require a concrete
external caller or a paved-path rationale, plus a test.
2. **Protocol facts must not go stale.** If event names, request names, or method
parameters change, update `README.md`, `CHANGELOG.md`, and tests in the same change set.
3. **Serde roundtrip is a hard contract.** Any change to `Event`, `Request`, `ContentPart`,
`DisplayBlock`, or method params/result types must include a serde roundtrip test.
The envelope format (`{"type":"...","payload":{...}}`) must be preserved.
4. **Dependencies are architecture changes.** No new crate without a rationale:
why std/local code is not enough, transitive impact, MSRV, license, and feature-flag
consequences. Small helper crates are rejected by default.
5. **Refactors isolate mechanics from behavior.** File moves, splits, renames, and
formatting-only changes must be separate from semantic changes whenever practical.
6. **MSRV is 1.80.** Do not use language or std features introduced after Rust 1.80.
## Rust Safety Rules (Hard Constraints)
These rules apply to **new or modified production code** under `src/` (outside
`#[cfg(test)]`). Violations in touched code must be fixed before merge.
1. **`unwrap()` is banned.** Use `?`, `if let`, `match`, `ok_or`, `bail!`, or `.context()`.
2. **`expect()` is banned.** No "this should never happen" — it always happens eventually.
3. **`panic!()` is banned.** Graceful degradation only; propagate errors via `Result`.
4. **`std::thread::sleep` is banned in `async fn`.** Use `tokio::time::sleep(...).await`.
5. **`std::sync::Mutex` is banned in `async fn`.** Use `tokio::sync::Mutex` to avoid blocking the executor.
6. **All external `Command::output().await` must have a `tokio::time::timeout`.** Prevent infinite hangs from rogue child processes.
7. **All `spawn()` calls must set `kill_on_drop(true)` or attach to a `CancellationToken`.** Prevent zombie processes.
### Preconditions & Invariants
Prefer expressing preconditions in types before comments or runtime checks:
1. Use a specific type/newtype/parser constructor that makes invalid states unrepresentable.
2. Keep fields private when they carry invariants.
3. If the invariant cannot be encoded in the type, document it and add a `debug_assert!` next to the use.
Example:
```rust
/// Average of a non-empty slice.
/// Precondition: `!items.is_empty()`
pub fn average(items: &[f64]) -> f64 {
debug_assert!(!items.is_empty(), "precondition: non-empty slice");
items.iter().sum::<f64>() / items.len() as f64
}
```
`debug_assert!` is a last-line development check, not a substitute for type design.
### Tests (`#[cfg(test)]`)
`unwrap()`/`expect()` are allowed for brevity, but prefer `?` where it keeps the test readable.
## Error Handling Doctrine (Hard Constraints)
### Meta Principle
**Every error has an owner, a representation, and a consumer. Never swallow an error.**
### 1. Typed Errors Only
- **Library code uses `thiserror`.** Every public function that can fail returns a specific
`WireError` enum variant. Callers must be able to `match` on variants.
- **`anyhow` is banned from the public API.** If a function is `pub` and returns `anyhow::Result`,
it must be refactored to a typed `thiserror` enum.
- **`thiserror` messages must be lowercase without trailing punctuation.**
- Good: `#[error("connection refused")]`
- Bad: `#[error("Connection refused.")]`
### 2. Error Context & Chaining
- **Preserve the cause chain.** `#[source]` or `#[from]` must be used so that the full
chain is available to callers.
- **Add context at boundaries.** Wire I/O and JSON parse boundaries should include
enough detail to be actionable (e.g. the raw method name or request id).
### 3. Silent Errors Are Banned
- **`let _ = ...` on `Result` is banned unless explicitly justified.**
- **Explicit ignore requires a comment.**
```rust
#[allow(unused_must_use)]
let _ = sender.try_send(event);
```
## Wire Protocol Compatibility (Hard Constraints)
This crate implements the Kimi Code CLI Wire Protocol v1.10 (with backward-compatible deserialization for older fields). Compatibility is not optional.
### 1. Envelope Format
- `Event` and `Request` must serialize as `{"type":"<PascalCaseName>","payload":{...}}`.
- `Event::ToolCall` envelope type is `"ToolCall"`; the payload carries an inner
`type: "function"` discriminator.
- `ContentPart` uses `type` inside `payload` (e.g. `"text"`, `"image_url"`).
### 2. Serde Coverage
- Every new or changed protocol type must have a roundtrip test:
`serialize → deserialize → assert_eq!(original, roundtripped)`.
- Tests must exercise both the envelope layer and the payload layer.
### 3. Unknown Fields
- The protocol may add new fields. Where possible, use `#[serde(default)]` and
`skip_serializing_if = "Option::is_none"` to tolerate forward-compatible additions.
- Do not use `deny_unknown_fields` on Wire protocol structs unless the spec explicitly
requires it.
### 4. Secret Redaction
- The `redact` feature scrubs secrets from JSON values before they reach logs or debug output.
- Any new field that may contain a token, key, or password must be covered by redaction tests.
## Build & Test
Local verification commands:
```bash
# Run all tests (unit + integration + doc-tests)
cargo test --all-features
# Lint and type-check
cargo clippy --all-targets --all-features
cargo check --all-targets --all-features
# Documentation (must build without warnings)
cargo doc --no-deps --all-features
```
CI runs: `cargo test --all-features`, `cargo clippy --all-targets --all-features`,
`cargo doc --no-deps --all-features`.
## Clippy Lint Policy
Enabled lints live in `src/lib.rs` via `#![warn(...)]`. They produce warnings during
compilation without breaking the build, but must be addressed before merge.
### Tier 1 — Must-fix (high value, low noise)
| `clippy::await_holding_lock` | `std::sync::Mutex` or `RefCell` held across `.await` | Prevents executor blocking and deadlocks in async code |
| `clippy::dbg_macro` | `dbg!()` left in committed code | Debugging macros must not reach production |
| `clippy::wildcard_imports` | `use module::*` outside preludes/tests | Keeps imports explicit and traceable |
| `clippy::unused_async` | `async fn` that does not `.await` anything | Removes unnecessary async overhead |
### Tier 2 — Recommended (address when touching nearby code)
| `clippy::missing_panics_doc` | `expect()` / `panic!()` without doc comment explaining the invariant |
| `clippy::cast_sign_loss` | `as u64` from signed types (e.g. `i64 as u64`) |
### Tier 3 — Already clean (maintain zero violations)
- Zero `TODO` / `FIXME` / `HACK` comments in production code
- All public types implement `Debug`
- Zero `unsafe` blocks (currently 0 in `src/`)
## Editing Rules
- When modifying code, check whether a subdirectory has its own `AGENTS.md` for more specific guidance.
- Keep deeper-directory rules as overrides to these root rules.
- Update this file if you change any convention it describes.