pan-wizard 3.30.0 → 3.32.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +30 -26
- package/agents/pan-design-checker.md +18 -0
- package/agents/pan-optimizer.md +5 -1
- package/agents/pan-plan-checker.md +23 -0
- package/agents/pan-reviewer.md +19 -0
- package/agents/pan-verifier.md +23 -0
- package/bin/install-lib.cjs +129 -33
- package/bin/install.js +16 -9
- package/commands/pan/army.md +4 -4
- package/commands/pan/design-phase.md +8 -0
- package/commands/pan/exec-phase.md +1 -1
- package/commands/pan/git.md +2 -2
- package/commands/pan/learn.md +2 -0
- package/commands/pan/optimize.md +14 -2
- package/commands/pan/review-deep.md +5 -5
- package/commands/pan/verify-phase.md +8 -9
- package/hooks/dist/pan-check-update.js +53 -2
- package/hooks/dist/pan-context-monitor.js +42 -1
- package/hooks/dist/pan-cost-logger.js +132 -11
- package/hooks/dist/pan-state-reinject.js +158 -0
- package/hooks/dist/pan-stop-guard.js +50 -4
- package/hooks/dist/pan-trace-logger.js +324 -18
- package/package.json +1 -1
- package/pan-wizard-core/bin/lib/commands.cjs +22 -5
- package/pan-wizard-core/bin/lib/config.cjs +2 -0
- package/pan-wizard-core/bin/lib/constants.cjs +31 -1
- package/pan-wizard-core/bin/lib/context-budget.cjs +27 -2
- package/pan-wizard-core/bin/lib/core.cjs +232 -23
- package/pan-wizard-core/bin/lib/cost-rebuild.cjs +11 -0
- package/pan-wizard-core/bin/lib/cost.cjs +135 -46
- package/pan-wizard-core/bin/lib/findings.cjs +381 -0
- package/pan-wizard-core/bin/lib/focus.cjs +72 -19
- package/pan-wizard-core/bin/lib/foreign-planning.cjs +24 -6
- package/pan-wizard-core/bin/lib/frontmatter.cjs +5 -2
- package/pan-wizard-core/bin/lib/hygiene.cjs +12 -2
- package/pan-wizard-core/bin/lib/init.cjs +4 -1
- package/pan-wizard-core/bin/lib/memory-optimize.cjs +12 -6
- package/pan-wizard-core/bin/lib/memory-rebuild.cjs +9 -2
- package/pan-wizard-core/bin/lib/optimize.cjs +349 -21
- package/pan-wizard-core/bin/lib/phase-report.cjs +4 -1
- package/pan-wizard-core/bin/lib/review-deep.cjs +36 -0
- package/pan-wizard-core/bin/lib/roadmap.cjs +87 -18
- package/pan-wizard-core/bin/lib/state.cjs +93 -24
- package/pan-wizard-core/bin/lib/suggest.cjs +21 -9
- package/pan-wizard-core/bin/lib/template.cjs +6 -3
- package/pan-wizard-core/bin/lib/verdict.cjs +431 -0
- package/pan-wizard-core/bin/lib/verify-deploy.cjs +19 -2
- package/pan-wizard-core/bin/lib/verify-preflight.cjs +4 -2
- package/pan-wizard-core/bin/lib/verify-scope.cjs +156 -0
- package/pan-wizard-core/bin/lib/verify.cjs +25 -2
- package/pan-wizard-core/bin/pan-tools.cjs +69 -6
- package/pan-wizard-core/learnings/README.md +10 -7
- package/pan-wizard-core/mcp/tool-registry.cjs +16 -4
- package/pan-wizard-core/references/verdict-contract.md +101 -0
- package/pan-wizard-core/templates/verification-report.md +16 -0
- package/pan-wizard-core/workflows/exec-phase.md +73 -47
- package/pan-wizard-core/workflows/health.md +2 -0
- package/pan-wizard-core/workflows/help.md +2 -2
- package/pan-wizard-core/workflows/learn.md +4 -2
- package/pan-wizard-core/workflows/milestone-audit.md +15 -3
- package/pan-wizard-core/workflows/optimize.md +14 -1
- package/pan-wizard-core/workflows/phase-tests.md +1 -1
- package/pan-wizard-core/workflows/plan-phase.md +20 -15
- package/pan-wizard-core/workflows/progress.md +1 -1
- package/pan-wizard-core/workflows/todo-add.md +1 -1
- package/pan-wizard-core/workflows/todo-check.md +7 -6
- package/pan-wizard-core/workflows/verify-phase.md +3 -13
- package/pan-zcode/README.md +3 -3
- package/scripts/build-agent-plugin.js +4 -0
- package/scripts/build-hooks.js +2 -1
- package/scripts/build-plugin.js +10 -0
- package/scripts/git-hooks/pre-commit +2 -1
- package/scripts/npm-dist-tag.js +47 -0
- package/scripts/plugin-path.js +6 -1
- package/scripts/test-surface.cjs +1 -0
|
@@ -46,24 +46,32 @@ const TOKENS_FILE = 'tokens.jsonl';
|
|
|
46
46
|
/**
|
|
47
47
|
* Default rate table ($ per million tokens).
|
|
48
48
|
* Override per-model in config.json → cost.rates.
|
|
49
|
+
*
|
|
50
|
+
* `cache_write` is the five-minute cache write. `cache_write_1h` — Anthropic rows
|
|
51
|
+
* only — is the one-hour write, 2x base input on every current Claude model
|
|
52
|
+
* (pricing table "1h cache writes", read 2026-09-26). It prices the
|
|
53
|
+
* `cache_write_1h_tokens` a ledger row carries when the transcript recorded the
|
|
54
|
+
* lifetime split; a row without it, or a rate without `cache_write_1h`, bills every
|
|
55
|
+
* write at `cache_write` (market-ideas queue M13). OpenAI and Google report no
|
|
56
|
+
* lifetime split, so their rows carry none.
|
|
49
57
|
*/
|
|
50
58
|
const DEFAULT_RATES = {
|
|
51
59
|
// Anthropic — platform.claude.com/docs/en/about-claude/pricing, every row read
|
|
52
60
|
// 2026-09-23. Opus 4.6+/Opus 5 are $5/$25 (the old $15/$75 Opus pricing ended with
|
|
53
61
|
// the 4.5 generation). cache_write is the 5-minute write rate (1.25× input; the
|
|
54
|
-
// 1-hour write bills 2
|
|
62
|
+
// 1-hour write bills 2× and is priced from cache_write_1h, see above). Cache reads follow
|
|
55
63
|
// the 0.1× convention EXCEPT on three models the page footnotes: Fable 5.1 and
|
|
56
64
|
// Mythos 5.1 at 0.025× input, Opus 5.5 at 0.05×. Each needs its own row, because
|
|
57
65
|
// the family-prefix fallback would otherwise price it at its predecessor's reads.
|
|
58
66
|
// Fable 5.1 is what the `fable`/`best` aliases resolve to (model-config: neither
|
|
59
67
|
// Fable model is any plan's default); cached re-reads are the bulk of PAN's
|
|
60
68
|
// traffic (ADR-0044).
|
|
61
|
-
'claude-fable-5-1': { input: 10.0, output: 50.0, cache_read: 0.25, cache_write: 12.5 },
|
|
62
|
-
'claude-fable-5': { input: 10.0, output: 50.0, cache_read: 1.0, cache_write: 12.5 },
|
|
69
|
+
'claude-fable-5-1': { input: 10.0, output: 50.0, cache_read: 0.25, cache_write: 12.5, cache_write_1h: 20.0 },
|
|
70
|
+
'claude-fable-5': { input: 10.0, output: 50.0, cache_read: 1.0, cache_write: 12.5, cache_write_1h: 20.0 },
|
|
63
71
|
// Mythos 5.1 / Mythos 5 (limited availability): $10/$50; Mythos 5.1 reads at
|
|
64
72
|
// 0.025× input like Fable 5.1, Mythos 5 follows the 0.1× rule (R7).
|
|
65
|
-
'claude-mythos-5-1': { input: 10.0, output: 50.0, cache_read: 0.25, cache_write: 12.5 },
|
|
66
|
-
'claude-mythos-5': { input: 10.0, output: 50.0, cache_read: 1.0, cache_write: 12.5 },
|
|
73
|
+
'claude-mythos-5-1': { input: 10.0, output: 50.0, cache_read: 0.25, cache_write: 12.5, cache_write_1h: 20.0 },
|
|
74
|
+
'claude-mythos-5': { input: 10.0, output: 50.0, cache_read: 1.0, cache_write: 12.5, cache_write_1h: 20.0 },
|
|
67
75
|
// Opus 5.5 (claude-opus-5-5, released 2026-09-22): $4 input / $20 output, 5-minute
|
|
68
76
|
// writes $5, cache hits $0.20 — the page prices its hits at 0.05× input. Claude
|
|
69
77
|
// Code 2.1.280 made it the default model on every plan (model-config, read
|
|
@@ -71,24 +79,24 @@ const DEFAULT_RATES = {
|
|
|
71
79
|
// prefix priced it as Opus 5: cache reads 2.5× high, the rest a quarter high
|
|
72
80
|
// (reality check 2026-09-22, R25). The models overview lists no dated snapshot —
|
|
73
81
|
// from the 4.6 generation on, the dateless id is canonical.
|
|
74
|
-
'claude-opus-5-5': { input: 4.0, output: 20.0, cache_read: 0.20, cache_write: 5.0 },
|
|
75
|
-
'claude-opus-5': { input: 5.0, output: 25.0, cache_read: 0.5, cache_write: 6.25 },
|
|
76
|
-
'claude-opus-4-8': { input: 5.0, output: 25.0, cache_read: 0.5, cache_write: 6.25 },
|
|
77
|
-
'claude-opus-4-7': { input: 5.0, output: 25.0, cache_read: 0.5, cache_write: 6.25 },
|
|
78
|
-
'claude-opus-4-6': { input: 5.0, output: 25.0, cache_read: 0.5, cache_write: 6.25 },
|
|
82
|
+
'claude-opus-5-5': { input: 4.0, output: 20.0, cache_read: 0.20, cache_write: 5.0, cache_write_1h: 8.0 },
|
|
83
|
+
'claude-opus-5': { input: 5.0, output: 25.0, cache_read: 0.5, cache_write: 6.25, cache_write_1h: 10.0 },
|
|
84
|
+
'claude-opus-4-8': { input: 5.0, output: 25.0, cache_read: 0.5, cache_write: 6.25, cache_write_1h: 10.0 },
|
|
85
|
+
'claude-opus-4-7': { input: 5.0, output: 25.0, cache_read: 0.5, cache_write: 6.25, cache_write_1h: 10.0 },
|
|
86
|
+
'claude-opus-4-6': { input: 5.0, output: 25.0, cache_read: 0.5, cache_write: 6.25, cache_write_1h: 10.0 },
|
|
79
87
|
// Opus 4.5 (dated id claude-opus-4-5-20251101): without this row the dated id had
|
|
80
88
|
// no family prefix to land on and priced as null (R7).
|
|
81
|
-
'claude-opus-4-5': { input: 5.0, output: 25.0, cache_read: 0.5, cache_write: 6.25 },
|
|
89
|
+
'claude-opus-4-5': { input: 5.0, output: 25.0, cache_read: 0.5, cache_write: 6.25, cache_write_1h: 10.0 },
|
|
82
90
|
// Sonnet 5 is $2/$10: the launch price announced as introductory through
|
|
83
91
|
// 2026-08-31 was made permanent and the scheduled rise to $3/$15 cancelled.
|
|
84
92
|
// Lesson: never write down a pre-announced price — this row carried the future
|
|
85
93
|
// rate for a month and over-billed by half.
|
|
86
|
-
'claude-sonnet-5': { input: 2.0, output: 10.0, cache_read: 0.20, cache_write: 2.50 },
|
|
87
|
-
'claude-sonnet-4-6': { input: 3.0, output: 15.0, cache_read: 0.3, cache_write: 3.75 },
|
|
94
|
+
'claude-sonnet-5': { input: 2.0, output: 10.0, cache_read: 0.20, cache_write: 2.50, cache_write_1h: 4.0 },
|
|
95
|
+
'claude-sonnet-4-6': { input: 3.0, output: 15.0, cache_read: 0.3, cache_write: 3.75, cache_write_1h: 6.0 },
|
|
88
96
|
// Sonnet 4.5 (dated id claude-sonnet-4-5-20250929): $3/$15/$0.30/$3.75 (R7).
|
|
89
|
-
'claude-sonnet-4-5': { input: 3.0, output: 15.0, cache_read: 0.3, cache_write: 3.75 },
|
|
97
|
+
'claude-sonnet-4-5': { input: 3.0, output: 15.0, cache_read: 0.3, cache_write: 3.75, cache_write_1h: 6.0 },
|
|
90
98
|
// Haiku 4.5: API id claude-haiku-4-5-20251001, alias claude-haiku-4-5.
|
|
91
|
-
'claude-haiku-4-5': { input: 1.0, output: 5.0, cache_read: 0.1, cache_write: 1.25 },
|
|
99
|
+
'claude-haiku-4-5': { input: 1.0, output: 5.0, cache_read: 0.1, cache_write: 1.25, cache_write_1h: 2.0 },
|
|
92
100
|
|
|
93
101
|
// OpenAI — developers.openai.com/api/docs/pricing, Standard tier, short context
|
|
94
102
|
// (≤272K input tokens), read 2026-09-23. On the GPT-5.6 and GPT-6 families a cache
|
|
@@ -149,9 +157,9 @@ const DEFAULT_RATES = {
|
|
|
149
157
|
// (Opus 5.5 since 2026-09-22 — the model the inherit tier runs on for most users),
|
|
150
158
|
// mid the Sonnet the `sonnet` alias resolves to on the Anthropic API (Sonnet 5),
|
|
151
159
|
// fast Haiku 4.5.
|
|
152
|
-
'reasoning': { input: 4.0, output: 20.0, cache_read: 0.20, cache_write: 5.0 },
|
|
153
|
-
'mid': { input: 2.0, output: 10.0, cache_read: 0.20, cache_write: 2.50 },
|
|
154
|
-
'fast': { input: 1.0, output: 5.0, cache_read: 0.1, cache_write: 1.25 },
|
|
160
|
+
'reasoning': { input: 4.0, output: 20.0, cache_read: 0.20, cache_write: 5.0, cache_write_1h: 8.0 },
|
|
161
|
+
'mid': { input: 2.0, output: 10.0, cache_read: 0.20, cache_write: 2.50, cache_write_1h: 4.0 },
|
|
162
|
+
'fast': { input: 1.0, output: 5.0, cache_read: 0.1, cache_write: 1.25, cache_write_1h: 2.0 },
|
|
155
163
|
};
|
|
156
164
|
|
|
157
165
|
function metricsDir(cwd) {
|
|
@@ -175,29 +183,80 @@ function familyPrefixRate(rates, model) {
|
|
|
175
183
|
|
|
176
184
|
// ─── Claude Code `modelPricing` as a rate source (2026-09) ──────────────────
|
|
177
185
|
//
|
|
178
|
-
// Claude Code ≥2.1.
|
|
179
|
-
// MANAGED
|
|
180
|
-
//
|
|
181
|
-
//
|
|
182
|
-
//
|
|
183
|
-
//
|
|
184
|
-
//
|
|
185
|
-
//
|
|
186
|
+
// Claude Code ≥2.1.242 lets an organisation report spend at contracted rates
|
|
187
|
+
// through a MANAGED setting, and prices its own /usage, status line and
|
|
188
|
+
// telemetry with it. Honouring the same block keeps PAN's ledger on the numbers
|
|
189
|
+
// the organisation actually pays. The documented shape (settings-reference
|
|
190
|
+
// `modelPricing`, read raw 2026-09-26):
|
|
191
|
+
//
|
|
192
|
+
// modelPricing: {
|
|
193
|
+
// multiplier: 0.85, // optional, > 0 and ≤ 10
|
|
194
|
+
// overrides: { // optional
|
|
195
|
+
// "claude-sonnet-4-6": { input, output, cacheRead, cacheWrite } // $/1M, each 0–10000, all four required
|
|
196
|
+
// }
|
|
197
|
+
// }
|
|
198
|
+
//
|
|
199
|
+
// `multiplier` "scales every cost Claude Code computes, whether or not an
|
|
200
|
+
// overrides row covers it" — below 1 a discount, above 1 a chargeback markup
|
|
201
|
+
// (2.1.271). It is applied on top of an override row's rates, and to the list
|
|
202
|
+
// price of every model no row covers. Claude Code drops a row or a multiplier it
|
|
203
|
+
// cannot parse and keeps the rest; PAN does the same.
|
|
204
|
+
//
|
|
205
|
+
// Until 2026-09-26 PAN parsed a different shape — `{ "<modelId>":
|
|
206
|
+
// { inputCostPer1MTokens, outputCostPer1MTokens } }` with cache rates derived —
|
|
207
|
+
// that the documentation does not describe; a real policy file matched nothing
|
|
208
|
+
// and PAN priced at list price. (Market-ideas queue M2 found it while adding the
|
|
209
|
+
// multiplier.)
|
|
186
210
|
|
|
187
211
|
const round6 = (n) => Number(n.toFixed(6));
|
|
188
212
|
|
|
213
|
+
const MODEL_PRICING_MAX_MULTIPLIER = 10;
|
|
214
|
+
const MODEL_PRICING_MAX_RATE = 10000;
|
|
215
|
+
|
|
216
|
+
// resolveRate reads this off a rates map to scale its built-in fallback. A
|
|
217
|
+
// symbol, so Object.keys (the family-prefix match, `models check`) never sees it.
|
|
218
|
+
const BUILTIN_MULTIPLIER = Symbol('pan.builtinRateMultiplier');
|
|
219
|
+
|
|
220
|
+
/** The block's multiplier when it is a valid one, otherwise null. */
|
|
221
|
+
function modelPricingMultiplier(modelPricing) {
|
|
222
|
+
if (!modelPricing || typeof modelPricing !== 'object' || !('multiplier' in modelPricing)) return null;
|
|
223
|
+
const raw = modelPricing.multiplier;
|
|
224
|
+
if (raw === null || raw === '' || typeof raw === 'boolean') return null;
|
|
225
|
+
const m = Number(raw);
|
|
226
|
+
return Number.isFinite(m) && m > 0 && m <= MODEL_PRICING_MAX_MULTIPLIER ? m : null;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
const scaleRate = (rate, m) => ({
|
|
230
|
+
input: round6(rate.input * m),
|
|
231
|
+
output: round6(rate.output * m),
|
|
232
|
+
cache_read: round6(rate.cache_read * m),
|
|
233
|
+
cache_write: round6(rate.cache_write * m),
|
|
234
|
+
...(typeof rate.cache_write_1h === 'number' ? { cache_write_1h: round6(rate.cache_write_1h * m) } : {}),
|
|
235
|
+
});
|
|
236
|
+
|
|
237
|
+
/**
|
|
238
|
+
* PAN rate rows for a managed `modelPricing` block's `overrides`, with the
|
|
239
|
+
* block's multiplier applied. A row missing any of the four rates, or holding
|
|
240
|
+
* one outside 0–10000, is dropped.
|
|
241
|
+
* @param {object|null} modelPricing - `{ multiplier?, overrides? }`
|
|
242
|
+
* @returns {Object<string, {input:number, output:number, cache_read:number, cache_write:number}>}
|
|
243
|
+
*/
|
|
189
244
|
function ratesFromModelPricing(modelPricing) {
|
|
190
245
|
const out = {};
|
|
191
246
|
if (!modelPricing || typeof modelPricing !== 'object' || Array.isArray(modelPricing)) return out;
|
|
192
|
-
|
|
247
|
+
const overrides = modelPricing.overrides;
|
|
248
|
+
if (!overrides || typeof overrides !== 'object' || Array.isArray(overrides)) return out;
|
|
249
|
+
const m = modelPricingMultiplier(modelPricing) || 1;
|
|
250
|
+
const valid = (v) => v !== null && v !== '' && typeof v !== 'boolean'
|
|
251
|
+
&& Number.isFinite(Number(v)) && Number(v) >= 0 && Number(v) <= MODEL_PRICING_MAX_RATE;
|
|
252
|
+
for (const [id, p] of Object.entries(overrides)) {
|
|
193
253
|
if (!p || typeof p !== 'object') continue;
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
out[id] = { input, output, cache_read: round6(input * readMult), cache_write: round6(input * writeMult) };
|
|
254
|
+
if (![p.input, p.output, p.cacheRead, p.cacheWrite].every(valid)) continue;
|
|
255
|
+
// "cacheWrite covers both five-minute and one-hour cache writes."
|
|
256
|
+
out[id] = scaleRate({
|
|
257
|
+
input: Number(p.input), output: Number(p.output),
|
|
258
|
+
cache_read: Number(p.cacheRead), cache_write: Number(p.cacheWrite), cache_write_1h: Number(p.cacheWrite),
|
|
259
|
+
}, m);
|
|
201
260
|
}
|
|
202
261
|
return out;
|
|
203
262
|
}
|
|
@@ -229,25 +288,35 @@ function loadManagedModelPricing(dir = managedSettingsDir()) {
|
|
|
229
288
|
.sort()
|
|
230
289
|
.map(f => path.join(dropIns, f)));
|
|
231
290
|
} catch { /* no drop-in directory */ }
|
|
291
|
+
// Later files win: a later `multiplier` replaces an earlier one, and
|
|
292
|
+
// `overrides` merge per model id.
|
|
232
293
|
let merged = null;
|
|
233
294
|
for (const f of files) {
|
|
234
295
|
let parsed;
|
|
235
296
|
try { parsed = JSON.parse(fs.readFileSync(f, 'utf8')); } catch { continue; }
|
|
236
297
|
const mp = parsed && typeof parsed === 'object' ? parsed.modelPricing : null;
|
|
237
|
-
if (mp
|
|
298
|
+
if (!mp || typeof mp !== 'object' || Array.isArray(mp)) continue;
|
|
299
|
+
merged = merged || {};
|
|
300
|
+
if ('multiplier' in mp) merged.multiplier = mp.multiplier;
|
|
301
|
+
if (mp.overrides && typeof mp.overrides === 'object' && !Array.isArray(mp.overrides)) {
|
|
302
|
+
merged.overrides = { ...(merged.overrides || {}), ...mp.overrides };
|
|
303
|
+
}
|
|
238
304
|
}
|
|
239
305
|
return merged;
|
|
240
306
|
}
|
|
241
307
|
|
|
242
308
|
// The rate table a cost computation actually sees. Precedence, highest first:
|
|
243
|
-
// 1. `.planning/config.json → cost.rates` — PAN's explicit per-project override
|
|
244
|
-
//
|
|
245
|
-
//
|
|
246
|
-
//
|
|
247
|
-
//
|
|
309
|
+
// 1. `.planning/config.json → cost.rates` — PAN's explicit per-project override,
|
|
310
|
+
// used exactly as written (the managed multiplier does not touch it)
|
|
311
|
+
// 2. managed `modelPricing.overrides` — contracted rates, × the multiplier
|
|
312
|
+
// 3. DEFAULT_RATES — resolveRate's own fallback, × the managed multiplier
|
|
313
|
+
// Returns undefined (not {}) when nothing overrides and no multiplier applies, so
|
|
314
|
+
// callers keep the exact pre-2026-09 behaviour of passing no config rates.
|
|
248
315
|
function effectiveRates(config, managedPricing = loadManagedModelPricing()) {
|
|
249
316
|
const rates = { ...ratesFromModelPricing(managedPricing), ...(config?.cost?.rates || {}) };
|
|
250
|
-
|
|
317
|
+
const m = modelPricingMultiplier(managedPricing);
|
|
318
|
+
if (m !== null && m !== 1) rates[BUILTIN_MULTIPLIER] = m;
|
|
319
|
+
return Object.keys(rates).length > 0 || rates[BUILTIN_MULTIPLIER] ? rates : undefined;
|
|
251
320
|
}
|
|
252
321
|
|
|
253
322
|
function resolveRate(model, tier, configRates) {
|
|
@@ -264,6 +333,12 @@ function resolveRate(model, tier, configRates) {
|
|
|
264
333
|
}
|
|
265
334
|
if (tier && configRates[tier]) return configRates[tier];
|
|
266
335
|
}
|
|
336
|
+
const builtin = builtinRate(model, tier);
|
|
337
|
+
const m = configRates ? configRates[BUILTIN_MULTIPLIER] : undefined;
|
|
338
|
+
return builtin && m ? scaleRate(builtin, m) : builtin;
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
function builtinRate(model, tier) {
|
|
267
342
|
if (model && DEFAULT_RATES[model]) return DEFAULT_RATES[model];
|
|
268
343
|
if (model) {
|
|
269
344
|
const fam = familyPrefixRate(DEFAULT_RATES, model);
|
|
@@ -299,8 +374,13 @@ function computeCost(rec, configRates) {
|
|
|
299
374
|
// outright. On a realistic row (30k input / 5k output / 200k cache read / 12k
|
|
300
375
|
// cache write on Opus-5 rates) it reported $0.30 against a true $0.45 — a 33%
|
|
301
376
|
// understatement, always in the direction of looking cheaper.
|
|
302
|
-
|
|
303
|
-
|
|
377
|
+
// The one-hour share of the writes, when the row recorded the lifetime split (M13),
|
|
378
|
+
// bills at the one-hour rate; the rest — and every write on a row or rate without
|
|
379
|
+
// the split — at `cache_write`.
|
|
380
|
+
const write1h = Math.min(cacheWrite, Math.max(0, Number(rec.cache_write_1h_tokens) || 0));
|
|
381
|
+
const rate1h = typeof rate.cache_write_1h === 'number' ? rate.cache_write_1h : rate.cache_write;
|
|
382
|
+
const usd = (input * rate.input + output * rate.output + cacheRead * rate.cache_read
|
|
383
|
+
+ (cacheWrite - write1h) * rate.cache_write + write1h * rate1h) / 1_000_000;
|
|
304
384
|
return Math.round(usd * 10000) / 10000;
|
|
305
385
|
}
|
|
306
386
|
|
|
@@ -389,8 +469,8 @@ function readRecords(cwd) {
|
|
|
389
469
|
*/
|
|
390
470
|
// No single subagent runs for six hours — the longest native-workflow phase runs
|
|
391
471
|
// measured in the harness finish inside an hour — while a parent-transcript slice
|
|
392
|
-
// that spans a working day, or the idle night between two stops, does.
|
|
393
|
-
// the
|
|
472
|
+
// that spans a working day, or the idle night between two stops, does. The hooks
|
|
473
|
+
// write the span as measured; only this reader applies the rule. Calibrated on
|
|
394
474
|
// eleven field ledgers (2026-09): of the timed rows the old ratio rule flagged,
|
|
395
475
|
// 55 spanned under three hours and 12 spanned six to twenty-four — the latter all
|
|
396
476
|
// parent slices booked to a `general-purpose` or workflow subagent.
|
|
@@ -668,7 +748,16 @@ function checkRatesStaleness(now = new Date()) {
|
|
|
668
748
|
function cmdModelsCheck(raw) {
|
|
669
749
|
// Surface the managed rates too: an organisation that pins `modelPricing`
|
|
670
750
|
// should be able to see that PAN found the block, not infer it from totals.
|
|
671
|
-
const
|
|
751
|
+
const managed = loadManagedModelPricing();
|
|
752
|
+
const result = {
|
|
753
|
+
...checkRatesStaleness(),
|
|
754
|
+
managed_model_pricing: Object.keys(ratesFromModelPricing(managed)),
|
|
755
|
+
managed_pricing_multiplier: modelPricingMultiplier(managed),
|
|
756
|
+
};
|
|
757
|
+
// A multiplier PAN cannot apply is reported, not silently dropped.
|
|
758
|
+
if (managed && 'multiplier' in managed && result.managed_pricing_multiplier === null) {
|
|
759
|
+
result.managed_pricing_multiplier_ignored = managed.multiplier;
|
|
760
|
+
}
|
|
672
761
|
const human = result.stale
|
|
673
762
|
? `Rate table verified ${result.rates_verified_at} (${result.age_days} days ago) — STALE: re-verify provider pricing and bump RATES_VERIFIED_AT in cost.cjs`
|
|
674
763
|
: `Rate table verified ${result.rates_verified_at} (${result.age_days} days ago) — OK`;
|