Recently, kimi-code migrated from Python to TypeScript. Here’s a quick analysis.
Based on my review of the kimi-code source code (particularly packages/kosong/src/providers/kimi.ts, kimi-schema.ts, kimi-files.ts, etc.) and relevant OpenCode compatibility issues, here are the kimi-k2.6-specific optimizations in kimi-code and how they differ from OpenCode.
1. Native Kimi Provider (Not a Generic OpenAI-compatible Layer)
kimi-code does not treat Kimi as “just another OpenAI-compatible endpoint.” Instead, it implements a dedicated kimi provider type:
| Feature | kimi-code | OpenCode |
|---|---|---|
| Provider Type | Dedicated 'kimi' type with independent adapter | Accessed via generic OpenAI/Anthropic bridge |
| Proprietary Fields | Native handling of reasoning_content, thinking, generationKwargs | reasoning_content often lost in the bridge layer |
| Auth Headers | Supports kimiRequestHeaders, X-Msh-Tool-Call-Id, and other Moonshot-specific headers | Generic header forwarding |
2. Full Lifecycle Handling of reasoning_content
kimi-k2.6 has thinking enabled by default and requires reasoning_content to be preserved across multi-turn conversation history. Otherwise, tool calls will result in a 400 error.
How kimi-code handles it:
convertMessage: Extracts internalthinkcontent parts and serializes them into thereasoning_contentfield, ensuring thinking content is never lost in message history- Streaming Parser: Explicitly extracts
delta.reasoning_content/message.reasoning_contentin both_convertStreamResponseand_convertNonStreamResponse - TUI Rendering: A dedicated
ThinkingComponentrenders thinking content in real time, with expand/collapse support and a spinner animation
OpenCode’s Problem:
The OpenCode Go bridge drops reasoning_content on the second turn, causing the Moonshot API to return:
| |
3. JSON Schema Normalization (kimi-schema.ts)
Moonshot’s tool parameter validator has strict and unique requirements for JSON Schema. This is one of the primary sources of incompatibility between OpenCode and kimi-k2.6.
What kimi-code’s normalizeKimiToolSchema does:
- Dereferences
$ref: Inlines definitions from$defs/definitions, eliminating external references - Fills in missing
type: The Kimi validator rejects nested property schemas that omittype(e.g., MCP-generated enum-only schemas). kimi-code infers and backfillstype: string/object/array, etc. - Circular reference detection: Preserves the original
$refwhen a circular reference is detected, avoiding infinite recursion
OpenCode’s Problem:
Generated schemas use #/definitions/ instead of the #/$defs/ format required by Moonshot, and lack schema type inference and backfilling for Kimi, causing complex tool calls to fail with 400.
4. Native Thinking Mode Configuration System
kimi-code has built-in support for Kimi’s thinking mode from the configuration layer all the way to the UI:
Config Parsing:
ThinkingConfigSchemasupportsmode: auto/on/offandeffort: low/medium/high/xhigh/maxModel Capability Tags:
ModelAliassupportscapabilities: ['thinking', 'always_thinking']Model Selector UI: Press
←→to toggle thinking on/off;always-onmodels cannot be turned offProvider Method:
withThinking(effort)correctly generates:1 2 3 4{ "reasoning_effort": "high", "extra_body": { "thinking": { "type": "enabled" } } }Token Budget: Automatically normalizes legacy
max_tokensto Kimi’s preferredmax_completion_tokens
OpenCode’s Problem:
When using the Anthropic bridge, it hardcodes thinking content blocks, but the Kimi API only supports text/image_url/video_url/video, resulting in:
| |
5. Native Moonshot Service Integration
kimi-code includes Moonshot-exclusive services instead of relying on generic local implementations:
MoonshotFetchURLProvider: Prioritizes Moonshot’scoding-fetchservice (with built-in page text extraction), falling back to local fetch only on failureMoonshotWebSearchProvider: Calls the Moonshot search API directly, supportingenable_page_crawlingKimiFiles: Uploads videos to the Moonshot file service, returningvideo_urlin thems://<file-id>format
6. Tool Call Layer Details
- Built-in Functions: Tool names starting with
$are recognized as Kimi builtin functions and serialized astype: 'builtin_function' - Usage Extraction: Supports Moonshot’s proprietary
choices[0].usageplacement, as well ascached_tokensand other fields - Finish Reason Mapping: Maps OpenAI-style
stop/tool_calls/lengthvalues to an internal unified enum
7. CLI Core and LLM SDK Architectural Isolation
This is an easily overlooked but important architectural difference.
The core CLI of kimi-code (apps/kimi-code) does not directly depend on any OpenAI or Anthropic TypeScript SDK. Looking at its package.json, the core dependencies are only generic libraries like TUI rendering (pi-tui), CLI parsing (commander), and syntax highlighting (cli-highlight). All LLM provider interactions are isolated within the self-developed kosong package.
While packages/kosong internally uses openai and @anthropic-ai/sdk as implementation details (since the Kimi API is OpenAI-compatible), it exposes a unified LLM abstraction interface to the outside. The CLI core only depends on kosong and has no awareness of underlying vendor SDKs.
OpenCode is different. Its packages/opencode core package directly depends on a large number of vendor SDKs:
@ai-sdk/openai@ai-sdk/anthropic@ai-sdk/google@ai-sdk/azure@openrouter/ai-sdk-provider- … (more than a dozen provider-specific packages in total)
This means OpenCode’s core code is deeply coupled with each vendor’s SDK, while kimi-code’s core CLI stays clean, with all model interactions fully isolated through a self-developed abstraction layer.
8. What Commit History Reveals About Evolution Paths
The structural code differences above are just a static snapshot. What’s more interesting is comparing the commit histories of the two projects—their dynamic evolution directions are completely different.
kimi-code: Native Design, Continuously Reducing Configuration Burden
842e699 — “Kimi For Coding” (Initial Commit)
This was the starting point of the entire project. The initial code already included:
packages/kosong/src/providers/kimi.ts: Dedicated Kimi providerpackages/kosong/src/providers/kimi-schema.ts: Dedicated JSON Schema normalizerpackages/kosong/src/providers/kimi-files.ts: Dedicated file upload service
Conclusion: kimi-code treated the Kimi API as a first-class citizen from day one, not as a later patch.
d95b013 fix(catalog): preserve reasoning fields in custom model (#70)
This commit fixed a very subtle issue. models.dev uses the interleaved field to mark reasoning support, but early code treated interleaved=true as undefined, causing models selected via /connect to silently lose their reasoning capability.
Fixes:
interleaved=trueis mapped to the defaultreasoning_contentinterleavedis added to theupdate-catalog.mjsallowlist; otherwise the offline catalog in release builds would silently drop the field again
61f7d0e fix(kosong): make openai-compatible thinking work without reasoning_key (#78)
This is the core commit for reasoning handling, showcasing kimi-code’s deep thinking on compatibility. The diff reveals a three-layer design:
Inbound Auto-Scan (response parsing)
1 2const KNOWN_REASONING_KEYS = ['reasoning_content', 'reasoning_details', 'reasoning'] as const; // Auto-scan three fields; first string value winsOutbound Default Write-Back (request serialization)
1 2const DEFAULT_OUTBOUND_REASONING_KEY = KNOWN_REASONING_KEYS[0]; // 'reasoning_content' // Defaults to writing back as reasoning_content, no user config neededAuto-Inject
reasoning_effort(historical continuity)1 2// When history contains ThinkPart but caller hasn't explicitly set reasoning_effort, // auto-inject 'medium' to prevent strict gateways like One API / DeepSeek from returning 400
Edge cases are handled meticulously: blank reasoning_key ("") is normalized to undefined; values explicitly set by the caller via withGenerationKwargs are not silently overwritten by auto-injection.
The verification goal explicitly states:
Manually verified end-to-end against the real DeepSeek API with a hand-written config.toml that does not set reasoning_key: thinking content renders, no 400, multi-turn conversations work.
OpenCode: Generic Layer Design, OpenAI-centric
eb84f46 fix(llm): split OpenAI reasoning summary blocks (#29000)
This commit demonstrates OpenCode’s completely different approach to reasoning—designed around the OpenAI Responses API:
- Maintains a state machine for
encrypted_contentanditem_reference - Folds multiple summary parts by
item_id+summary_index - When
store:false, filters out reasoning items lackingencrypted_content
This is completely different from Kimi’s reasoning_content mechanism. Kimi does not need encrypted_content or item_reference; it simply attaches a reasoning_content field to the message.
A Hard Fact
- OpenCode Issue #26331 “Bug: OpenCode Go bridge layer incompatible with kimi-k2.6 tool calls” — Status: still open
- OpenCode Issue #27054 “KIMI K2.6 showing error in Opencode GO” — Status: closed, but the resolution was to disable MCP (a workaround)
The last comment on #27054:
The workaround is to disable your MCP and then initiate the session
That’s not a fix. That’s avoiding the problem.
Commit History Comparison Summary
| Dimension | kimi-code | OpenCode |
|---|---|---|
| Initial Design | Initial commit includes full Kimi provider + schema normalizer + file service | Generic multi-model architecture, adapted later via bridge |
| Reasoning Mechanism | Designed around reasoning_content field, with auto-scan / write-back / effort injection | Designed around OpenAI Responses’ encrypted_content + item_reference |
| Schema Handling | Dedicated normalizeKimiToolSchema, dereferences $ref + backfills type | Generic schema validation, focused on friendly error messages |
| Config Philosophy | Makes OpenAI-compatible gateways “zero-config” by auto-inferring all fields | Relies on users manually adapting via bridge/config |
| Issue Status | Continuously shipping reasoning-related patches (#70, #78) | kimi-k2.6 compatibility issue #26331 still open |
Summary: Core Differences
| Dimension | kimi-code | OpenCode |
|---|---|---|
| Architecture Positioning | Native design for Kimi/Moonshot, dedicated provider | Generic multi-model agent, adapted via bridge |
| Thinking/Reasoning | Native support, full lifecycle preservation of reasoning_content | Easily lost in bridge layer, causing 400 errors |
| JSON Schema | Dedicated normalizeKimiToolSchema for dereferencing and type backfilling | Generic schema generation, does not meet Kimi validator requirements |
| API Format | Directly generates Moonshot-native format (including thinking config, $defs normalization, etc.) | Transformed through OpenAI/Anthropic protocol conversion, causing format mismatches |
| Service Integration | Built-in Moonshot fetch/search/file services | Uses generic local tools |
| Core Dependencies | CLI core does not directly depend on vendor SDKs; isolated via self-developed kosong package | Core package directly coupled with @ai-sdk/openai and more than a dozen other vendor SDKs |
Looking at commit history, kimi-code’s evolution is directed at continuously eliminating user configuration burden (reasoning_key went from required → optional override → auto-inferred; interleaved went from filtered → correctly mapped), while OpenCode’s evolution is directed at deepening OpenAI ecosystem integration (Responses API, encrypted reasoning, item reference), leaving Kimi adaptation stuck at the generic bridge layer.
That’s the truth at the commit level: one is native evolution, the other is a bridge gap.
