@compr/opscontext-mcp 2.5.0 → 2.5.1
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/dist/cli.js +58 -20
- package/dist/default-pricing.d.ts +36 -0
- package/dist/default-pricing.js +57 -0
- package/dist/detector.js +3 -1
- package/dist/transcript-collector.d.ts +18 -0
- package/dist/transcript-collector.js +5 -0
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -602,7 +602,8 @@ import { activate, deactivate, getActivationStatus, gateCheck, } from "./activat
|
|
|
602
602
|
import { syncTierA, syncTierB, loadCommunityStore, communityRulesToChunks, mergeWithDedup, STORE_PATH as COMMUNITY_STORE_PATH, } from "./community-sync.js";
|
|
603
603
|
import { readAuditLog, verifyChain, filterByRange, toCsv, } from "./audit.js";
|
|
604
604
|
import { loadRepoPolicy, parsePolicy, formatPolicySummary, formatValidationErrors, repoPolicyPath, } from "./policy.js";
|
|
605
|
-
import { collectRuns, metricsFor, transcriptRoot, emptyTally, addTally, totalTokens, } from "./transcript-collector.js";
|
|
605
|
+
import { collectRuns, metricsFor, transcriptRoot, emptyTally, addTally, totalTokens, pricingStatus, pricingFor, } from "./transcript-collector.js";
|
|
606
|
+
import { DEFAULT_PRICING, DEFAULT_PRICING_ASOF } from "./default-pricing.js";
|
|
606
607
|
import { runTranscriptHeuristics, DEFAULT_COST_THRESHOLDS, } from "./detector.js";
|
|
607
608
|
import { getStagedFiles, runSecretScan, runDocCoverage, runCommitMessageRequired, runRuleParity, formatSecretViolations, formatDocCoverageViolations, formatSecretViolationsJson, formatDocCoverageViolationsJson, formatCommitMessageViolations, formatCommitMessageViolationsJson, formatRuleParityViolations, formatRuleParityViolationsJson, } from "./hooks.js";
|
|
608
609
|
import { safeAppend } from "./audit.js";
|
|
@@ -2289,20 +2290,27 @@ function loadCostThresholds(cwd) {
|
|
|
2289
2290
|
const res = loadRepoPolicy(cwd);
|
|
2290
2291
|
if (res && res.ok && res.policy.agent_cost) {
|
|
2291
2292
|
const a = res.policy.agent_cost;
|
|
2293
|
+
// [DEFAULT-RATES-SHIP-WITH-THE-PACKAGE] — an agent_cost block that omits
|
|
2294
|
+
// `pricing` must not silently price nothing.
|
|
2295
|
+
const hasOwnRates = a.pricing.length > 0;
|
|
2292
2296
|
return {
|
|
2293
2297
|
t: {
|
|
2294
2298
|
billing_mode: a.billing_mode,
|
|
2295
|
-
pricing: a.pricing,
|
|
2299
|
+
pricing: hasOwnRates ? a.pricing : DEFAULT_PRICING,
|
|
2296
2300
|
min_cache_efficiency: a.min_cache_efficiency,
|
|
2297
2301
|
max_tool_calls_per_agent: a.max_tool_calls_per_agent,
|
|
2298
2302
|
max_cost_per_agent_usd: a.max_cost_per_agent_usd,
|
|
2299
2303
|
min_fanout_for_canary: a.min_fanout_for_canary,
|
|
2300
2304
|
max_failed_share: a.max_failed_share,
|
|
2301
2305
|
},
|
|
2302
|
-
source: ".contextengine/policy.json"
|
|
2306
|
+
source: ".contextengine/policy.json" +
|
|
2307
|
+
(hasOwnRates ? "" : ` (rates: built-in, as of ${DEFAULT_PRICING_ASOF})`),
|
|
2303
2308
|
};
|
|
2304
2309
|
}
|
|
2305
|
-
return {
|
|
2310
|
+
return {
|
|
2311
|
+
t: DEFAULT_COST_THRESHOLDS,
|
|
2312
|
+
source: `built-in defaults, rates as of ${DEFAULT_PRICING_ASOF} (no agent_cost in policy.json)`,
|
|
2313
|
+
};
|
|
2306
2314
|
}
|
|
2307
2315
|
async function cliCost(argv) {
|
|
2308
2316
|
const flag = (name) => {
|
|
@@ -2355,7 +2363,17 @@ async function cliCost(argv) {
|
|
|
2355
2363
|
let vol = emptyTally();
|
|
2356
2364
|
let agents = 0, toolCalls = 0, failed = 0, capacity = 0, reported = 0;
|
|
2357
2365
|
let cost = 0, withoutCache = 0, unpriced = 0;
|
|
2366
|
+
// Which models carried tokens but matched no rate — named in the output so
|
|
2367
|
+
// the fix is actionable instead of "something was unpriced".
|
|
2368
|
+
const unpricedModels = new Set();
|
|
2358
2369
|
for (const { run, m } of scored) {
|
|
2370
|
+
for (const a of run.agents) {
|
|
2371
|
+
for (const [model, tally] of a.tokensByModel) {
|
|
2372
|
+
if (totalTokens(tally) > 0 && !pricingFor(model, t.pricing)) {
|
|
2373
|
+
unpricedModels.add(model ?? "(no model recorded)");
|
|
2374
|
+
}
|
|
2375
|
+
}
|
|
2376
|
+
}
|
|
2359
2377
|
vol = addTally(vol, run.totals);
|
|
2360
2378
|
agents += m.agents;
|
|
2361
2379
|
toolCalls += m.toolCalls;
|
|
@@ -2383,12 +2401,6 @@ async function cliCost(argv) {
|
|
|
2383
2401
|
console.log("");
|
|
2384
2402
|
// ── 2. VALUED COST ──────────────────────────────────────────────────────
|
|
2385
2403
|
const notional = t.billing_mode === "subscription";
|
|
2386
|
-
console.log(`VALUED COST (API list prices)${notional ? " — NOTIONAL, NOT BILLED" : ""}`);
|
|
2387
|
-
if (notional) {
|
|
2388
|
-
console.log(" This machine runs Claude Code on a subscription: no dollar below is");
|
|
2389
|
-
console.log(" debited. Use these figures to compare approaches, not as spend.");
|
|
2390
|
-
}
|
|
2391
|
-
const costRow = (label, n) => console.log(` ${label.padEnd(16)} ${("$" + n.toFixed(2)).padStart(8)} ${cost ? ((100 * n) / cost).toFixed(1).padStart(5) : " 0.0"}%`);
|
|
2392
2404
|
let ci = 0, ccw = 0, ccr = 0, co = 0;
|
|
2393
2405
|
for (const { m } of scored) {
|
|
2394
2406
|
ci += m.cost.input;
|
|
@@ -2396,16 +2408,42 @@ async function cliCost(argv) {
|
|
|
2396
2408
|
ccr += m.cost.cacheRead;
|
|
2397
2409
|
co += m.cost.output;
|
|
2398
2410
|
}
|
|
2399
|
-
|
|
2400
|
-
|
|
2401
|
-
|
|
2402
|
-
|
|
2403
|
-
|
|
2404
|
-
console.log(`
|
|
2405
|
-
|
|
2406
|
-
|
|
2407
|
-
|
|
2408
|
-
|
|
2411
|
+
const agg = {
|
|
2412
|
+
input: ci, cacheWrite: ccw, cacheRead: ccr, output: co,
|
|
2413
|
+
total: cost, withoutCache, unpricedTokens: unpriced,
|
|
2414
|
+
};
|
|
2415
|
+
const status = pricingStatus(agg);
|
|
2416
|
+
console.log(`VALUED COST (API list prices)${notional && status !== "unpriced" ? " — NOTIONAL, NOT BILLED" : ""}`);
|
|
2417
|
+
// [NEVER-RENDER-AN-UNKNOWN-AS-A-NUMBER] — with nothing priced there is no
|
|
2418
|
+
// cost to show. Printing a $0.00 table here reads as "this run was free"
|
|
2419
|
+
// and "caching saved 0%", both false.
|
|
2420
|
+
if (status === "unpriced") {
|
|
2421
|
+
console.log(` UNPRICED — no rate matched any model in this data, so no cost can be`);
|
|
2422
|
+
console.log(` stated. ${fmtTok(unpriced)} tokens were moved. This is an unknown, not $0.`);
|
|
2423
|
+
console.log("");
|
|
2424
|
+
console.log(` Models seen without a rate: ${[...unpricedModels].sort().join(", ") || "(unknown)"}`);
|
|
2425
|
+
console.log(` Add them to .contextengine/policy.json → agent_cost.pricing.`);
|
|
2426
|
+
console.log("");
|
|
2427
|
+
}
|
|
2428
|
+
else {
|
|
2429
|
+
if (notional) {
|
|
2430
|
+
console.log(" This machine runs Claude Code on a subscription: no dollar below is");
|
|
2431
|
+
console.log(" debited. Use these figures to compare approaches, not as spend.");
|
|
2432
|
+
}
|
|
2433
|
+
const costRow = (label, n) => console.log(` ${label.padEnd(16)} ${("$" + n.toFixed(2)).padStart(8)} ${cost ? ((100 * n) / cost).toFixed(1).padStart(5) : " 0.0"}%`);
|
|
2434
|
+
costRow("cache read", ccr);
|
|
2435
|
+
costRow("cache write", ccw);
|
|
2436
|
+
costRow("input (fresh)", ci);
|
|
2437
|
+
costRow("output", co);
|
|
2438
|
+
console.log(` ${"total".padEnd(16)} ${("$" + cost.toFixed(2)).padStart(8)}`);
|
|
2439
|
+
console.log(` ${"without cache".padEnd(16)} ${("$" + withoutCache.toFixed(2)).padStart(8)} ` +
|
|
2440
|
+
`caching saved $${(withoutCache - cost).toFixed(2)} (${withoutCache ? (100 * (1 - cost / withoutCache)).toFixed(0) : "0"}%)`);
|
|
2441
|
+
if (status === "partial") {
|
|
2442
|
+
console.log(` ⚠ ${fmtTok(unpriced)} tokens UNPRICED and NOT in the figures above` +
|
|
2443
|
+
` (${[...unpricedModels].sort().join(", ") || "unknown model"}) — the total is a floor, not the cost`);
|
|
2444
|
+
}
|
|
2445
|
+
console.log("");
|
|
2446
|
+
}
|
|
2409
2447
|
// ── 3. INTENSITY (the capacity proxy) ───────────────────────────────────
|
|
2410
2448
|
console.log(`INTENSITY (capacity proxy${notional ? " — the scarce resource here" : ""})`);
|
|
2411
2449
|
console.log(` subagents ${String(agents).padStart(8)}`);
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Built-in model rates, in dollars per million tokens.
|
|
3
|
+
*
|
|
4
|
+
* 🔒 LOCKED [DEFAULT-RATES-SHIP-WITH-THE-PACKAGE] — 2026-08-20
|
|
5
|
+
* ⛔ NEVER ship an empty default pricing table again.
|
|
6
|
+
* WHY: `[PRICING-LIVES-IN-POLICY]` was read as "ship no rates at all", so
|
|
7
|
+
* 2.5.0 shipped `pricing: []` as the default. Every user without an
|
|
8
|
+
* `agent_cost` block in their own policy.json got a VALUED COST panel
|
|
9
|
+
* reading `total $0.00` and `caching saved $0.00 (0%)` over 1.08 BILLION
|
|
10
|
+
* real tokens — a confident, wrong-looking verdict on the headline feature
|
|
11
|
+
* of the release. The LOCK's intent was "rates must be correctable without
|
|
12
|
+
* a release", not "the product ships priced at nothing".
|
|
13
|
+
* FIX: ship rates here, as DATA in their own module, never inline in the
|
|
14
|
+
* collector / detector / CLI. `.contextengine/policy.json` →
|
|
15
|
+
* `agent_cost.pricing` still wins outright when present, and this file
|
|
16
|
+
* compiles to plain readable JS in `dist/`, so a rate can be corrected in
|
|
17
|
+
* place without waiting for a release.
|
|
18
|
+
*
|
|
19
|
+
* Rates are Anthropic API list prices. Cache read is 0.1x input, cache write
|
|
20
|
+
* 5m is 1.25x input, cache write 1h is 2x input.
|
|
21
|
+
*/
|
|
22
|
+
import type { ModelPricing } from "./transcript-collector.js";
|
|
23
|
+
/**
|
|
24
|
+
* When these rates were last checked against published pricing. Surfaced in
|
|
25
|
+
* `contextengine cost` output: a rate table with no date is a rate table
|
|
26
|
+
* nobody knows to distrust.
|
|
27
|
+
*/
|
|
28
|
+
export declare const DEFAULT_PRICING_ASOF = "2026-08-20";
|
|
29
|
+
/**
|
|
30
|
+
* Longest-prefix matched, so dated ids (`claude-haiku-4-5-20251001`) resolve
|
|
31
|
+
* to their family. Deliberately NO `*` catch-all: a model absent from this
|
|
32
|
+
* table must report as UNPRICED, never be valued at a guessed rate
|
|
33
|
+
* (`[ABSENCE-IS-NOT-A-VERDICT]`).
|
|
34
|
+
*/
|
|
35
|
+
export declare const DEFAULT_PRICING: ModelPricing[];
|
|
36
|
+
//# sourceMappingURL=default-pricing.d.ts.map
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Built-in model rates, in dollars per million tokens.
|
|
3
|
+
*
|
|
4
|
+
* 🔒 LOCKED [DEFAULT-RATES-SHIP-WITH-THE-PACKAGE] — 2026-08-20
|
|
5
|
+
* ⛔ NEVER ship an empty default pricing table again.
|
|
6
|
+
* WHY: `[PRICING-LIVES-IN-POLICY]` was read as "ship no rates at all", so
|
|
7
|
+
* 2.5.0 shipped `pricing: []` as the default. Every user without an
|
|
8
|
+
* `agent_cost` block in their own policy.json got a VALUED COST panel
|
|
9
|
+
* reading `total $0.00` and `caching saved $0.00 (0%)` over 1.08 BILLION
|
|
10
|
+
* real tokens — a confident, wrong-looking verdict on the headline feature
|
|
11
|
+
* of the release. The LOCK's intent was "rates must be correctable without
|
|
12
|
+
* a release", not "the product ships priced at nothing".
|
|
13
|
+
* FIX: ship rates here, as DATA in their own module, never inline in the
|
|
14
|
+
* collector / detector / CLI. `.contextengine/policy.json` →
|
|
15
|
+
* `agent_cost.pricing` still wins outright when present, and this file
|
|
16
|
+
* compiles to plain readable JS in `dist/`, so a rate can be corrected in
|
|
17
|
+
* place without waiting for a release.
|
|
18
|
+
*
|
|
19
|
+
* Rates are Anthropic API list prices. Cache read is 0.1x input, cache write
|
|
20
|
+
* 5m is 1.25x input, cache write 1h is 2x input.
|
|
21
|
+
*/
|
|
22
|
+
/**
|
|
23
|
+
* When these rates were last checked against published pricing. Surfaced in
|
|
24
|
+
* `contextengine cost` output: a rate table with no date is a rate table
|
|
25
|
+
* nobody knows to distrust.
|
|
26
|
+
*/
|
|
27
|
+
export const DEFAULT_PRICING_ASOF = "2026-08-20";
|
|
28
|
+
function rate(model, input, output) {
|
|
29
|
+
return {
|
|
30
|
+
model,
|
|
31
|
+
input_per_mtok: input,
|
|
32
|
+
output_per_mtok: output,
|
|
33
|
+
cache_read_per_mtok: Number((input * 0.1).toFixed(4)),
|
|
34
|
+
cache_write_5m_per_mtok: Number((input * 1.25).toFixed(4)),
|
|
35
|
+
cache_write_1h_per_mtok: Number((input * 2).toFixed(4)),
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Longest-prefix matched, so dated ids (`claude-haiku-4-5-20251001`) resolve
|
|
40
|
+
* to their family. Deliberately NO `*` catch-all: a model absent from this
|
|
41
|
+
* table must report as UNPRICED, never be valued at a guessed rate
|
|
42
|
+
* (`[ABSENCE-IS-NOT-A-VERDICT]`).
|
|
43
|
+
*/
|
|
44
|
+
export const DEFAULT_PRICING = [
|
|
45
|
+
rate("claude-opus-5", 5, 25),
|
|
46
|
+
rate("claude-opus-4-8", 5, 25),
|
|
47
|
+
rate("claude-opus-4-7", 5, 25),
|
|
48
|
+
rate("claude-opus-4-6", 5, 25),
|
|
49
|
+
rate("claude-opus-4-5", 5, 25),
|
|
50
|
+
rate("claude-fable-5", 10, 50),
|
|
51
|
+
rate("claude-mythos-5", 10, 50),
|
|
52
|
+
rate("claude-sonnet-5", 3, 15),
|
|
53
|
+
rate("claude-sonnet-4-6", 3, 15),
|
|
54
|
+
rate("claude-sonnet-4-5", 3, 15),
|
|
55
|
+
rate("claude-haiku-4-5", 1, 5),
|
|
56
|
+
];
|
|
57
|
+
//# sourceMappingURL=default-pricing.js.map
|
package/dist/detector.js
CHANGED
|
@@ -334,9 +334,11 @@ export const _internal = {
|
|
|
334
334
|
detectDrift, detectNoInsight, detectSilentFailure, detectStaleDocSignal,
|
|
335
335
|
};
|
|
336
336
|
import { metricsFor } from "./transcript-collector.js";
|
|
337
|
+
import { DEFAULT_PRICING } from "./default-pricing.js";
|
|
337
338
|
export const DEFAULT_COST_THRESHOLDS = {
|
|
338
339
|
billing_mode: "subscription",
|
|
339
|
-
|
|
340
|
+
// [DEFAULT-RATES-SHIP-WITH-THE-PACKAGE] — never [] again.
|
|
341
|
+
pricing: DEFAULT_PRICING,
|
|
340
342
|
min_cache_efficiency: 3,
|
|
341
343
|
max_tool_calls_per_agent: 2,
|
|
342
344
|
max_cost_per_agent_usd: 3,
|
|
@@ -130,6 +130,24 @@ export declare function pricingFor(model: string | null, table: ModelPricing[]):
|
|
|
130
130
|
* AND intensity — never one alone.
|
|
131
131
|
*/
|
|
132
132
|
export declare function costOf(t: TokenTally, p: ModelPricing | null): CostBreakdown;
|
|
133
|
+
/**
|
|
134
|
+
* Whether a cost figure can be presented as money at all.
|
|
135
|
+
*
|
|
136
|
+
* 🔒 LOCKED [NEVER-RENDER-AN-UNKNOWN-AS-A-NUMBER] — 2026-08-20
|
|
137
|
+
* ⛔ NEVER print a $0.00 cost row, total, or "caching saved" figure while
|
|
138
|
+
* `unpricedTokens > 0`.
|
|
139
|
+
* WHY: 2.5.0 rendered a full VALUED COST table of $0.00 over 1.08 billion
|
|
140
|
+
* unpriced tokens, including "caching saved $0.00 (0%)" — which reads as
|
|
141
|
+
* "your caching achieves nothing" when the true reuse was 8x. The token
|
|
142
|
+
* accounting was right; the PRESENTATION layer turned "I have no rates"
|
|
143
|
+
* into a number. That is Session 21's rule at the display layer: any
|
|
144
|
+
* plausible-looking value returned from a branch meaning "I could not
|
|
145
|
+
* determine this" is the bug, however reasonable it looks.
|
|
146
|
+
* FIX: branch on this before formatting. `unpriced` must render the word
|
|
147
|
+
* UNPRICED, never a currency amount.
|
|
148
|
+
*/
|
|
149
|
+
export type PricingStatus = "priced" | "partial" | "unpriced";
|
|
150
|
+
export declare function pricingStatus(c: CostBreakdown): PricingStatus;
|
|
133
151
|
export declare function emptyTally(): TokenTally;
|
|
134
152
|
export declare function addTally(a: TokenTally, b: TokenTally): TokenTally;
|
|
135
153
|
export declare function totalTokens(t: TokenTally): number;
|
|
@@ -84,6 +84,11 @@ export function costOf(t, p) {
|
|
|
84
84
|
t.output * p.output_per_mtok) / M;
|
|
85
85
|
return { input, cacheWrite, cacheRead, output, total: input + cacheWrite + cacheRead + output, withoutCache, unpricedTokens: 0 };
|
|
86
86
|
}
|
|
87
|
+
export function pricingStatus(c) {
|
|
88
|
+
if (c.unpricedTokens === 0)
|
|
89
|
+
return "priced";
|
|
90
|
+
return c.total === 0 ? "unpriced" : "partial";
|
|
91
|
+
}
|
|
87
92
|
export function emptyTally() {
|
|
88
93
|
return { input: 0, cacheWrite5m: 0, cacheWrite1h: 0, cacheRead: 0, output: 0 };
|
|
89
94
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@compr/opscontext-mcp",
|
|
3
|
-
"version": "2.5.
|
|
3
|
+
"version": "2.5.1",
|
|
4
4
|
"description": "OpsContext for AI Agents — read-only fleet visibility (PM2/nginx/Docker/git/cron) + tamper-evident audit log + policy-as-code hooks. The ops + compliance layer Claude Code can't grow natively.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|