MoonshotAI / MoonshotAI/kimi-code
ACP adapter does not report token usage (PromptResponse.usage empty, no usage_update emitted)
Nobody has claimed this yet.
- Dominant language
- TypeScript
- Stars
- 7.5k
- Forks
- 1.2k
- Avg merge
- 11h 53m
- Merged PRs (30d)
- 350
Description
Summary
Kimi Code's ACP adapter does not report token usage to ACP clients. The PromptResponse.usage field is left empty and no usage_update events are emitted on turn.ended. This causes downstream ACP clients (e.g., Multica, OpenCode, Claude Code running kimi as backend) to have zero visibility into token consumption and cost.
Impact
-
Breaking for external orchestration platforms. Multica's
runtime usagecommand shows zero rows for Kimi runtime, while Claude/OpenAI runtimes on the same daemon return full per-model breakdowns. Cost calibration autopilots that read usage data permanently skip Kimi agents. -
The data exists internally. Kimi Code's internal SDK tracks usage via
session.getUsage()and serializes it towire.jsonl(inputOther/output/inputCacheRead/inputCacheCreation). It just never crosses the ACP boundary. -
Old Python kimi-cli had the same bug. kimi-cli#2394 reported the identical issue in the legacy Python codebase. This is a known gap.
Root Cause
In packages/acp-adapter/src/session.ts, the turn.ended handler resolves with only stopReason:
// Current (v0.26.0):
resolve({ stopReason: ... })
// No PromptResponse.usage filled
// No usage_update event emitted
The ACP protocol supports two paths for usage reporting:
PromptResponse.usage— maps directly toTurnResponseand is consumed by Multica's kimi adapter (kimi.go L305–L385).session/updatewithusage_update— parsed by the shared ACP client with camelCase/snake_case compatibility (hermes.go L988–L1058).
Either path, if filled, would be picked up by existing downstream code immediately—no changes needed in Multica or other ACP consumers.
Proposed Fix
In the turn.ended handler (or a new prompt.ended handler), call session.getUsage() and populate PromptResponse.usage:
const usage = session.getUsage()
resolve({
stopReason: ...,
usage: {
inputTokens: usage.inputTokens,
outputTokens: usage.outputTokens,
// cache fields if tracked:
// cacheReadInputTokens: usage.inputCacheRead,
// cacheCreationInputTokens: usage.inputCacheCreation,
}
})
Alternatively (or additionally), emit a usage_update via session/update notification.
Steps to Reproduce
- Run kimi-code v0.26.0 in ACP mode (
kimi acp) - Complete any turn (simple prompt or tool-calling turn, doesn't matter)
- Observe the
promptresponse —usagefield is absent or empty - Compare with Claude Code / OpenCode in ACP mode — both fill
usageon every turn
Environment
- kimi-code: 0.26.0 (Nix-installed on macOS ARM64, Node.js v24.18.0)
- ACP protocol version: 1.4
- Model: k3 (also reproducible with kimi-for-coding)
Related
- kimi-cli#2394 — same issue in legacy Python codebase
- Multica adapter ready to consume: kimi.go#L305-L385
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start in packages/acp-adapter/src/session.ts at the turn.ended handler around lines 1161–1205, then inspect session.getUsage() and the ACP response/notification types. Reproduce with kimi acp and complete a turn; done means PromptResponse.usage contains the available token counts or a usage_update is emitted and the result is visible to ACP clients.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- typescript
- Domain
- api
- Issue type
- Bug
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Activity status
- Quiet
- Clarity
- Clearly specified
- Newbie friendliness
- 72/100