From 559026b54d2591dc83c0ff04bf1e233a03b7bd39 Mon Sep 17 00:00:00 2001 From: Drew Stone Date: Thu, 13 Aug 2026 02:43:39 -0600 Subject: [PATCH 1/2] fix(runtime): accept fractional USD in driver prompt-cache evidence The driver applied one rule to every numeric prompt-cache field: a non-negative safe integer. Token counts satisfy it; USD amounts do not. Any provider that reports cache savings in dollars therefore killed the driver on its first turn with driverAgent: prompt-cache field "readSavingsUsd" must be a non-negative safe integer which is what tangle-router returns (readSavingsUsd 0.0034 on a healthy response). The failure is total: the root driver never completes a turn, so the whole supervised run ends no-winner with reason driver-failed. The rule now splits by field kind. A name ending in usd (case-insensitive) must be a non-negative FINITE number; every other numeric field keeps the non-negative safe-integer rule. The check moves into an exported validateDriverPromptCache so the contract is named and unit-testable. 6 new tests cover the real router shape, fractional token counts, negative and non-finite USD, string fields, absent evidence, and the suffix rule (usdTokens stays a count). Typecheck clean; supervise suite 55/55. --- .../supervise/coordination-driver.test.ts | 44 +++++++++++++++++++ src/runtime/supervise/coordination-driver.ts | 42 ++++++++++++++---- 2 files changed, 78 insertions(+), 8 deletions(-) create mode 100644 src/runtime/supervise/coordination-driver.test.ts diff --git a/src/runtime/supervise/coordination-driver.test.ts b/src/runtime/supervise/coordination-driver.test.ts new file mode 100644 index 00000000..177068bb --- /dev/null +++ b/src/runtime/supervise/coordination-driver.test.ts @@ -0,0 +1,44 @@ +import { describe, expect, it } from 'vitest' +import { validateDriverPromptCache } from './coordination-driver' + +describe('validateDriverPromptCache', () => { + it('accepts fractional USD savings — the shape a real router reports', () => { + // The exact evidence that refused an otherwise healthy driver: tangle-router returns + // cache savings in dollars, which are fractional, and the count rule rejected them. + expect( + validateDriverPromptCache({ readTokens: 5888, writeTokens: 0, readSavingsUsd: 0.0034 }), + ).toBeUndefined() + }) + + it('still refuses a fractional TOKEN count', () => { + const error = validateDriverPromptCache({ readTokens: 12.5 }) + expect(error?.message).toContain('"readTokens"') + expect(error?.message).toContain('non-negative safe integer') + }) + + it('refuses a negative USD amount and a non-finite one', () => { + expect(validateDriverPromptCache({ readSavingsUsd: -0.01 })?.message).toContain( + 'non-negative finite number', + ) + expect(validateDriverPromptCache({ writeCostUsd: Number.NaN })?.message).toContain( + 'non-negative finite number', + ) + expect(validateDriverPromptCache({ writeCostUsd: Number.POSITIVE_INFINITY })).toBeDefined() + }) + + it('refuses a negative token count and ignores string fields', () => { + expect(validateDriverPromptCache({ readTokens: -1 })?.message).toContain('safe integer') + expect(validateDriverPromptCache({ tier: 'ephemeral-1h' })).toBeUndefined() + }) + + it('treats absent evidence as acceptable rather than inventing zeroes', () => { + expect(validateDriverPromptCache(undefined)).toBeUndefined() + expect(validateDriverPromptCache({})).toBeUndefined() + }) + + it('matches USD fields case-insensitively at the end of the name only', () => { + expect(validateDriverPromptCache({ savingsUSD: 1.25 })).toBeUndefined() + // `usdTokens` is a count despite carrying the substring — the suffix is what decides. + expect(validateDriverPromptCache({ usdTokens: 1.25 })?.message).toContain('safe integer') + }) +}) diff --git a/src/runtime/supervise/coordination-driver.ts b/src/runtime/supervise/coordination-driver.ts index ecfdc79d..d42229f1 100644 --- a/src/runtime/supervise/coordination-driver.ts +++ b/src/runtime/supervise/coordination-driver.ts @@ -296,6 +296,39 @@ function providerAttemptEvidence(model: string | undefined): ProviderModelExecut ) } +/** + * Validate provider-reported prompt-cache evidence. + * + * Prompt-cache carries two kinds of number and they obey different rules: token COUNTS are + * integers, and USD amounts are fractional by nature. Applying the count rule to a dollar + * field refuses every provider that reports cache savings in dollars — a healthy router + * response carrying `readSavingsUsd: 0.0034` failed the driver outright before this split. + * + * Returns the refusal, or `undefined` when the evidence is acceptable. + */ +export function validateDriverPromptCache( + promptCache: Readonly> | undefined, +): ValidationError | undefined { + for (const [field, value] of Object.entries(promptCache ?? {})) { + if (typeof value !== 'number') continue + const isUsdField = /usd$/i.test(field) + if (isUsdField) { + if (!Number.isFinite(value) || value < 0) { + return new ValidationError( + `driverAgent: prompt-cache field ${JSON.stringify(field)} must be a non-negative finite number`, + ) + } + continue + } + if (!Number.isSafeInteger(value) || value < 0) { + return new ValidationError( + `driverAgent: prompt-cache field ${JSON.stringify(field)} must be a non-negative safe integer`, + ) + } + } + return undefined +} + /** * Build the intelligent recursive driver. Its `act` is the LLM tool-loop; spawn it as a * `driverChild` (`driver-executor.ts`) to run it inside a nested scope, recursively. @@ -496,14 +529,7 @@ export function driverAgent(opts: DriverAgentOptions): Agent { 'driverAgent: transportAttempts must be a positive safe integer when reported', ) } - for (const [field, value] of Object.entries(res.promptCache ?? {})) { - if (typeof value === 'number' && (!Number.isSafeInteger(value) || value < 0)) { - evidenceError = new ValidationError( - `driverAgent: prompt-cache field ${JSON.stringify(field)} must be a non-negative safe integer`, - ) - break - } - } + evidenceError = validateDriverPromptCache(res.promptCache) ?? evidenceError const trustedCost = res.costProvenance === 'provider-receipt' || res.costProvenance === 'billing-receipt' const cacheUsage = driverPromptCacheUsage(res.usage?.input, res.promptCache) From 9f85d8c386b14ac8277b32979ee61bce04e15d46 Mon Sep 17 00:00:00 2001 From: Drew Stone Date: Thu, 13 Aug 2026 03:00:47 -0600 Subject: [PATCH 2/2] refactor(runtime): classify prompt-cache fields by schema, not by name MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Addresses the review's convergent finding (4 of 6): the /usd$/i suffix was the sole semantic discriminator, so a provider dollar field named savingsDollars would still be refused and an unknown count ending in 'usd' would skip integer checking. Classification is now schema-first. PromptCacheUsage names its members and readSavingsUsd is its only dollar amount, so known fields are validated by what they ARE; promptCache stays an open record because the sandbox path forwards provider fields verbatim, so unknown fields keep the documented usd-suffix convention as the fallback. Also from the review: a USD ceiling keeps 1e308 out (the old all-integer rule rejected it as a side effect), and three tests were added — whole dollars, -0, the absurd value, the full router shape with missTokens, and known-member classification. 9 tests here, supervise suite 58/58, lint and typecheck clean. --- .../supervise/coordination-driver.test.ts | 32 +++++++++++++++++-- src/runtime/supervise/coordination-driver.ts | 20 ++++++++++-- 2 files changed, 46 insertions(+), 6 deletions(-) diff --git a/src/runtime/supervise/coordination-driver.test.ts b/src/runtime/supervise/coordination-driver.test.ts index 177068bb..31565144 100644 --- a/src/runtime/supervise/coordination-driver.test.ts +++ b/src/runtime/supervise/coordination-driver.test.ts @@ -18,14 +18,33 @@ describe('validateDriverPromptCache', () => { it('refuses a negative USD amount and a non-finite one', () => { expect(validateDriverPromptCache({ readSavingsUsd: -0.01 })?.message).toContain( - 'non-negative finite number', + 'non-negative finite number of dollars', ) expect(validateDriverPromptCache({ writeCostUsd: Number.NaN })?.message).toContain( - 'non-negative finite number', + 'non-negative finite number of dollars', ) expect(validateDriverPromptCache({ writeCostUsd: Number.POSITIVE_INFINITY })).toBeDefined() }) + it('accepts a whole-dollar USD amount and -0, and keeps an absurd one out', () => { + expect(validateDriverPromptCache({ readSavingsUsd: 5 })).toBeUndefined() + expect(validateDriverPromptCache({ readSavingsUsd: -0 })).toBeUndefined() + // The old all-integer rule rejected 1e308 as a side effect; the ceiling keeps that. + expect(validateDriverPromptCache({ readSavingsUsd: 1e308 })?.message).toContain('dollars') + }) + + it('accepts the full router-reported shape including missTokens', () => { + expect( + validateDriverPromptCache({ + readTokens: 5888, + writeTokens: 0, + missTokens: 111, + readSavingsUsd: 0.0034, + status: 'hit', + }), + ).toBeUndefined() + }) + it('refuses a negative token count and ignores string fields', () => { expect(validateDriverPromptCache({ readTokens: -1 })?.message).toContain('safe integer') expect(validateDriverPromptCache({ tier: 'ephemeral-1h' })).toBeUndefined() @@ -36,7 +55,14 @@ describe('validateDriverPromptCache', () => { expect(validateDriverPromptCache({})).toBeUndefined() }) - it('matches USD fields case-insensitively at the end of the name only', () => { + it('classifies known schema members by what they are, not by their name', () => { + // readSavingsUsd is the schema's only dollar member; the token members stay counts even + // though nothing in their names says so. + expect(validateDriverPromptCache({ missTokens: 12.5 })?.message).toContain('safe integer') + expect(validateDriverPromptCache({ readSavingsUsd: 0.5 })).toBeUndefined() + }) + + it('falls back to the usd name-suffix convention for unknown pass-through fields', () => { expect(validateDriverPromptCache({ savingsUSD: 1.25 })).toBeUndefined() // `usdTokens` is a count despite carrying the substring — the suffix is what decides. expect(validateDriverPromptCache({ usdTokens: 1.25 })?.message).toContain('safe integer') diff --git a/src/runtime/supervise/coordination-driver.ts b/src/runtime/supervise/coordination-driver.ts index d42229f1..db340fc1 100644 --- a/src/runtime/supervise/coordination-driver.ts +++ b/src/runtime/supervise/coordination-driver.ts @@ -296,6 +296,15 @@ function providerAttemptEvidence(model: string | undefined): ProviderModelExecut ) } +/** The USD-denominated members of {@link PromptCacheUsage} — the schema, not a guess. Every + * other known member (`readTokens`, `writeTokens`, `missTokens`) is a token COUNT. */ +const PROMPT_CACHE_USD_FIELDS: ReadonlySet = new Set(['readSavingsUsd']) + +/** Dollar amounts above this are provider nonsense, not evidence. The old all-integer rule + * rejected them as a side effect; keeping an explicit ceiling preserves that protection + * without pretending a dollar amount is an integer. */ +const MAX_PROMPT_CACHE_USD = 1_000_000 + /** * Validate provider-reported prompt-cache evidence. * @@ -304,6 +313,11 @@ function providerAttemptEvidence(model: string | undefined): ProviderModelExecut * field refuses every provider that reports cache savings in dollars — a healthy router * response carrying `readSavingsUsd: 0.0034` failed the driver outright before this split. * + * Classification is schema-first: a field named in {@link PromptCacheUsage} is validated by + * what that member IS. `promptCache` is an open record (the sandbox path forwards provider + * fields verbatim), so an unknown field falls back to the `usd` name-suffix convention — + * documented here as the contract a provider must follow to report dollars. + * * Returns the refusal, or `undefined` when the evidence is acceptable. */ export function validateDriverPromptCache( @@ -311,11 +325,11 @@ export function validateDriverPromptCache( ): ValidationError | undefined { for (const [field, value] of Object.entries(promptCache ?? {})) { if (typeof value !== 'number') continue - const isUsdField = /usd$/i.test(field) + const isUsdField = PROMPT_CACHE_USD_FIELDS.has(field) || /usd$/i.test(field) if (isUsdField) { - if (!Number.isFinite(value) || value < 0) { + if (!Number.isFinite(value) || value < 0 || value > MAX_PROMPT_CACHE_USD) { return new ValidationError( - `driverAgent: prompt-cache field ${JSON.stringify(field)} must be a non-negative finite number`, + `driverAgent: prompt-cache field ${JSON.stringify(field)} must be a non-negative finite number of dollars`, ) } continue