P1.5.2 Kimi K2 Adapter — Verification

Round: R92, Phase 1.5, Wave 3 (parallel slice 1/3) Base SHA: 89adef66 Branch: feature/p1-5-2-kimi-adapter Implementation commit: 8e6a0b5d

1. Gate run

> npm run build
> tsc
> postbuild: copy-migrations: copied 9 migration(s) → dist/db/migrations
✓ tsc strict compile clean.

> npm run lint
> eslint src
✓ zero warnings, zero errors.

> npm test (full suite)
Test Suites: 1 failed, 71 passed, 72 total
Tests:       1 failed, 3171 passed, 3172 total
Time:        ~46–67s (varies with CPU contention)

Test count delta: 3153 baseline (per task prompt) → 3172 total (+19 net new). All 19 are in src/__tests__/domains/router/adapters/kimi.test.ts.

Single failure: consensus/parity-harness.test.ts G7.1 performance budget (Expected: < 5000 ms; Received: ~5800–5935 ms). Pre-existing flake, documented in CLAUDE.md §5 and the task prompt. Retry-clean evidence:

> npm test -- --testPathPattern=parity-harness
Test Suites: 2 passed, 2 total
Tests:       100 passed, 100 total

Isolated run is green. The flake is a CPU-contention artifact under full-suite Jest load — unrelated to this slice. The Kimi adapter only adds 19 pure-function tests with mocked fetch, delayFn, and logger; it adds zero CPU pressure to the parity harness.

2. Kimi-only run

> npm test -- --testPathPattern=adapters/kimi
Test Suites: 1 passed, 1 total
Tests:       19 passed, 19 total
Time:        14.757 s

All 19 Kimi tests green.

3. Test inventory

# Test Status
1 happy path: returns CompletionResult with correct fields PASS
2 token accounting: promptTokens + completionTokens mirror Claude shape PASS
3 401 from Kimi → KimiApiError with status 401 PASS
4 missing COLIBRI_KIMI_API_KEY → KimiConfigError PASS
5 tool-use response → Anthropic-shape JSON-stringified content PASS
6 fetchFn override is invoked PASS
7 latency measurement: 50ms delay → latencyMs >= 50 PASS
8 sends POST to <baseUrl>/chat/completions PASS
9 Authorization: Bearer header includes API key (no x-api-key) PASS
10 tools array maps AnthropicTool to Kimi function shape PASS
11 429 → exponential backoff up to MAX_RETRIES → KIMI_RETRIES_EXHAUSTED PASS
12 system prompt prepended as messages[0] with role=system PASS
13 empty tools array does not send tools key PASS
14 tool args JSON.parse failure → _parse_error envelope (no throw) PASS
15 finish_reason ‘tool_calls’ maps to ‘tool_use’ PASS
16 honors COLIBRI_KIMI_BASE_URL override via options.baseUrl PASS
17 createKimiCompletionWithTools also throws KimiConfigError on missing key PASS
18 KimiConfigError carries code KIMI_CONFIG_ERROR PASS
19 logger receives [kimi] log line with token + latency fields PASS

15 numbered tests from the packet + 4 ancillary parity tests = 19 total. Well above the 5–10 floor specified in the task prompt.

Network error wrap (separate test) verified — fetch throw → KimiApiError with status: undefined.

4. Tool-use mapping evidence

Request side (Anthropic → Kimi)

// Input
AnthropicTool {
  name: 'get_weather',
  description: 'Get the current weather',
  input_schema: { type: 'object', properties: { location: { type: 'string' } }, required: ['location'] }
}

// Sent in request body (verified via Test #10)
{
  type: 'function',
  function: {
    name: 'get_weather',
    description: 'Get the current weather',
    parameters: { type: 'object', properties: { location: { type: 'string' } }, required: ['location'] }
  }
}

Response side (Kimi → Anthropic-shape content)

Kimi tool_call:

{ "id": "call_001", "type": "function", "function": { "name": "get_weather", "arguments": "{\"location\":\"London\"}" } }

