feat: add economy/maximum presets, tool-lab and subagent-cursor extensions
Some checks failed
CI / windows node 24 / wine blocking (push) Has been skipped
CI / node 22.19 (push) Has been skipped
CI / node 26 (push) Has been skipped
CI / python 3.10 / keyless SDK (push) Has been skipped
CI / python runtime / release-shaped Linux x64 (push) Has been skipped
CI / wine apt cache (push) Successful in 7s
CI / serial / linux (push) Has been skipped
Deploy documentation / build (push) Failing after 1m25s
Deploy documentation / deploy (push) Has been skipped
Landlock Run / Matrix (push) Successful in 5s
Release (vendor) / Pack npm tarballs (push) Failing after 2m47s
Release (dsh) / Pack npm tarballs (push) Failing after 1m56s
Sandbox / sandbox e2e (landlock, ubuntu-24.04) (push) Failing after 1m57s
Sandbox / sandbox e2e (bwrap, ubuntu-latest) (push) Failing after 1m19s
Release (vendor) / Publish to npm (push) Has been skipped
Release (dsh) / Publish to npm (push) Has been skipped
CI / serial / windows (self-hosted standby) (push) Has been cancelled
CI / larger-runner-benchmark (16, linux, dsh-ubuntu-24-04-16core, typecheck) (push) Has been cancelled
Landlock Run / darwin (no platform package — degradation proof) (push) Has been cancelled
Landlock Run / ${{ matrix.platform }} (push) Has been cancelled
CI / node 24 / static (push) Has been cancelled
CI / node 24 / coverage (push) Has been cancelled
CI / node 24 / snapshots and artifacts (push) Has been cancelled
CI / windows node 24 / native complete (push) Has been cancelled
CI / serial / linux (self-hosted standby) (push) Has been cancelled
CI / serial / macos (push) Has been cancelled
CI / larger-runner-benchmark (16, windows, dsh-windows-2025-16core, production-site) (push) Has been cancelled
CI / larger-runner-benchmark (32, linux, dsh-ubuntu-24-04-32core, typecheck) (push) Has been cancelled
CI / larger-runner-benchmark (32, windows, dsh-windows-2025-32core, production-site) (push) Has been cancelled
CI / larger-runner-benchmark (4, linux, dsh-ubuntu-24-04-4core, typecheck) (push) Has been cancelled
CI / larger-runner-benchmark (4, windows, dsh-windows-2025-4core, production-site) (push) Has been cancelled
CI / larger-runner-benchmark (64, linux, dsh-ubuntu-24-04-64core, typecheck) (push) Has been cancelled
CI / larger-runner-benchmark (64, windows, dsh-windows-2025-64core, production-site) (push) Has been cancelled
CI / larger-runner-benchmark (8, windows, dsh-windows-2025-8core, production-site) (push) Has been cancelled
CI / larger-runner-benchmark (96, linux, dsh-ubuntu-24-04-96core, typecheck) (push) Has been cancelled
CI / larger-runner-benchmark (96, windows, dsh-windows-2025-96core, production-site) (push) Has been cancelled
CI / consolidated-runner-benchmark (16, linux, dsh-ubuntu-24-04-16core, 16) (push) Has been cancelled
CI / consolidated-runner-benchmark (16, windows, dsh-windows-2025-16core, 2) (push) Has been cancelled
CI / consolidated-runner-benchmark (32, linux, dsh-ubuntu-24-04-32core, 32) (push) Has been cancelled
CI / consolidated-runner-benchmark (32, windows, dsh-windows-2025-32core, 2) (push) Has been cancelled
CI / consolidated-runner-benchmark (4, linux, dsh-ubuntu-24-04-4core, 4) (push) Has been cancelled
CI / consolidated-runner-benchmark (4, windows, dsh-windows-2025-4core, 2) (push) Has been cancelled
CI / consolidated-runner-benchmark (64, linux, dsh-ubuntu-24-04-64core, 32) (push) Has been cancelled
CI / consolidated-runner-benchmark (64, windows, dsh-windows-2025-64core, 2) (push) Has been cancelled
CI / consolidated-runner-benchmark (8, linux, dsh-ubuntu-24-04-8core, 8) (push) Has been cancelled
CI / consolidated-runner-benchmark (8, windows, dsh-windows-2025-8core, 2) (push) Has been cancelled
CI / consolidated-runner-benchmark (96, linux, dsh-ubuntu-24-04-96core, 32) (push) Has been cancelled
CI / consolidated-runner-benchmark (96, windows, dsh-windows-2025-96core, 2) (push) Has been cancelled
CI / all checks passed (push) Has been cancelled
Sandbox / sandbox e2e (seatbelt, macos-latest) (push) Has been cancelled
CI / larger-runner-benchmark (8, linux, dsh-ubuntu-24-04-8core, typecheck) (push) Has been cancelled
Sandbox / sandbox e2e (landlock, ubuntu-24.04-arm) (push) Has been cancelled
E2E (real DeepSeek API) / e2e (push) Failing after 1m24s

