MoonshotAI / MoonshotAI/kimi-code

ACP adapter does not report token usage (PromptResponse.usage empty, no usage_update emitted)

Open
#1,855 1 comment 0 reactions 0 assignees View on GitHub

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

  1. Breaking for external orchestration platforms. Multica's runtime usage command 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.

  2. The data exists internally. Kimi Code's internal SDK tracks usage via session.getUsage() and serializes it to wire.jsonl (inputOther/output/inputCacheRead/inputCacheCreation). It just never crosses the ACP boundary.

  3. 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:

  1. PromptResponse.usage — maps directly to TurnResponse and is consumed by Multica's kimi adapter (kimi.go L305–L385).
  2. session/update with usage_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

  1. Run kimi-code v0.26.0 in ACP mode (kimi acp)
  2. Complete any turn (simple prompt or tool-calling turn, doesn't matter)
  3. Observe the prompt response — usage field is absent or empty
  4. Compare with Claude Code / OpenCode in ACP mode — both fill usage on 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

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. 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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.