/** * ADR-O001 D8 — cost tiers: a named cost level that sets BOTH the model AND * the reasoning/thinking effort for a role, on whatever provider that role * runs on. * * Measured on one real org run, holding volume constant: all-Opus $1,744, * as-run blended $1,050, all-Sonnet $698, all-Haiku $349. Tiering is roughly * a 3x win and is independent of every other D-decision. * * ────────────────────────────────────────────────────────────────────────── * DO NOT TIER A DELIBERATIVE ROLE INTO A WEAK VOICE. * * ADR-O001, "What this does NOT apply to — deliberation and debate": * * "do not tier a debate participant into a weak voice. Tier by * *difficulty of the judgement*, not by role label. A cheap model in a * debate produces cheap arguments and the synthesiser cannot tell." * * Tier by how hard the judgement is, not by what the role is called. A role * whose value is the quality of its disagreement — a design reviewer, a * red-teamer, a debate participant, a synthesiser reading every position — * belongs at `"exempt"` (see EXEMPT_TIER), which opts it out of tiering * entirely and leaves its model exactly as it was. Control its cost with a * budget (D1), not with a cheaper model. * ────────────────────────────────────────────────────────────────────────── * * Three properties this deliberately has: * * - **Defaulted off.** An org that declares no `cost_tiers` resolves to * `undefined` here and behaves byte-identically to before. Asserted by * org-cost-tier.test.ts, not merely promised by this comment. * * - **Provider-agnostic.** A tier is a table keyed by *provider key* — a * Vercel vendor slug (`glm`, `openai`, …) when the role has one, else the * role's runtime id (`codex`, `kimicode`, `claude`, …). The built-in * catalog covers Claude only (the default and the only provider whose * model ids and effort mechanism this repo can verify); every other * provider — including one this code has never heard of — is declared in * the org config with no code change. * * - **Never a silent downgrade.** If a tier has no entry for a role's * provider, resolution THROWS (and `validateCostTiers` reports it at org * start, before a token is spent) rather than quietly picking something. * * Effort is abstract here (`off | low | medium | high | xhigh | max`, the * Claude Agent SDK's own vocabulary plus an explicit `off`). Each provider * expresses it its own way: * - Claude: natively — session.ts hands it to the runner, which sets the * SDK's `effort` option (or `thinking: {type:'disabled'}` for `off`). * - Any other provider: whatever env vars the org declares under * `cost_tiers.providers..effort_env.`, merged into the * session env. Providers that encode effort in the model id instead * (e.g. `gemini-3.6-flash-high`) need nothing extra — name the model * you want per tier. * - A provider with no mechanism at all simply ignores the level. */ import type { OrgDef, OrgRole } from './types.js'; /** Abstract reasoning-effort level. Mirrors the Claude Agent SDK's * `EffortLevel` ('low' | 'medium' | 'high' | 'xhigh' | 'max') plus an * explicit 'off' meaning "no extended thinking at all". */ export declare const ORG_EFFORT_LEVELS: readonly ['off', 'low', 'medium', 'high', 'xhigh', 'max']; export type OrgEffortLevel = (typeof ORG_EFFORT_LEVELS)[number]; /** Reserved tier name: this role is not tiered at all. See the header. */ export declare const EXEMPT_TIER = "exempt"; /** What one tier resolves to for one provider. */ export interface CostTierEntry { model: string; effort?: OrgEffortLevel; } /** A tier: provider key → entry. */ export type CostTierDef = Record; /** How one provider expresses an effort level, when it can. */ export interface CostTierProviderDef { /** Env vars to set for the session, per effort level. */ effort_env?: Partial>>; } /** The org's `cost_tiers` block. */ export interface CostTiersConfig { /** Tier applied to every role that has no entry in `roles`. */ default?: string; /** Per-role assignment. A bare string is a tier name (or `"exempt"`); * the object form additionally overrides the tier's effort, which is how * "patrol roles drop effort since they do simpler, more repetitive work" * is expressed without minting a whole extra tier. */ roles?: Record; /** User catalog. Merged OVER the built-in catalog per tier, per provider — * so adding `{economy: {codex: …}}` extends built-in `economy` rather than * replacing its `claude` entry. */ tiers?: Record; /** Per-provider effort expression. */ providers?: Record; } /** Built-in catalog. Claude only, on purpose: these are the model ids and the * effort mechanism this repo can actually verify. Tiering a role on any other * provider means naming that provider's models in `cost_tiers.tiers` — which * is the honest alternative to inventing model ids and downgrading a role * onto a string nobody checked. */ export declare const BUILTIN_COST_TIERS: Record; /** Thrown when a tier cannot resolve a model for a role's provider, or names * a tier that does not exist. Never swallowed into a default — see header. */ export declare class CostTierError extends Error { } /** The provider key a role tiers under: its Vercel vendor slug when it has * one (that is what actually picks the model), else its runtime id, else the * org's runtime, else 'claude'. Mirrors resolveModel()'s own vendor-then- * runtime precedence in session.ts. */ export declare function costTierProviderKey(role: Pick, orgRuntime?: string, vendor?: string): string; /** What a role's tier resolved to. */ export interface ResolvedCostTier { tier: string; provider: string; model: string; effort?: OrgEffortLevel; /** Env vars expressing `effort` for this provider; `{}` when the provider * has no env mechanism (Claude, which is handled natively, or a provider * with none at all). */ env: Record; } export declare function defaultRoleEffort(runtime: string | undefined): OrgEffortLevel | undefined; /** True when the org assigned this role the reserved `exempt` tier: it asked for * no tiering at all, so it also gets no default effort. */ export declare function isRoleExempt(def: Pick | undefined, roleId: string): boolean; /** * Resolve the cost tier for one role. Returns `undefined` when the org * declares no tiers, when the role is assigned none, or when it is * `"exempt"` — all of which mean "leave this role exactly as it was". * * Throws CostTierError when a tier IS assigned but cannot be honoured, so a * misconfigured tier is a loud failure rather than a silent downgrade. */ export declare function resolveRoleCostTier(args: { role: OrgRole; def?: Pick & Record; /** Vendor override, when the caller already resolved a named provider's * vendor (session.ts does). */ vendor?: string; }): ResolvedCostTier | undefined; /** * Every cost-tier problem in an org definition, as human-readable lines. * Empty for an org that declares no tiers (the upgrade-safe case). Called by * the daemon before it spawns anything — a tier that cannot resolve must fail * the run at start, not ten minutes and a wrong model later. */ export declare function validateCostTiers(def: OrgDef): string[]; //# sourceMappingURL=cost-tier.d.ts.map