Skip to content

feat: CoreMessage.thinkingTokens — meter host-projected thinking payload - #242

Merged
ranxianglei merged 2 commits into
masterfrom
2026-09-10_coremessage-thinking-tokens
Sep 10, 2026
Merged

feat: CoreMessage.thinkingTokens — meter host-projected thinking payload#242
ranxianglei merged 2 commits into
masterfrom
2026-09-10_coremessage-thinking-tokens

Conversation

@ranxianglei

Copy link
Copy Markdown
Owner

Fixes #241

Reasoning-model hosts resend thinking blocks every turn, but thinking is not part of the visible text projection — so every per-message counting site undercounted by exactly the cumulative thinking volume. With reliable usage reporting the realUsage floor masks this; without it (usage-less relays/providers) estimates run systematically low and nudge/emergency compression bands trigger late → upstream context overflow risk.

Changes

  • CoreMessage.thinkingTokens?: number (src/types.ts) — metering-only: never rendered, truncated, or indexed. Host contract: attach to exactly one core per original message (split tool-call cores must not repeat it or totals multiply).
  • countMessageTokens(message, countTokens) in src/tokenize.ts — single counting caliber: text + guarded positive-finite thinking; exported from index.ts.
  • All per-message counting sites switched to the caliber:
    • src/compress.ts — applyCompression compressedTokens; computeContextBreakdown
    • src/recommend.ts — computeProtectedRefs visible tokens; buildCompressibleRanges protected/compressible tokens (chars stays text-length: the truncation gate counts truncatable payload)
    • src/report.ts — collectVisible
    • src/render-refs.ts — ref-tag tokens attribute (what the model sees per message now matches block/range/breakdown accounting)

Compatibility

Non-breaking: field is optional; hosts that never set it behave exactly as before.

Verification

typecheck ✓ / 637 tests ✓ (incl. new tests/thinking-tokens.test.ts, 6 tests) / build ✓

Reasoning-model hosts resend thinking blocks every turn but they are not
part of the visible text projection, so every per-message counting site
undercounted by exactly the cumulative thinking volume. For providers or
relays without reliable usage reporting this shifts nudge/emergency
compression bands late and risks upstream context overflow.

Add optional CoreMessage.thinkingTokens (metering-only: never rendered,
truncated, or indexed) plus countMessageTokens() as the single counting
caliber used by block compressedTokens, context breakdown, protected/
compressible range tokens, status reports, and ref-tag token attributes.
Host contract: attach to exactly one core per original message.
@github-actions

github-actions Bot commented Sep 10, 2026

Copy link
Copy Markdown

📦 Built Package Artifact

Branch: 2026-09-10_coremessage-thinking-tokens (81b4d57)

Option A — Install from npm PR tag (recommended)

In your adapter project:

npm install acp-kernel@pr-242

Each push to this PR publishes a new version under the pr-242 npm tag.

Option B — Download artifact

  1. Download the artifact from the Actions run
  2. Extract the tarball and install:
tar xzf acp-kernel-pr242.tgz
npm install ./package

This comment is automatically updated on each push.

Review nit from #242 review: the positive-finite guard on
CoreMessage.thinkingTokens was duplicated inline in render-refs.ts
instead of going through the shared caliber. Extract it as
thinkingTokenValue() in tokenize.ts so countMessageTokens and the
ref-tag tokens attribute cannot drift apart, and cover the ref-tag
path with an invalid-payload test.
@ranxianglei

Copy link
Copy Markdown
Owner Author

[bot] 🏷 Review complete (issue #241).

Verified independently on 850e711 before review, and again after the fix below: typecheck ✓ / 638/638 tests ✓ / build ✓ (same three steps as CI, Node 22). Coverage audit confirmed all five per-message counting sites from the issue are switched to the shared caliber, and the remaining .text counting sites (absorb.ts ×2, truncate-tools.ts) are intentionally text-only — they measure truncatable/absorbable visible payload, matching the spec that the truncation gate counts only truncatable payload. Wire codec passes CoreMessage by reference so thinkingTokens survives proxy round-trips; search fingerprint excludes thinking per the never-indexed contract.

One fix applied directly (81b4d57): the positive-finite guard on thinkingTokens was duplicated inline in render-refs.ts instead of going through the single counting caliber. Extracted as thinkingTokenValue() in tokenize.ts, shared by countMessageTokens and the ref-tag tokens attribute, plus an invalid-payload test for the ref-tag path. Diff stays focused: 3 files, +31/−12.

Verdict: ready to merge (human merges).

@ranxianglei
ranxianglei merged commit 3c8b00c into master Sep 10, 2026
4 checks passed
@ranxianglei ranxianglei mentioned this pull request Sep 10, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[feature] CoreMessage 增加 thinkingTokens:reasoning 模型回传体积计入 ACP 计量

1 participant