fix(token-meter): close projected usage review gaps

This commit is contained in:
Hypatia May
2026-07-30 17:22:15 +08:00
parent e23cd8e406
commit 6d58953f30
43 changed files with 280 additions and 183 deletions

View File

@@ -2,5 +2,5 @@
# side as of the last confirmed-consistent state. Both languages carry equal authority;
# after editing either side, bring the other along and re-record with:
# pnpm run verify-translation-pairing --write docs/core-data-structures/session.md
session.md: d315c63daec2fd7dae23406e4a513a746b05c1b8
session.zh.md: c616626c8b022e9cbd94f55153286f8626926588
session.md: bc1f8533756cc4f1bf64c9e482d4c4979d086fcc
session.zh.md: 1e5c058d95716bf832b1170e22dbb4bb560b7e92

View File

@@ -92,13 +92,13 @@ interface SessionEventMap {
*/
'request/header': { header: EpochHeader; reason: RequestHeaderReason }
/**
* Registration-bound context capacity for the route a request resolved to,
* Registration-bound context metadata for the route a request resolved to,
* appended inside its step beside `request/header` and only when the route
* or capacity differs from the last record. It is log-only and deliberately
* NOT part of {@link EpochHeader}: capacity is adapter metadata about a
* route, not an input the request was built from, so it must not participate
* in request reconstruction or header equality. Absent for a route whose
* adapter advertises no capacity.
* in request reconstruction or header equality. `contextWindow` is absent
* when the route's adapter advertises no capacity.
*/
'request/context': RequestContext
/**
@@ -176,21 +176,21 @@ Canonical form represents an empty system prompt or tool list as an absent field
### The route capacity event: `request/context`
The context window of the route a request resolved to is separate logged state, appended beside `request/header` inside the same step and only when the provider, model, or capacity differs from the previous record. It stays outside `EpochHeader` because that type is the reconstruction contract compared field-wise by `headerEquals`: capacity describes a route, not a request input, so folding it in would let a capacity change register as a request-envelope `change` and would pull adapter metadata into the loop's reconstruction invariant. Like `request/header`, it is not a `SurfaceEventType` and produces no LLM message. `session.requestContext()` folds the latest record incrementally. A route whose adapter advertises no capacity appends nothing, which consumers read as "capacity unknown".
The context metadata of the route a request resolved to is separate logged state, appended beside `request/header` inside the same step and only when the provider, model, or capacity differs from the previous record. It stays outside `EpochHeader` because that type is the reconstruction contract compared field-wise by `headerEquals`: capacity describes a route, not a request input, so folding it in would let a capacity change register as a request-envelope `change` and would pull adapter metadata into the loop's reconstruction invariant. Like `request/header`, it is not a `SurfaceEventType` and produces no LLM message. `session.requestContext()` folds the latest record incrementally. A route whose adapter advertises no capacity is recorded with `contextWindow` absent, so the new record clears an older route's capacity.
```ts type-equiv
/**
* Registration-bound context capacity of one resolved model route. Adapter
* Registration-bound context metadata of one resolved model route. Adapter
* metadata about a route rather than a request input, which is why it lives
* outside {@link EpochHeader}.
*/
interface RequestContext {
/** Registered provider route the capacity was resolved through. */
/** Registered provider route the metadata was resolved through. */
provider: string
/** Provider-owned model id the capacity belongs to. */
/** Provider-owned model id the metadata belongs to. */
model: string
/** Maximum combined request and response context in tokens. */
contextWindow: number
/** Maximum combined request and response context in tokens; absent when the adapter advertises none. */
contextWindow?: number
}
```
@@ -453,11 +453,11 @@ declare class Session {
*/
requestHeader(): EpochHeader | undefined;
/**
* The route capacity in force after the log's last `request/context` event —
* The route metadata in force after the log's last `request/context` event —
* what the NEXT request deduplicates against — or undefined before any such
* record. Maintained incrementally like {@link requestHeader}, so a per-step
* read costs O(new events).
* @returns the folded capacity record, or undefined when none exists yet.
* @returns the folded context record, or undefined when none exists yet.
*/
requestContext(): RequestContext | undefined;
/**

View File

@@ -92,13 +92,13 @@ interface SessionEventMap {
*/
'request/header': { header: EpochHeader; reason: RequestHeaderReason }
/**
* Registration-bound context capacity for the route a request resolved to,
* Registration-bound context metadata for the route a request resolved to,
* appended inside its step beside `request/header` and only when the route
* or capacity differs from the last record. It is log-only and deliberately
* NOT part of {@link EpochHeader}: capacity is adapter metadata about a
* route, not an input the request was built from, so it must not participate
* in request reconstruction or header equality. Absent for a route whose
* adapter advertises no capacity.
* in request reconstruction or header equality. `contextWindow` is absent
* when the route's adapter advertises no capacity.
*/
'request/context': RequestContext
/**
@@ -178,21 +178,21 @@ interface EpochHeader {
### 路由容量事件:`request/context`
请求所解析到的路由的上下文窗口是独立的已记录状态,在同一步骤内紧随 `request/header` 追加,且仅在提供方、模型或容量与上一条记录不同时追加。它保持在 `EpochHeader` 之外,因为该类型是由 `headerEquals` 逐字段比较的重建契约:容量描述的是路由,不是请求输入,把它折叠进去会让一次容量变化被登记为请求信封的 `change`,也会把适配器元数据拉进 loop 的重建不变式。与 `request/header` 一样,它不是 `SurfaceEventType`,也不产生 LLM 消息。`session.requestContext()` 以增量方式归并最新一条记录。适配器不公布容量的路由不追加任何记录,消费方将此读作「容量未知」
请求所解析到的路由的上下文元数据是独立的已记录状态,在同一步骤内紧随 `request/header` 追加,且仅在提供方、模型或容量与上一条记录不同时追加。它保持在 `EpochHeader` 之外,因为该类型是由 `headerEquals` 逐字段比较的重建契约:容量描述的是路由,不是请求输入,把它折叠进去会让一次容量变化被登记为请求信封的 `change`,也会把适配器元数据拉进 loop 的重建不变式。与 `request/header` 一样,它不是 `SurfaceEventType`,也不产生 LLM 消息。`session.requestContext()` 以增量方式归并最新一条记录。适配器不公布容量的路由会以缺失 `contextWindow` 的形式记录,因此新记录可以清除较早路由的容量
```ts type-equiv
/**
* Registration-bound context capacity of one resolved model route. Adapter
* Registration-bound context metadata of one resolved model route. Adapter
* metadata about a route rather than a request input, which is why it lives
* outside {@link EpochHeader}.
*/
interface RequestContext {
/** Registered provider route the capacity was resolved through. */
/** Registered provider route the metadata was resolved through. */
provider: string
/** Provider-owned model id the capacity belongs to. */
/** Provider-owned model id the metadata belongs to. */
model: string
/** Maximum combined request and response context in tokens. */
contextWindow: number
/** Maximum combined request and response context in tokens; absent when the adapter advertises none. */
contextWindow?: number
}
```
@@ -455,11 +455,11 @@ declare class Session {
*/
requestHeader(): EpochHeader | undefined;
/**
* The route capacity in force after the log's last `request/context` event —
* The route metadata in force after the log's last `request/context` event —
* what the NEXT request deduplicates against — or undefined before any such
* record. Maintained incrementally like {@link requestHeader}, so a per-step
* read costs O(new events).
* @returns the folded capacity record, or undefined when none exists yet.
* @returns the folded context record, or undefined when none exists yet.
*/
requestContext(): RequestContext | undefined;
/**

View File

@@ -373,13 +373,13 @@ Source: [`packages/plan/plan-mode/src/index.ts:51`](../packages/plan/plan-mode/s
```ts persistence-catalog
/**
* Registration-bound context capacity for the route a request resolved to,
* Registration-bound context metadata for the route a request resolved to,
* appended inside its step beside `request/header` and only when the route
* or capacity differs from the last record. It is log-only and deliberately
* NOT part of {@link EpochHeader}: capacity is adapter metadata about a
* route, not an input the request was built from, so it must not participate
* in request reconstruction or header equality. Absent for a route whose
* adapter advertises no capacity.
* in request reconstruction or header equality. `contextWindow` is absent
* when the route's adapter advertises no capacity.
*/
'request/context': RequestContext
```