Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -330,7 +330,7 @@ earns its cost (the knobs themselves:
| Situation | Model | Effort | Why this model | Why this effort |
| --------- | ----- | ------ | -------------- | --------------- |
| Exploration (`scout`) | sonnet | low | Finding and tracing code is retrieval, not reasoning - a cheap tier reports as well as a costly one, and the file volume stays in the subagent regardless. | The work is mechanical lookup; extra thinking buys nothing. |
| Ordinary implementation (`implementer`) | sonnet | medium | Sonnet is near-opus quality on single-file, clear-shape coding at a fraction of the price (intro pricing through 2026-08-31 makes it ~2.5x cheaper than opus) - for work whose approach the plan already decided, the margin never changes the outcome. | The plan already decided the approach; the agent executes real logic, not design. |
| Ordinary implementation (`implementer`) | sonnet | medium | Sonnet is near-opus quality on single-file, clear-shape coding at a fraction of the price (permanently priced at $2/$10, ~2.5x cheaper than opus) - for work whose approach the plan already decided, the margin never changes the outcome. | The plan already decided the approach; the agent executes real logic, not design. |
| Complex implementation (`implementer` `model=opus`) | opus | medium (pinned) | Multi-file or cross-layer changes, security/money/migrations/concurrency/public contracts, or a retry after a weak sonnet result - a wrong approach is expensive, and Opus 5 stepped the tier up at UNCHANGED price, so escalate when in doubt. (Ambiguous tasks are not an escalation case: implementer stops on ambiguity by contract - clarify first.) | The `model=opus` dispatch changes the model only - the Agent tool has no effort param, so the pinned medium stays; the escalation buys the tier, not extra thinking. |
| Code review (`reviewer`) | opus | high | Review is an asymmetric bet - one pass guards against a bug that costs far more if it ships, so it is the one place to prefer the top tier by default. | High: subtle correctness bugs hide from shallow reading. Anthropic reports Opus 5 review holds at lower effort - medium is a future eval candidate; the pin stays high until measured. |
| Tests / builds (`test-runner`) | haiku | low | Running a command and summarizing output is mechanical; the value is keeping raw logs out of the main context, not the model doing it. | Low: no reasoning, just report. |
Expand Down Expand Up @@ -387,7 +387,7 @@ exact numbers live on
| ------ | ------------ | ------------------- |
| fable | ~2x | Frontier reasoning. A main-session choice for all-hard-reasoning days (architecture, subtle debugging hunts) - never a routing target. |
| opus | 1x | The escalation tier: code review, multi-file/cross-layer implementation, security/money/concurrency-sensitive changes. Opus 5 made this tier a step stronger at the same price. |
| sonnet | ~0.6x (~0.4x intro through 2026-08-31) | The workhorse: ordinary implementation from an approved plan, exploration, E2E driving. Near-opus on clear-shape coding. |
| sonnet | ~0.4x (permanent $2/$10) | The workhorse: ordinary implementation from an approved plan, exploration, E2E driving. Near-opus on clear-shape coding. |
| haiku | ~0.2x | Mechanical grind: test/build runs, diff sanity checks, trivial sweeps. |

Generation notes (as of the Opus 5 launch, July 2026 - these rot
Expand Down Expand Up @@ -629,7 +629,7 @@ Attribution comes from `agent-<id>.meta.json`, the sidecar Claude Code writes be
`tokens` also prices the volume it just counted, because tokens need translating and dollars do not. Sample output - the window rolls daily, so your figures will differ:

```text
At API list prices (rates as of 2026-08-11), this is what the volume above would have cost on the Claude API:
At API list prices (rates as of 2026-09-09), this is what the volume above would have cost on the Claude API:
as it ran $2,380
had every subagent inherited its session model $4,417
difference $2,037
Expand All @@ -638,7 +638,7 @@ At API list prices (rates as of 2026-08-11), this is what the volume above would

Read it as a counterfactual, which is exactly what it is: on a subscription you pay none of this, and the difference carries the same assumption as the volume figures above - that every subagent would otherwise have inherited the session model, which agents pinned by other plugins would not.

The arithmetic is per token type rather than per token, because they are priced an order of magnitude apart: base input at the model rate, cache reads at 0.1x, five-minute cache writes at 1.25x, one-hour writes at 2x, and output at its own rate. Cache reads dominate real transcripts, so a flat volume multiply would overstate the bill several times over. Transcripts break cache writes down by TTL; a line carrying only a total is charged at the cheaper five-minute rate.
The arithmetic is per token type rather than per token, because they are priced an order of magnitude apart: base input at the model rate, cache reads at 0.1x (0.025x on Fable 5.1 and Mythos 5.1), five-minute cache writes at 1.25x, one-hour writes at 2x, and output at its own rate. Cache reads dominate real transcripts, so a flat volume multiply would overstate the bill several times over. Transcripts break cache writes down by TTL; a line carrying only a total is charged at the cheaper five-minute rate.

Several documented modifiers push these figures DOWNWARD, so treat them as conservative rather than optimistic. Models from Opus 4.7, Sonnet 5 and Fable 5 onward use a tokenizer producing roughly 30% more tokens for the same text than Sonnet 4.6 and earlier, so re-pricing a cheap model's token count at an expensive model's rate understates what that work would really have cost there. Nothing in a usage line reveals whether fast mode (double rates on Opus 5 and Opus 4.8) or US-only inference (1.1x from 4.6 onward) applied, so neither is added. And a model absent from the price table is excluded from every figure and declared on its own line, never counted as free - a window where nothing can be priced prints that declaration instead of figures, because silence would read as "this cost nothing".

Expand Down
31 changes: 15 additions & 16 deletions hooks/dispatch-counter.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -282,44 +282,45 @@ const shortModel = (m) => m ? m.replace(/^claude-/, "").replace(/-\d{8}$/, "") :
// (Opus 4.1 bills at three times Opus 4.5) and a loose pattern would quietly
// misprice a retired model. A model absent from this table is reported as
// unpriced volume, never as zero.
const PRICES_ASOF = "2026-08-11";
const SONNET5_STANDARD_FROM = Date.parse("2026-09-01T00:00:00Z");
const PRICES_ASOF = "2026-09-09";
const PRICES = [
// Retired families first: a looser pattern below must not claim them.
[/opus-4-1-|opus-4-20/, () => [15, 75]],
// Pre-4.x ids put the generation first (claude-3-5-haiku-...), so both orders
// are matched; the row above needs the same trick for claude-opus-4-20250514.
[/3-5-haiku|haiku-3-5/, () => [0.8, 4]],
// 5.1 cache reads at 0.025x base input, ahead of the looser 5.x row below.
[/fable-5-1|mythos-5-1/, () => [10, 50, 0.025]],
[/fable-5|mythos-5/, () => [10, 50]],
[/opus-5|opus-4-8|opus-4-7|opus-4-6|opus-4-5/, () => [5, 25]],
// The one model on the page whose price changes on a date rather than with a
// new id: introductory $2/$10 through 2026-08-31, standard $3/$15 after.
[/sonnet-5/, (at) => (at < SONNET5_STANDARD_FROM ? [2, 10] : [3, 15])],
// Permanent $2/$10: the scheduled increase to $3/$15 on 2026-09-01 was
// called off.
[/sonnet-5/, () => [2, 10]],
[/sonnet-4-6|sonnet-4-5|sonnet-4-20/, () => [3, 15]],
[/haiku-4-5/, () => [1, 5]],
];
// Prompt-caching multipliers, quoted from the same page: a 5-minute cache write
// costs 1.25x base input, a 1-hour write 2x, and a cache read 0.1x. Transcripts
// break cache writes down by TTL, so no averaging is needed.
// costs 1.25x base input, a 1-hour write 2x. Cache reads are 0.1x base input
// for every model except where the page states otherwise (Fable 5.1 and
// Mythos 5.1 read at 0.025x - the row's third element overrides CACHE_READ).
const CACHE_WRITE_5M = 1.25, CACHE_WRITE_1H = 2, CACHE_READ = 0.1;

// Billable input volume: everything the model read, however it was cached.
// Output is counted separately because it prices an order of magnitude higher.
const volOf = (v) => v.in + v.cr + v.cw5 + v.cw1h;

// Dollars for one model's token counts, or null when the model is not on the
// price table. `at` is the instant the rates are taken from - the price of a
// window, not of today, since one model's rate changes on a date inside the
// horizon these reports can cover.
// price table. `at` is kept as the hook for a future dated rate; no row on
// the table uses it today.
function costOf(model, v, at) {
const row = model ? PRICES.find(([re]) => re.test(model)) : null;
if (!row) return null;
const [inRate, outRate] = row[1](at);
const [inRate, outRate, crMul = CACHE_READ] = row[1](at);
const perTok = inRate / 1e6;
return (v.in * perTok)
+ (v.cw5 * perTok * CACHE_WRITE_5M)
+ (v.cw1h * perTok * CACHE_WRITE_1H)
+ (v.cr * perTok * CACHE_READ)
+ (v.cr * perTok * crMul)
+ (v.out * outRate / 1e6);
}

Expand Down Expand Up @@ -647,10 +648,8 @@ if (process.argv[2] === "tokens") {
const perAgent = new Map();
let metaless = 0; // agent transcripts whose sidecar named no type
let unknownAgents = 0, unknownVol = 0; // models tierOf cannot rank
// Cost accounting. The rate epoch is the END of the window, not "now", so a
// historical window is priced at the rates that applied to it - one model on
// the price table changes rate on a date that falls inside the horizon these
// windows can reach.
// Cost accounting. priceAt is kept as the hook for a future dated rate; no
// row on the table uses it today.
const priceAt = win.end;
let costRan = 0, costInherited = 0, unpricedVol = 0, unpricedSessionVol = 0;
let mainCost = 0, mainUnpricedVol = 0;
Expand Down
16 changes: 16 additions & 0 deletions hooks/dispatch-counter.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -1025,6 +1025,22 @@ test("cost prices every token type at its own documented rate", () => {
} finally { rmSync(cfg, { recursive: true, force: true }); }
});

test("fable-5.1 cache reads price at 0.025x, sonnet-5 stays at $2/$10", () => {
const cfg = freshConfigDir();
const dir = join(cfg, "projects", "proj", "sess-1", "subagents");
mkdirSync(dir, { recursive: true });
writeFileSync(join(cfg, "projects", "proj", "sess-1.jsonl"), usageLine("claude-opus-5", 1) + "\n");
// Fable 5.1 cache read: 1M x 0.025 x $10/MTok = $0.25 (vs $1.00 at the 0.1x
// every other model gets). Sonnet 5: 1M base input x $2/MTok = $2.00, the
// permanent rate - the scheduled 2026-09-01 increase to $3/$15 was called off.
writeFileSync(join(dir, "agent-a.jsonl"), costLine("claude-fable-5-1", { cacheRead: 1e6 }) + "\n");
writeFileSync(join(dir, "agent-b.jsonl"), costLine("claude-sonnet-5", { input: 1e6 }) + "\n");
try {
const out = run(["tokens"], cfg);
assert.match(out, /as it ran\s+\$2\.25/);
} finally { rmSync(cfg, { recursive: true, force: true }); }
});

test("a cache-write bucket this code has never heard of is still charged", () => {
const cfg = freshConfigDir();
const dir = join(cfg, "projects", "proj", "sess-1", "subagents");
Expand Down
6 changes: 3 additions & 3 deletions skills/model-routing/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -135,9 +135,9 @@ actually earns its cost:
which stays in the subagent regardless of tier.
- **Ordinary implementation -> sonnet/medium.** Sonnet is near-opus
quality on single-file, clear-shape coding at a fraction of the price
(as of the Opus 5 launch, July 2026: sonnet runs at intro pricing
through 2026-08-31, ~2.5x cheaper than opus). For work whose approach
the plan already decided, that margin does not change the outcome, so
(sonnet is permanently priced at $2/$10, ~2.5x cheaper than opus). For
work whose approach the plan already decided, that margin does not
change the outcome, so
sonnet stays the value default. Medium effort because the agent
executes, it does not design.
- **Complex implementation -> opus, still at the agent's pinned medium**
Expand Down
Loading