- new economy and maximum agent presets with three-role pipeline skill
- new packages/extensions/tool-lab (home-lab ComfyUI/Docling/Whishper tools)
- new packages/subagent/subagent-cursor provider
- openrouter balance UI with on-demand refresh
- session projection context-seed boundary fold
- regenerate docs catalogs; keep local searxng benchmark scripts
This commit is contained in:
2026-08-23 22:12:30 +07:00
parent e818e37f0a
commit 8719fa84f0
112 changed files with 5686 additions and 169 deletions

View File

@@ -20,11 +20,17 @@ import { toPiReplayState } from './replay.ts'
* @returns harness counts; cache fields appear only when non-zero (pi-ai reports zeros, not absence).
*/
export function mapUsage(usage: PiUsage): TokenUsage {
const usageRecord = usage as unknown as Record<string, unknown>
const rawCost = usageRecord['cost'] ?? usageRecord['total_cost']
const costUsd = typeof rawCost === 'number' && Number.isFinite(rawCost) && rawCost >= 0
? rawCost
: undefined
return {
inputTokens: usage.input,
outputTokens: usage.output,
...usage.cacheRead > 0 ? { cacheReadTokens: usage.cacheRead } : {},
...usage.cacheWrite > 0 ? { cacheWriteTokens: usage.cacheWrite } : {},
...costUsd !== undefined ? { costUsd } : {},
}
}

View File

@@ -138,6 +138,7 @@ export interface TokenUsage {
cacheReadTokens?: number
cacheWriteTokens?: number
reasoningTokens?: number
costUsd?: number
}
/** Display metadata for one registered provider route. */

View File