Emitted in CompletionResult.content (verified via Test #5):

[ { "type": "tool_use", "id": "call_001", "name": "get_weather", "input": { "location": "London" } } ]

This is byte-identical in SHAPE to what createCompletionWithTools from src/domains/integrations/claude.ts returns when the Claude API responds with a tool_use block.

finish_reason mapping (verified via Test #15)

Kimi finish_reason Anthropic stop_reason
stop end_turn (Test #1)
tool_calls tool_use (Test #15)
length max_tokens
content_filter refusal
(other) pass-through

Tool args parse-fail tolerance (verified via Test #14)

Malformed arguments: "this-is-not-json{"input: { _parse_error: <msg>, _raw: 'this-is-not-json{' }. No throw. Test #14 PASS.

5. Surface parity table

CompletionResult field Claude adapter Kimi adapter Parity
content string (text or JSON-stringified blocks) string (text or JSON-stringified blocks)
model string from json.model string from json.model
promptTokens number from usage.input_tokens number from usage.prompt_tokens ✓ shape
completionTokens number from usage.output_tokens number from usage.completion_tokens ✓ shape
latencyMs Date.now() - startMs Date.now() - startMs
stopReason string from json.stop_reason string from mapped finish_reason ✓ shape

All 6 fields present, all 6 typed identically. instanceof CompletionResult works against both adapters (it’s a structural interface).

Error class Claude adapter Kimi adapter Parity
Config error AnthropicConfigError, code: ANTHROPIC_CONFIG_ERROR KimiConfigError, code: KIMI_CONFIG_ERROR ✓ shape
API error AnthropicApiError, code: ANTHROPIC_API_ERROR \| ANTHROPIC_RETRIES_EXHAUSTED, status: number \| undefined KimiApiError, code: KIMI_API_ERROR \| KIMI_RETRIES_EXHAUSTED, status: number \| undefined ✓ shape
Injection seam Claude Kimi Parity
fetchFn
logger
delayFn
apiKey (option override)
Base URL override — (hardcoded) baseUrl option + env ✓+
Retry policy Claude Kimi
Retryable: 429 + 5xx
MAX_RETRIES 3 3
BASE_DELAY_MS 100 100
Backoff geometric ×2 geometric ×2
Network errors not retried not retried

6. Acceptance criteria coverage

Criterion Evidence
createKimiCompletion(prompt, options) → Promise<CompletionResult> Test #1
createKimiCompletionWithTools(prompt, tools, options) Tests #5, #10, #13
Reads COLIBRI_KIMI_API_KEY (missing → KimiConfigError) Tests #4, #17, #18
Reads COLIBRI_KIMI_BASE_URL (default Kimi endpoint) Tests #8, #16
Tool-use response shape: Anthropic-SDK compatible Tests #5, #15
Injection seams: fetchFn, logger, delayFn Tests #6, #7, #19
KimiApiError shape parity with AnthropicApiError Tests #3, #11, plus network-error test
5–10 parity tests 19 tests landed
No MCP tool registration No server.ts import / register call; grep clean
Build + lint + test green §1

7. Untouched files

$ git diff origin/main -- src/domains/router/index.ts
(empty)
$ git diff origin/main -- src/domains/integrations/claude.ts
(empty)
$ git diff origin/main -- src/config.ts
(empty)
$ git diff origin/main -- src/domains/router/fallback.ts
(empty)
$ git diff origin/main -- src/domains/router/scoring.ts
(empty)

src/domains/router/index.ts is UNTOUCHED, per the sibling-race scope override. Re-export coordination is deferred to a fold-in commit between Wave 3 and Wave 4.

8. Diff summary

docs/audits/p1-5-2-kimi-adapter-audit.md         +227 lines
docs/contracts/p1-5-2-kimi-adapter-contract.md   +217 lines
docs/packets/p1-5-2-kimi-adapter-packet.md       +188 lines
src/domains/router/adapters/kimi.ts              +475 lines
src/__tests__/domains/router/adapters/kimi.test.ts +682 lines
docs/verification/p1-5-2-kimi-adapter-verification.md (this file)

5 commits on feature/p1-5-2-kimi-adapter per the 5-step chain.

9. Risk register — verified

Risk Mitigation Status
Test fixtures drift from Claude pattern. Mirrored function-by-function (makeMockFetch, makeSilentLogger, makeInstantDelay, baseOptions).
Tool-args parse-fail crashes tests. _parse_error envelope per Test #14.
Module-load throw on missing env. process.env[...] reads are call-time only. ✓ (verified by build pass without env)
src/domains/router/index.ts accidentally modified. git diff empty.
Logger writes to stdout. Default console.error; tests assert via injected logger.
fetch global not available. Node 20+ baseline; tests inject mock.
Retry exhaustion uses wrong literal. 'KIMI_RETRIES_EXHAUSTED' per Test #11.
Tool descriptor mapping field-rename mistake. parameters: t.input_schema only; Test #10 verifies.
Authorization header malformed. 'Bearer ' + key; Test #9 verifies.

10. Forbiddens — verified

  • No edits to main checkout. (Worked from .worktrees/claude/p1-5-2-kimi-adapter.)
  • No commits on main.
  • No force-push, --no-verify, --amend.
  • No mutation of src/domains/router/index.ts.
  • No mutation of any file outside src/domains/router/adapters/kimi.ts + src/__tests__/domains/router/adapters/kimi.test.ts + 5 chain docs.
  • No AMS_* env vars — only COLIBRI_KIMI_API_KEY and COLIBRI_KIMI_BASE_URL.
  • No MCP tool registration (no server.ts import).
  • No hardcoded model version — DEFAULT_MODEL = 'kimi-k2-0905-preview' is the documented default; options.model overrides.
  • No fallback chain logic in the adapter.

11. Exit criteria

  • Gates: build PASS, lint PASS, test PASS (3171/3172 — 1 pre-existing flake retry-clean).
  • 19 parity tests pass.
  • Tool-use mapping documented + tested.
  • Surface parity table complete.
  • index.ts untouched.

Slice complete. Ready for PR review.


Back to top

Colibri — documentation-first MCP runtime. Apache 2.0 + Commons Clause.

This site uses Just the Docs, a documentation theme for Jekyll.