Imported from P-A-N-52/kimi-code-desktop (
runtime/kimi-code/packages/klient/AGENTS.md). Install upstream withnpx skills add P-A-N-52/kimi-code-desktop --skill klient. Copyright stays with the author.
klient Agent Guide
Package-local rules for packages/klient.
Architecture
The package is layered; keep the layers strict when changing code:
- Facade (
src/core/facade/,src/core/klient.ts) — the only public API: aggregatedglobal.*/session(id).*/session(id).agent(id).*methods and theirevents.*hubs. No engine service tokens, noonDid*/onWill*names, and no escape hatch to raw services — do not reintroduce a service locator (core()/service()/makeProxy). - Contract (
src/contract/) — zod input/output schemas for every wire method plus event payload schemas. Schemas are hand-mirrored from agent-core-v2 types and pinned by the compile-time parity assertions intest/contract-parity.ts; when the engine types change, tsc fails here first.maybe()/noResult()insrc/contract/helpers.tsencode the HTTP wire'snull-vs-undefinedsemantics — use them for everyX | undefined/voidresult. - Transports (
src/transports/{ipc,memory}) — each implements theKlientChannelSPI (src/core/channel.ts) and nothing else. ipc frames the same dispatcher traffic as NDJSON over a unix socket and shares the in-process dispatcher with memory; memory JSON round-trips every value so both transports return byte-identical data.
The facade only covers services that behave identically on both transports
(the in-process dispatcher mirrors the server's scope resolution, including
main-agent materialization via ensureMainAgent). onWill/hook-style
interception is not wire-exposable
(engine hooks are in-process OrderedHookSlots); file upload and the
terminal surface are v1-only and live in the legacy suites.
Testing
- One shared conformance suite (
test/helpers/conformance.ts) runs unchanged against every transport — one test file per transport undertest/. Add new global facade coverage there, not per-transport. test/e2e/legacy/+test/e2e/harness/— the legacy/api/v1live suites (moved from server-e2e). They skip unlessKIMI_SERVER_URLpoints at a running server and must keep running unchanged; the v1 surface has no in-memory equivalent, so these stay live-server-only — do not try to run them against the in-process transports.- The retired
scenarios/scripts were rewritten as suites: image-upload and terminal (v1-only surfaces) live intest/e2e/legacy/.
Observability (inherited from server-e2e)
- Keep observability inside each e2e case; every live case prints structured,
case-scoped details (requests, envelopes, WS handshakes, terminal frames,
error envelopes) through the shared logger in
test/e2e/legacy/log.ts, not ad hocconsole.log. - Logs must stay visible for passing Vitest cases — write through stdout.
- When adding or changing an e2e case, update its observability at the same time; do not add a scenario solely to print data an existing case should already expose.
Command reference
pnpm --filter @moonshot-ai/klient test— all Vitest suites (unit + conformance + e2e; live cases skip without their env).KIMI_SERVER_URL=http://127.0.0.1:58627 pnpm --filter @moonshot-ai/klient test— include the live legacy cases against a running server.pnpm --filter @moonshot-ai/klient docker:e2e— docker e2e; the run derives its runner name/namespace from the current workspace to avoid cross-workspace conflicts.pnpm --filter @moonshot-ai/klient typecheck/pnpm smoke(in-process smoke over the memory transport; seeexamples/smoke.ts).pnpm --filter @moonshot-ai/klient smoke:boundary— ModelRequester boundary probe: pings every model configured in the real~/.kimi-code/config.tomlthrough the in-process engine, then drives deterministic failure modes against a local stub to show which errors the ChatProvider layer wraps and which the requester owns (seeexamples/model-requester-boundary.ts).pnpm --filter @moonshot-ai/klient smoke:select-tools— select_tools (progressive tool disclosure) probe for kimi-type providers: stub-verifies the kimi-only wire encoding of dynamic tool declarations, then runs a live two-step select→use flow per real kimi model (seeexamples/kimi-select-tools.ts).