@@ -43,7 +43,11 @@ is the available balance from `GET /credits` (`total_credits` minus the spent
the monthly `usageTokens`/`limitTokens` budget from `GET /auth/key`,
`isFreeTier`, and an `updatedAt` epoch. Before any successful fetch it serves
an all-`null`
record; a failed refresh keeps the last-known snapshot and logs. The same key
record; a failed refresh keeps the last-known snapshot and logs. The
`refresh()` method fetches the account NOW and resolves with the resulting
snapshot, for a user-initiated read that must not wait for the next scheduled
tick; concurrent callers share one in-flight fetch, and a failed fetch
resolves with the last-known snapshot rather than rejecting. The same key
also refreshes the model pricing table from `GET /models`
(`pricing.prompt`/`completion` USD per token, plus a flat `request` fee and
optional `input_cache_read`/`input_cache_write` when disclosed).
@@ -54,7 +58,14 @@ step) against the pricing table. Attribution prefers the assembled message's
own `provider`/`model`; a chunk-only (failed) step prices from the newest
`request/context` route. A step on a non-`openrouter` provider is outside the
domain and changes nothing; an OpenRouter step whose model has no pricing
entry counts as an unknown (unpriced) step. The projection's `steps` view
entry counts as an unknown (unpriced) step.
Every value describes only its own session's work. A forked child session's
log opens with a verbatim copy of its parent's up to the header's
`seedLength`, and the fold skips that inherited prefix (reading only its
`request/context` route records, which carry no cost but attribute the
child's first chunk-only step). A session total plus its subagents' totals is
therefore a sum, never a double count. The projection's `steps` view
field maps each priced step to its cost in USD under `${turn}:${step}` keys,
so per-step surfaces can render spend without re-pricing; unpriced steps are
absent from the map.
@@ -95,7 +106,10 @@ usage events, never a new model-visible input.
promotional pricing may differ from the model table.
- **Pricing as of the fold** — the projection prices a cell with the model
table current when that cell folds. Refreshing pricing only affects cells
folded afterward; already-folded history keeps its prior figures.
folded afterward; already-folded history keeps its prior figures. A session
folded while the table was empty (no key yet, or a failed first fetch)
keeps its steps recorded as unpriced until a `stateVersion` bump refolds
them.
- **Per-token approximation** — cache-read/cache-write fall back to the
prompt rate when the API does not disclose separate cache rates, and the
flat request fee is charged once per step. Bills may differ by fractions

View File

@@ -103,6 +103,8 @@ export class OpenRouterUsageGateway extends TypertRemoteService {
private readonly abortController = new AbortController()
/** Authoritative settings thunk; re-pointed when the section (re)mounts. */
private currentSource: () => Config
/** In-flight on-demand balance refresh, shared by concurrent callers. */
private pendingRefresh: Promise<OpenRouterBalance> | undefined
constructor(ctx: Context, config: Config = {}) {
super(ctx, 'openRouterUsage')
@@ -124,7 +126,7 @@ export class OpenRouterUsageGateway extends TypertRemoteService {
// live pricing thunk. The unit child activates only when a projection
// registry is composed (headless assemblies stay unaffected).
ctx.inject(['sessionProjections'], (projectionCtx) => {
projectionCtx.sessionProjections.register(createOpenRouterCostProjection((model) => this.pricing.get(model)))
projectionCtx.sessionProjections.register(createOpenRouterCostProjection(model => this.lookupPricing(model)))
})
ctx.effect(() => () => {
@@ -158,6 +160,44 @@ export class OpenRouterUsageGateway extends TypertRemoteService {
return { ...this.balance }
}
/**
* Fetch the account snapshot NOW and serve the result, for a user-initiated
* read that must not wait for the next scheduled tick. Concurrent callers
* share one in-flight fetch, so repeated clicks cost one request. A failed
* fetch resolves with the last-known snapshot rather than rejecting — the
* caller's own `updatedAt` comparison tells it whether the figure moved.
* @returns the snapshot after the refresh attempt settled.
*/
@Remote('refresh')
async refresh(): Promise<OpenRouterBalance> {
this.pendingRefresh ??= (async () => {
try {
await this.refreshBalance(this.resolve(this.currentSource()))
return { ...this.balance }
} finally {
this.pendingRefresh = undefined
}
})()
return await this.pendingRefresh
}
/**
* Look up pricing for a model ID, with fallback for tagged variants (`:free`, etc.).
* @param model - model identifier.
* @returns matched rate, or undefined if unknown.
*/
lookupPricing(model: string): ModelPricing | undefined {
const direct = this.pricing.get(model)
if (direct !== undefined) return direct
if (model.includes(':')) {
const tagIndex = model.indexOf(':')
const base = model.slice(0, tagIndex)
const basePricing = this.pricing.get(base)
if (basePricing !== undefined) return basePricing
}
return undefined
}
/** Materialize plugin defaults against the validated section. */
private resolve(config: Config): Required<Config> {
return {

View File

@@ -11,6 +11,14 @@
* Refreshing pricing only affects cells folded afterward — the documented
* "as of fold" limitation.
*
* Inherited history: a forked child session opens with a verbatim copy of its
* parent's log up to `header.seedLength`. Those steps are the parent's spend,
* which the parent's own value already reports, so the fold skips them and
* every value describes only the session's OWN work — a parent total plus its
* children's totals is a sum, never a double count. `request/context` records
* are still read from the inherited prefix: they carry no cost and the route
* they establish is what attributes the child's first chunk-only step.
*
* Model attribution: an `assistant/message` carries its own provider/model in
* `message.source`; a chunk-only (failed) step has none, so the fold prices it
* from the newest `request/context` last-wins record. A step on a provider that
@@ -24,7 +32,7 @@
import { z } from 'zod'
import type { TokenUsage } from '@deepseek-ai/dsh-llm'
import type { SessionEvent } from '@deepseek-ai/dsh-session'
import type { ProjectionDefinition } from '@deepseek-ai/dsh-session-projection'
import type { ProjectionDefinition, ProjectionFoldContext } from '@deepseek-ai/dsh-session-projection'
import type { ModelPricing, OpenRouterCost } from './types.ts'
/** The provider route this projection prices; LLM routing must land here. */
@@ -84,7 +92,13 @@ export function createOpenRouterCostProjection(
key: 'openRouterCost',
schema: costSchema as unknown as z.ZodType<OpenRouterCost>,
init: () => ({ totalUsd: 0, pricedSteps: 0, unknownModelSteps: 0, steps: {}, last: null, lastModel: null }),
apply: (state, event: SessionEvent) => {
apply: (state, event: SessionEvent, context: ProjectionFoldContext) => {
// A forked child's log opens with a verbatim copy of its parent's log.
// That spend belongs to the parent — which already reports it — so
// pricing it here would report it twice, once per forked child. The
// route records still apply: attribution carries across the boundary,
// so `request/context` below is read from the inherited prefix too.
if (event.seq < context.seedLength && event.type !== 'request/context') return state
if (event.type === 'request/context') {
const nextModel = { provider: event.data.provider, model: event.data.model }
if (state.lastModel?.provider === nextModel.provider && state.lastModel?.model === nextModel.model) return state
@@ -106,14 +120,21 @@ export function createOpenRouterCostProjection(
} else {
return state
}
// A non-openrouter step is outside this plugin's domain: never counted,
// never recorded (a later, correctly-attributed message for the same
// step must still land fresh).
if (attribution === undefined || attribution.provider !== OPENROUTER_PROVIDER) return state
const directCost = usage.costUsd
const pricing = attribution !== undefined ? pricingOf(attribution.model) : undefined
const isOpenRouterProvider = attribution !== undefined && (
attribution.provider === OPENROUTER_PROVIDER
|| attribution.provider.toLowerCase().includes('openrouter')
)
const pricing = pricingOf(attribution.model)
const priced = pricing !== undefined
const costUsd = priced ? stepCostUsd(usage, pricing) : 0
if (directCost === undefined && !isOpenRouterProvider && pricing === undefined) return state
const costUsd = directCost !== undefined
? directCost
: pricing !== undefined
? stepCostUsd(usage, pricing)
: 0
const priced = directCost !== undefined || pricing !== undefined
const previous = state.last !== null && state.last.turn === turn && state.last.step === step
? state.last
: null
@@ -144,6 +165,6 @@ export function createOpenRouterCostProjection(
steps: state.steps,
currency: 'USD',
}),
stateVersion: 2,
stateVersion: 4,
}
}

View File

@@ -33,11 +33,13 @@ export interface ModelPricing {
}
/**
* Whole-log OpenRouter spend for one session, priced from the logged token
* usage of its steps against the pricing table current at fold time. Every
* field is 0 until its first contributing priced step lands; a session whose
* provider route is not `openrouter`, or whose models have no pricing entry,
* stays all-zero.
* One session's OWN OpenRouter spend, priced from the logged token usage of
* its steps against the pricing table current at fold time. A forked child's
* inherited log prefix (the parent's history up to `header.seedLength`) is
* excluded, so a parent value plus its subagents' values is a sum rather
* than a double count. Every field is 0 until its first contributing priced
* step lands; a session whose provider route is not `openrouter`, or whose
* models have no pricing entry, stays all-zero.
*/
export interface OpenRouterCost {
/** Summed USD over steps priced against a known model entry. */

View File

@@ -38,6 +38,7 @@ function stubOpenRouter() {
const pricing = [
{ id: 'deepseek/deepseek-chat', pricing: { prompt: '0.0000014', completion: '0.0000028', request: '0' } },
]
const credits = { total_credits: 42, total_usage: 1, is_free_tier: false }
const calls: string[] = []
vi.stubGlobal('fetch', vi.fn(async (input: RequestInfo | URL) => {
const url = String(input)
@@ -46,9 +47,7 @@ function stubOpenRouter() {
return new Response(JSON.stringify({ data: pricing }), { status: 200 })
}
if (url.endsWith('/credits')) {
return new Response(JSON.stringify({
data: { total_credits: 42, total_usage: 1, is_free_tier: false },
}), { status: 200 })
return new Response(JSON.stringify({ data: { ...credits } }), { status: 200 })
}
if (url.endsWith('/auth/key')) {
return new Response(JSON.stringify({
@@ -57,7 +56,7 @@ function stubOpenRouter() {
}
return new Response('not found', { status: 404 })
}))
return { calls }
return { calls, credits }
}
async function loadComposition(): Promise<Context> {
@@ -170,4 +169,39 @@ describe('openrouter-usage real composition', () => {
expect(balance.currency).toBe('USD')
expect(balance.updatedAt).not.toBeNull()
})
it('re-fetches the account on demand and serves the moved figure', async () => {
const openRouter = stubOpenRouter()
const loaded = await loadComposition()
await vi.waitFor(() => {
expect(openRouter.calls.some(url => url.endsWith('/credits'))).toBe(true)
}, { timeout: 5000 })
expect(loaded.openRouterUsage.snapshot().balanceUsd).toBe(41)
openRouter.credits.total_usage = 12
await expect(loaded.openRouterUsage.refresh()).resolves.toMatchObject({ balanceUsd: 30 })
expect(loaded.openRouterUsage.snapshot().balanceUsd).toBe(30)
})
it('shares one in-flight fetch across concurrent on-demand refreshes', async () => {
const openRouter = stubOpenRouter()
const loaded = await loadComposition()
await vi.waitFor(() => {
expect(openRouter.calls.some(url => url.endsWith('/credits'))).toBe(true)
}, { timeout: 5000 })
const before = openRouter.calls.filter(url => url.endsWith('/credits')).length
const [first, second] = await Promise.all([
loaded.openRouterUsage.refresh(),
loaded.openRouterUsage.refresh(),
])
expect(first).toEqual(second)
expect(openRouter.calls.filter(url => url.endsWith('/credits')).length).toBe(before + 1)
// The shared promise is released once it settles, so a later click fetches again.
await loaded.openRouterUsage.refresh()
expect(openRouter.calls.filter(url => url.endsWith('/credits')).length).toBe(before + 2)
})
})

View File

@@ -224,6 +224,28 @@ describe('openRouterCost session projection', () => {
expect(projected(ctx, session)).toEqual({ totalUsd: 0, pricedSteps: 0, unknownModelSteps: 1, steps: {}, currency: 'USD' })
})
it('uses explicitly logged costUsd on usage directly even without pricing table entry', async () => {
const { ctx, session } = await harness()
session.append('request/context', { provider: 'openrouter', model: 'unlisted/custom-model' })
startStep(session, 1, 1)
session.append('assistant/chunk', {
turn: 1,
step: 1,
chunk: {
type: 'usage',
usage: { inputTokens: 500, outputTokens: 200, costUsd: 0.0042 },
},
})
session.append('step/end', { turn: 1, step: 1 })
expect(projected(ctx, session)).toEqual({
totalUsd: 0.0042,
pricedSteps: 1,
unknownModelSteps: 0,
steps: { '1:1': 0.0042 },
currency: 'USD',
})
})
it('pushes no change for unrelated events', async () => {
const { ctx, session } = await harness()
recordContext(session)
@@ -235,6 +257,47 @@ describe('openRouterCost session projection', () => {
expect(changed).not.toContain('openRouterCost')
})
it('excludes the inherited prefix of a forked child, so parent + child is a sum', async () => {
const { ctx, session: parent } = await harness()
recordContext(parent)
startStep(parent, 1, 1)
const parentSource = usageChunk(parent, { inputTokens: 1_000, outputTokens: 500 }, 1, 1)
finalUsage(parent, { inputTokens: 1_000, outputTokens: 500 }, 1, 1, [parentSource])
const parentCost = 1_000 * 1.4e-6 + 500 * 2.8e-6
expect(projected(ctx, parent).totalUsd).toBe(parentCost)
// The fork: the child's log opens with a verbatim copy of the parent's.
const seed = [...parent.events]
const child = ctx.sessions.create(undefined, { seed, meta: { seedLength: seed.length } })
startStep(child, 2, 1)
const childSource = usageChunk(child, { inputTokens: 10, outputTokens: 4 }, 2, 1)
finalUsage(child, { inputTokens: 10, outputTokens: 4 }, 2, 1, [childSource])
const childCost = 10 * 1.4e-6 + 4 * 2.8e-6
const childValue = projected(ctx, child)
expect(childValue.totalUsd).toBe(childCost)
expect(childValue.pricedSteps).toBe(1)
expect(childValue.steps).toEqual({ '2:1': childCost })
// The parent's own figure is untouched by the fork.
expect(projected(ctx, parent).totalUsd).toBe(parentCost)
})
it('attributes a chunk-only step of a forked child from a route in the inherited prefix', async () => {
const { ctx, session: parent } = await harness()
// The route record is the LAST thing the parent logs, so only the
// inherited prefix can supply the child's attribution.
recordContext(parent)
const seed = [...parent.events]
const child = ctx.sessions.create(undefined, { seed, meta: { seedLength: seed.length } })
startStep(child, 1, 1)
usageChunk(child, { inputTokens: 100, outputTokens: 20 }, 1, 1)
expect(projected(ctx, child)).toMatchObject({
totalUsd: 100 * 1.4e-6 + 20 * 2.8e-6,
pricedSteps: 1,
unknownModelSteps: 0,
})
})
it('restores from a JSON checkpoint', async () => {
const { ctx, session } = await harness()
recordContext(session)

View File

@@ -7,7 +7,7 @@ import { createMessage, createUserMessage } from '@deepseek-ai/dsh-llm'
import type { ContentBlock, ToolSchema } from '@deepseek-ai/dsh-llm'
import SessionStore from '@deepseek-ai/dsh-session'
import type { Session, SessionEvent } from '@deepseek-ai/dsh-session'
import SessionProjectionRegistry from '@deepseek-ai/dsh-session-projection'
import SessionProjectionRegistry, { foldContextOf } from '@deepseek-ai/dsh-session-projection'
import TokenMeter from '@deepseek-ai/dsh-token-meter'
import type { ContextBreakdownProjection } from '@deepseek-ai/dsh-token-meter/client'
import { CompactionId } from '@deepseek-ai/dsh-compaction'
@@ -184,6 +184,9 @@ describe('contextBreakdown session projection', () => {
it('folds a replacement without a claim at zero and fails on a mismatched claim', () => {
const definition = contextBreakdownProjectionDefinition
// The unit ignores the fold context; this session inherits no fork prefix.
const applyBreakdown = (state: Parameters<typeof definition.apply>[0], event: SessionEvent) =>
definition.apply(state, event, foldContextOf({}))
const replace = (start: number, end: number): SessionEvent => ({
type: 'user/message',
seq: 9,
@@ -206,22 +209,22 @@ describe('contextBreakdown session projection', () => {
data: { shadowedRange: { start, end }, shadowedSeqs: [start, end], shadowedTokenCount: 5 },
} as unknown as SessionEvent)
let state = definition.init()
state = definition.apply(state, append(1))
state = definition.apply(state, append(3))
state = applyBreakdown(state, append(1))
state = applyBreakdown(state, append(3))
// No metering event: the replacement contributes zero instead of throwing.
expect(definition.view(definition.apply(state, replace(1, 3))).messageTokens)
expect(definition.view(applyBreakdown(state, replace(1, 3))).messageTokens)
.toBe(definition.view(state).messageTokens)
// An adjacent claim for another range contradicts the replacement.
const mismatched = definition.apply(state, meter(1, 1, 8))
expect(() => definition.apply(mismatched, replace(1, 3))).toThrow('no adjacent shadow price')
const mismatched = applyBreakdown(state, meter(1, 1, 8))
expect(() => applyBreakdown(mismatched, replace(1, 3))).toThrow('no adjacent shadow price')
// A claim expires after one intervening event, so replacement delta is zero.
let expired = definition.apply(state, meter(1, 3, 8))
expired = definition.apply(expired, { type: 'todo/write', seq: 9, time: 0, data: { todos: [] } } as unknown as SessionEvent)
expect(definition.view(definition.apply(expired, replace(1, 3))).messageTokens)
let expired = applyBreakdown(state, meter(1, 3, 8))
expired = applyBreakdown(expired, { type: 'todo/write', seq: 9, time: 0, data: { todos: [] } } as unknown as SessionEvent)
expect(definition.view(applyBreakdown(expired, replace(1, 3))).messageTokens)
.toBe(definition.view(state).messageTokens)
// The armed claim prices exactly the next event's matching replacement.
const armed = definition.apply(state, meter(1, 3, 8))
expect(definition.view(definition.apply(armed, replace(1, 3))).messageTokens)
const armed = applyBreakdown(state, meter(1, 3, 8))
expect(definition.view(applyBreakdown(armed, replace(1, 3))).messageTokens)
.toBe(definition.view(state).messageTokens - 5 + estimateMessage(
createUserMessage({ content: [{ type: 'text', text: 'x' }], source: { kind: 'user' } }),
))