sprag-cli 3.55.0 → 3.56.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.ko.md +4 -0
- package/README.md +4 -0
- package/bin/cli.js +2 -2
- package/docs/CODEX.ko.md +40 -5
- package/docs/CODEX.md +69 -12
- package/package.json +1 -1
- package/src/claude-price.js +28 -0
- package/src/codex-brief.js +20 -0
- package/src/codex-cache-policy.js +26 -41
- package/src/codex-delegation.js +45 -5
- package/src/codex-doctor.js +1 -1
- package/src/codex-harness.js +20 -9
- package/src/codex-hooks.js +106 -18
- package/src/codex-ledger.js +94 -16
- package/src/codex-panel.js +12 -7
- package/src/codex-parser.js +41 -4
- package/src/codex-route-scan.js +14 -7
- package/src/codex-rule-health.js +18 -0
- package/src/commands/codex-delegate.js +12 -3
- package/src/commands/codex.js +18 -10
- package/src/commands/route-scan.js +4 -3
- package/src/cost.js +57 -3
- package/src/formatters/statusline.js +18 -7
- package/src/formatters/table.js +1 -0
- package/src/gateway-prices.js +39 -0
- package/src/litellm-models.js +72 -10
- package/src/model-rules.js +2 -1
- package/src/money.js +9 -0
- package/src/month-spend.js +10 -5
- package/src/openai-prices.js +33 -0
- package/src/route-scan.js +16 -8
- package/src/savings-ledger.js +10 -6
package/src/route-scan.js
CHANGED
|
@@ -25,7 +25,8 @@ import { collectSessionRecords } from './session-records.js';
|
|
|
25
25
|
import {
|
|
26
26
|
collectSubagentRuns, indexRuns, exactRunsForEpisode, fallbackRunsForEpisode,
|
|
27
27
|
} from './subagent-records.js';
|
|
28
|
-
import {
|
|
28
|
+
import { modelRank, isRecognizedModelId, TIER_TARGET_RANK, tierForRank } from './cost.js';
|
|
29
|
+
import { sessionCost } from './claude-price.js';
|
|
29
30
|
import { learnProfileMapping, resetModelAliasCache } from './model-alias.js';
|
|
30
31
|
import { modelRuleBaseText } from './model-rules.js';
|
|
31
32
|
|
|
@@ -115,7 +116,8 @@ const CATEGORIES = [
|
|
|
115
116
|
label: '탐색·조회 (파일/값 찾기)',
|
|
116
117
|
labelEn: 'lookup (finding files/values)',
|
|
117
118
|
agent: 'haiku-explore',
|
|
118
|
-
kw: [[/grep|검색|search|find/i, 2], [/찾아|어디|위치|목록|살펴/i, 1]
|
|
119
|
+
kw: [[/grep|검색|search|find/i, 2], [/찾아|어디|위치|목록|살펴/i, 1],
|
|
120
|
+
[/정의(?:돼|되어)\s*있|어느\s*파일|어떤\s*파일에|\bwhich\s+file\b|\bwhere\s+(?:is|are)\b[^?\n]*\bdefined\b/i, 1]],
|
|
119
121
|
},
|
|
120
122
|
{
|
|
121
123
|
id: 'read',
|
|
@@ -333,9 +335,13 @@ export function worthDelegating(tier, rank) {
|
|
|
333
335
|
* Approximation, deliberately stated as one: it holds token counts constant,
|
|
334
336
|
* which a cheaper model would not reproduce exactly. Directionally right and
|
|
335
337
|
* enough to rank rules by value, so it is rendered as "~$X".
|
|
338
|
+
*
|
|
339
|
+
* Signed: a run that moved to a pricier model is a loss and comes back
|
|
340
|
+
* negative. null means a side has no price (a gateway session whose /model/info
|
|
341
|
+
* table lacks the model), which is unpriced, not zero.
|
|
336
342
|
*/
|
|
337
343
|
export function runSaving(run, mainModel) {
|
|
338
|
-
if (!run.model || !mainModel) return
|
|
344
|
+
if (!run.model || !mainModel) return null;
|
|
339
345
|
const totals = {
|
|
340
346
|
input: run.input,
|
|
341
347
|
cacheCreation: run.cacheCreation,
|
|
@@ -344,9 +350,10 @@ export function runSaving(run, mainModel) {
|
|
|
344
350
|
ephemeral1h: run.ephemeral1h,
|
|
345
351
|
output: run.out,
|
|
346
352
|
};
|
|
347
|
-
const actual =
|
|
348
|
-
const counterfactual =
|
|
349
|
-
|
|
353
|
+
const actual = sessionCost(totals, run.model);
|
|
354
|
+
const counterfactual = sessionCost(totals, mainModel);
|
|
355
|
+
if (!actual || !counterfactual) return null;
|
|
356
|
+
return counterfactual.actual - actual.actual;
|
|
350
357
|
}
|
|
351
358
|
|
|
352
359
|
const clamp = (v, [lo, hi]) => Math.min(hi, Math.max(lo, v));
|
|
@@ -549,7 +556,7 @@ export async function runRouteScan({ days = 14 } = {}) {
|
|
|
549
556
|
d.runs += 1;
|
|
550
557
|
if (isFailedRun(run)) d.errRuns += 1;
|
|
551
558
|
d.outTokens += run.out || 0;
|
|
552
|
-
d.savedUsd += saved;
|
|
559
|
+
if (Number.isFinite(saved)) d.savedUsd += saved; // unpriced runs add nothing
|
|
553
560
|
delegatedStats.set(key, d);
|
|
554
561
|
};
|
|
555
562
|
// Ledger events feed the statusline's weekly/monthly "Routing saved"
|
|
@@ -759,7 +766,8 @@ export async function runRouteScan({ days = 14 } = {}) {
|
|
|
759
766
|
// profile-map.json's `modelAliases` to bring these runs back in.
|
|
760
767
|
if (!baseline || !isRecognizedModelId(baseline)) continue;
|
|
761
768
|
const usd = runSaving(e.run, baseline);
|
|
762
|
-
|
|
769
|
+
// A loss is recorded too; only an unpriced run (null) has nothing to record.
|
|
770
|
+
if (!Number.isFinite(usd)) continue;
|
|
763
771
|
priced.push({
|
|
764
772
|
key: e.run.path,
|
|
765
773
|
ts: e.run.endedAt ?? e.run.startedAt ?? Date.now(),
|
package/src/savings-ledger.js
CHANGED
|
@@ -29,6 +29,9 @@ import { userDataDir } from './paths.js';
|
|
|
29
29
|
const WEEK_MS = 7 * 24 * 3600 * 1000;
|
|
30
30
|
const MONTH_MS = 30 * 24 * 3600 * 1000;
|
|
31
31
|
|
|
32
|
+
/** Stored USD is kept to 4 decimals so re-scans compare equal and files stay small. */
|
|
33
|
+
export const roundUsd = (usd) => Math.round(Number(usd) * 10000) / 10000;
|
|
34
|
+
|
|
32
35
|
export function ledgerPath() {
|
|
33
36
|
return join(userDataDir(), 'delegation-ledger.json');
|
|
34
37
|
}
|
|
@@ -55,8 +58,9 @@ export function loadLedger() {
|
|
|
55
58
|
* Upsert saving events. `events` is an array of
|
|
56
59
|
* { key, ts, usd, rule, from, to } where `key` is the run's transcript path
|
|
57
60
|
* (unique per subagent run) and `from`/`to` name the models the routing
|
|
58
|
-
* decision moved between.
|
|
59
|
-
*
|
|
61
|
+
* decision moved between. `usd` is signed: a run on a pricier model is a loss
|
|
62
|
+
* and is recorded as a negative amount so it reduces the totals. Only
|
|
63
|
+
* non-finite amounts (unpriced runs) are skipped.
|
|
60
64
|
*/
|
|
61
65
|
export function recordDelegationEvents(events) {
|
|
62
66
|
if (!Array.isArray(events) || events.length === 0) return;
|
|
@@ -64,9 +68,9 @@ export function recordDelegationEvents(events) {
|
|
|
64
68
|
data.version = LEDGER_VERSION;
|
|
65
69
|
let changed = false;
|
|
66
70
|
for (const e of events) {
|
|
67
|
-
if (!e || !e.key || !
|
|
71
|
+
if (!e || !e.key || typeof e.usd !== 'number' || !Number.isFinite(e.usd) || !Number.isFinite(e.ts)) continue;
|
|
68
72
|
const prev = data.events[e.key];
|
|
69
|
-
const usd =
|
|
73
|
+
const usd = roundUsd(e.usd);
|
|
70
74
|
if (prev && prev.ts === e.ts && prev.usd === usd) continue;
|
|
71
75
|
data.events[e.key] = {
|
|
72
76
|
ts: e.ts,
|
|
@@ -115,8 +119,8 @@ export function delegationSavedTotals(now = Date.now()) {
|
|
|
115
119
|
const byPair = new Map();
|
|
116
120
|
try {
|
|
117
121
|
for (const e of Object.values(loadLedger().events)) {
|
|
118
|
-
const usd = Number(e.usd)
|
|
119
|
-
if (usd
|
|
122
|
+
const usd = Number(e.usd);
|
|
123
|
+
if (!Number.isFinite(usd)) continue;
|
|
120
124
|
totals.total += usd;
|
|
121
125
|
if (Number.isFinite(e.ts)) {
|
|
122
126
|
if (now - e.ts <= WEEK_MS) totals.week += usd;
|