@mauricode/token-derby 2.12.4 → 2.13.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 +10 -0
- package/dist/bin.js +62 -5
- package/dist/bin.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -64,6 +64,16 @@ The CLI sums `message.usage.output_tokens` across every `*.jsonl` under `~/.clau
|
|
|
64
64
|
|
|
65
65
|
Races can optionally also count *fresh input tokens* — i.e. `input_tokens + cache_creation_input_tokens` (your new context this turn) in addition to output. `cache_read_input_tokens` is never counted, since those reflect passive context size rather than work. The race creator opts in at `token-derby create` time; thresholds for Stampede!, Pulled Away!, and the heartbeat rate cap scale 10× in these races so the achievement cadence stays comparable.
|
|
66
66
|
|
|
67
|
+
## Stamina
|
|
68
|
+
|
|
69
|
+
Some races turn on stamina. Push a horse far above a sustainable pace and it tires — its tokens still count, but at a fraction of face value until it recovers. A normal working pace never triggers this; stamina only bites once you're running well past what the race considers sustainable.
|
|
70
|
+
|
|
71
|
+
Staying connected and easing off is how you recover. Disconnecting does not — a long gap since your last heartbeat earns the same recovery as one ordinary tick, not a bonus for the time you were away, so crashing is not a rest strategy. That's on top of the crash penalty above: you lose the tokens from the gap *and* you don't recover any faster for it.
|
|
72
|
+
|
|
73
|
+
Nothing here asks you to hold work back. A flat-out day still beats a lazy one — it just doesn't beat it by as much as the raw token count would suggest once your horse tires.
|
|
74
|
+
|
|
75
|
+
Stamina is off by default; org owners turn it on and tune it from the Race Settings tab of `token-derby web`. When it's on, the live view shows your horse's stamina as a percentage and bar, plus a multiplier once you're actually losing score to fatigue.
|
|
76
|
+
|
|
67
77
|
## Other models (Codex, Gemini)
|
|
68
78
|
|
|
69
79
|
At join you pick one **primary** model — Claude, Codex, or Gemini — counted 1:1.
|
package/dist/bin.js
CHANGED
|
@@ -252,6 +252,39 @@ function toTag(c, y) {
|
|
|
252
252
|
}
|
|
253
253
|
}
|
|
254
254
|
|
|
255
|
+
// ../shared/dist/scoring.js
|
|
256
|
+
function scoredOf(horse) {
|
|
257
|
+
return horse.scored_tokens ?? horse.current_tokens;
|
|
258
|
+
}
|
|
259
|
+
var STAMINA = {
|
|
260
|
+
SUSTAINABLE_PACE: 4e3,
|
|
261
|
+
DRAIN_PER_MIN: 4,
|
|
262
|
+
MAX_DRAIN_PER_MIN: 6,
|
|
263
|
+
RECOVER_PER_MIN: 2,
|
|
264
|
+
RECOVER_TICK_CAP_MS: 9e4,
|
|
265
|
+
TAPER_FLOOR: 25,
|
|
266
|
+
TIRED_MULTIPLIER: 0.5
|
|
267
|
+
};
|
|
268
|
+
function resolveStaminaConfig(race) {
|
|
269
|
+
const c = race.stamina_config ?? {};
|
|
270
|
+
return {
|
|
271
|
+
sustainable_pace: c.sustainable_pace ?? STAMINA.SUSTAINABLE_PACE,
|
|
272
|
+
drain_per_min: c.drain_per_min ?? STAMINA.DRAIN_PER_MIN,
|
|
273
|
+
max_drain_per_min: c.max_drain_per_min ?? STAMINA.MAX_DRAIN_PER_MIN,
|
|
274
|
+
recover_per_min: c.recover_per_min ?? STAMINA.RECOVER_PER_MIN,
|
|
275
|
+
taper_floor: c.taper_floor ?? STAMINA.TAPER_FLOOR,
|
|
276
|
+
tired_multiplier: c.tired_multiplier ?? STAMINA.TIRED_MULTIPLIER
|
|
277
|
+
};
|
|
278
|
+
}
|
|
279
|
+
var STAMINA_PARAM_BOUNDS = {
|
|
280
|
+
sustainable_pace: { min: 1e3, max: 2e4, step: 250, default: STAMINA.SUSTAINABLE_PACE },
|
|
281
|
+
drain_per_min: { min: 1, max: 12, step: 1, default: STAMINA.DRAIN_PER_MIN },
|
|
282
|
+
max_drain_per_min: { min: 2, max: 20, step: 1, default: STAMINA.MAX_DRAIN_PER_MIN },
|
|
283
|
+
recover_per_min: { min: 1, max: 8, step: 1, default: STAMINA.RECOVER_PER_MIN },
|
|
284
|
+
taper_floor: { min: 10, max: 60, step: 5, default: STAMINA.TAPER_FLOOR },
|
|
285
|
+
tired_multiplier: { min: 0.2, max: 0.9, step: 0.05, default: STAMINA.TIRED_MULTIPLIER }
|
|
286
|
+
};
|
|
287
|
+
|
|
255
288
|
// src/ui/HorseSprite.tsx
|
|
256
289
|
import { Box, Text } from "ink";
|
|
257
290
|
|
|
@@ -788,8 +821,8 @@ var HEARTBEAT_RETRY_DELAYS_MS = [1e3, 2e3, 4e3, 8e3, 15e3];
|
|
|
788
821
|
// src/version.ts
|
|
789
822
|
import { createRequire } from "module";
|
|
790
823
|
function readVersion() {
|
|
791
|
-
if ("2.
|
|
792
|
-
return "2.
|
|
824
|
+
if ("2.13.0".length > 0) {
|
|
825
|
+
return "2.13.0";
|
|
793
826
|
}
|
|
794
827
|
try {
|
|
795
828
|
const req = createRequire(import.meta.url);
|
|
@@ -1326,6 +1359,8 @@ async function createRaceCommand(organisationName) {
|
|
|
1326
1359
|
const counts_input = countInputRaw === "y" || countInputRaw === "yes";
|
|
1327
1360
|
const top5Raw = (await rl.question("Count only each racer's 5 most-active conversations toward their primary model's score? [y/N]: ")).trim().toLowerCase();
|
|
1328
1361
|
const primary_top5 = top5Raw === "y" || top5Raw === "yes";
|
|
1362
|
+
const staminaRaw = (await rl.question("Stamina \u2014 horses that run flat out tire and score less until they recover? [y/N]: ")).trim().toLowerCase();
|
|
1363
|
+
const stamina = staminaRaw === "y" || staminaRaw === "yes";
|
|
1329
1364
|
const resp = await createRace({
|
|
1330
1365
|
name,
|
|
1331
1366
|
start_time: start,
|
|
@@ -1334,7 +1369,8 @@ async function createRaceCommand(organisationName) {
|
|
|
1334
1369
|
...max !== void 0 ? { max_participants: max } : {},
|
|
1335
1370
|
...org ? { organisation_name: org } : {},
|
|
1336
1371
|
...counts_input ? { counts_input: true } : {},
|
|
1337
|
-
...primary_top5 ? { primary_top5: true } : {}
|
|
1372
|
+
...primary_top5 ? { primary_top5: true } : {},
|
|
1373
|
+
...stamina ? { stamina: true } : {}
|
|
1338
1374
|
});
|
|
1339
1375
|
console.log("");
|
|
1340
1376
|
console.log(" \u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557");
|
|
@@ -1353,6 +1389,9 @@ async function createRaceCommand(organisationName) {
|
|
|
1353
1389
|
if (primary_top5) {
|
|
1354
1390
|
console.log(" Primary score counts only each racer's top 5 conversations per beat.");
|
|
1355
1391
|
}
|
|
1392
|
+
if (stamina) {
|
|
1393
|
+
console.log(" Stamina on \u2014 horses running above a sustainable pace will tire.");
|
|
1394
|
+
}
|
|
1356
1395
|
console.log(` Share with participants: token-derby join ${resp.join_code}`);
|
|
1357
1396
|
return 0;
|
|
1358
1397
|
} catch (e) {
|
|
@@ -1442,8 +1481,10 @@ function StatusScreen(props) {
|
|
|
1442
1481
|
const divisionField = own?.division === void 0 ? [] : race.horses.filter((h) => h.division === own.division);
|
|
1443
1482
|
const divisionRank = own ? divisionField.indexOf(own) + 1 : 0;
|
|
1444
1483
|
const showDivision = (race.league_division_names?.length ?? 0) > 0 && divisionRank > 0;
|
|
1484
|
+
const staminaRow = race.stamina === true ? staminaLine(own, race) : null;
|
|
1445
1485
|
const rows = [
|
|
1446
|
-
{ label: "Tokens (race):", value: String(own?.
|
|
1486
|
+
{ label: "Tokens (race):", value: String(own?.final_scored_tokens ?? (own ? scoredOf(own) : 0)) },
|
|
1487
|
+
...staminaRow ? [staminaRow] : [],
|
|
1447
1488
|
{ label: "Position:", value: `${own?.rank ?? "\u2014"} of ${race.horses.length}` },
|
|
1448
1489
|
...showDivision ? [{ label: "Position (Division):", value: `${divisionRank} of ${divisionField.length}` }] : [],
|
|
1449
1490
|
{ label: "Leader:", value: leaderText(leader) },
|
|
@@ -1514,7 +1555,7 @@ function StatLines(props) {
|
|
|
1514
1555
|
}
|
|
1515
1556
|
function leaderText(h) {
|
|
1516
1557
|
if (!h) return "\u2014";
|
|
1517
|
-
return `${h.name}${h.user_name ? ` (${h.user_name})` : ""} \u2014 ${h.
|
|
1558
|
+
return `${h.name}${h.user_name ? ` (${h.user_name})` : ""} \u2014 ${h.final_scored_tokens ?? scoredOf(h)}`;
|
|
1518
1559
|
}
|
|
1519
1560
|
function elapsed(race) {
|
|
1520
1561
|
const start = new Date(race.start_time).getTime();
|
|
@@ -1528,6 +1569,22 @@ function bar(pct, width) {
|
|
|
1528
1569
|
const filled = Math.round(pct * width);
|
|
1529
1570
|
return "\u2593".repeat(filled) + "\u2591".repeat(width - filled);
|
|
1530
1571
|
}
|
|
1572
|
+
function staminaLine(own, race) {
|
|
1573
|
+
const stamina = own?.stamina ?? 100;
|
|
1574
|
+
const cfg = resolveStaminaConfig(race);
|
|
1575
|
+
const floor = cfg.taper_floor;
|
|
1576
|
+
const band = stamina > 50 ? "green" : stamina >= floor ? "amber" : "red";
|
|
1577
|
+
const color = band === "green" ? "green" : band === "amber" ? "yellow" : "red";
|
|
1578
|
+
const pct = Math.max(0, Math.min(1, stamina / 100));
|
|
1579
|
+
const multiplier = band === "red" ? cfg.tired_multiplier + (1 - cfg.tired_multiplier) * (stamina / floor) : null;
|
|
1580
|
+
return {
|
|
1581
|
+
label: "Stamina:",
|
|
1582
|
+
value: /* @__PURE__ */ jsxs5(Text7, { color, children: [
|
|
1583
|
+
`${Math.round(stamina)}% ${bar(pct, 20)}`,
|
|
1584
|
+
multiplier !== null ? ` \xD7${multiplier.toFixed(2)}` : ""
|
|
1585
|
+
] })
|
|
1586
|
+
};
|
|
1587
|
+
}
|
|
1531
1588
|
function statusColor(status) {
|
|
1532
1589
|
if (status === "live") return "green";
|
|
1533
1590
|
if (status === "pending") return "yellow";
|