@beastmode-develeap/beastmode 0.1.363 → 0.1.365
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/web/board.html +170 -7
- package/dist/web/build-commit.txt +1 -1
- package/dist/web/build-stamp.txt +1 -1
- package/package.json +1 -1
package/dist/web/board.html
CHANGED
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
}
|
|
16
16
|
</script>
|
|
17
17
|
<!--BOARD_DATA-->
|
|
18
|
-
<script>window.__BUILD_STAMP__ = "20260605-
|
|
18
|
+
<script>window.__BUILD_STAMP__ = "20260605-034414-4ae0705";</script>
|
|
19
19
|
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
20
20
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
21
21
|
<link href="https://fonts.googleapis.com/css2?family=IBM+Plex+Sans:wght@300;400;500;600;700&family=JetBrains+Mono:wght@400;500;600;700&display=swap" rel="stylesheet">
|
|
@@ -9947,6 +9947,14 @@ function SettingsPage() {
|
|
|
9947
9947
|
|
|
9948
9948
|
const [restartFields, setRestartFields] = useState(new Set());
|
|
9949
9949
|
|
|
9950
|
+
// Cost knobs (feature #888): the highest-cost in-flight item, shown next to
|
|
9951
|
+
// the per_item_usd_cap field so the operator can pick a sensible ceiling.
|
|
9952
|
+
const [costsByItems, setCostsByItems] = useState(null);
|
|
9953
|
+
// FR-2 hot-reload confirmation: the `cost` category is AUTO-reloaded by the
|
|
9954
|
+
// daemon (no restart), so after a save we surface "Reloading…" then
|
|
9955
|
+
// "Applied at HH:MM:SS" instead of a restart badge.
|
|
9956
|
+
const [costReload, setCostReload] = useState(null);
|
|
9957
|
+
|
|
9950
9958
|
useEffect(() => {
|
|
9951
9959
|
api('GET', '/api/config')
|
|
9952
9960
|
.then(setConfig)
|
|
@@ -9954,6 +9962,15 @@ function SettingsPage() {
|
|
|
9954
9962
|
.finally(() => setLoading(false));
|
|
9955
9963
|
}, []);
|
|
9956
9964
|
|
|
9965
|
+
useEffect(() => {
|
|
9966
|
+
// Not board-scoped: we want the global highest-cost item regardless of the
|
|
9967
|
+
// project filter. Swallow errors — the indicator falls back to "—".
|
|
9968
|
+
fetch('/api/costs/by-items')
|
|
9969
|
+
.then(r => r.ok ? r.json() : {})
|
|
9970
|
+
.then(data => setCostsByItems(data || {}))
|
|
9971
|
+
.catch(() => setCostsByItems({}));
|
|
9972
|
+
}, []);
|
|
9973
|
+
|
|
9957
9974
|
useEffect(() => {
|
|
9958
9975
|
api('GET', '/api/daemon/reload-categories')
|
|
9959
9976
|
.then(data => {
|
|
@@ -10007,6 +10024,15 @@ function SettingsPage() {
|
|
|
10007
10024
|
setConfig(result);
|
|
10008
10025
|
setSuccess('Configuration saved');
|
|
10009
10026
|
setTimeout(() => setSuccess(null), 3000);
|
|
10027
|
+
|
|
10028
|
+
// FR-2: the cost category hot-reloads without a restart. Show a brief
|
|
10029
|
+
// "Reloading…" then a timestamped "Applied" so the operator knows the
|
|
10030
|
+
// new ceiling is live, not pending a daemon bounce.
|
|
10031
|
+
setCostReload('reloading');
|
|
10032
|
+
setTimeout(() => {
|
|
10033
|
+
const t = new Date().toLocaleTimeString([], { hour12: false });
|
|
10034
|
+
setCostReload({ appliedAt: t });
|
|
10035
|
+
}, 700);
|
|
10010
10036
|
} catch (e) { setError(e.message); }
|
|
10011
10037
|
setSaving(false);
|
|
10012
10038
|
};
|
|
@@ -10044,6 +10070,17 @@ function SettingsPage() {
|
|
|
10044
10070
|
const cost = config.cost || {};
|
|
10045
10071
|
const humanGates = config.human_gates || {};
|
|
10046
10072
|
|
|
10073
|
+
// FR-3: highest in-flight item cost, used as a reference point next to the
|
|
10074
|
+
// per_item_usd_cap field. costsByItems is keyed by item id → { total_cost_usd }.
|
|
10075
|
+
// null = still loading; {} = loaded but no data → show "—".
|
|
10076
|
+
const highestItemCost = costsByItems
|
|
10077
|
+
? Object.values(costsByItems).reduce(
|
|
10078
|
+
(max, c) => Math.max(max, (c && c.total_cost_usd) || 0), 0)
|
|
10079
|
+
: null;
|
|
10080
|
+
const highestItemCostLabel = (highestItemCost && highestItemCost > 0)
|
|
10081
|
+
? (formatCost(highestItemCost) || '$' + highestItemCost.toFixed(2))
|
|
10082
|
+
: '—';
|
|
10083
|
+
|
|
10047
10084
|
// Build model options dynamically from whatever the config actually uses,
|
|
10048
10085
|
// plus well-known model families. No more hardcoding — when Anthropic
|
|
10049
10086
|
// releases a new model, it shows up as soon as someone sets it in config.
|
|
@@ -10232,13 +10269,44 @@ function SettingsPage() {
|
|
|
10232
10269
|
</div>
|
|
10233
10270
|
</div>
|
|
10234
10271
|
|
|
10235
|
-
<!-- Cost Section
|
|
10272
|
+
<!-- Cost Section. The "cost" category hot-reloads (ReloadCategory.AUTO),
|
|
10273
|
+
so it carries NO RestartBadge — instead saveConfig surfaces an
|
|
10274
|
+
inline "Reloading / Applied at HH:MM:SS" confirmation (FR-2). -->
|
|
10236
10275
|
<div class="settings-section">
|
|
10237
|
-
<h3 style="display:flex;align-items:center;gap:8px">
|
|
10276
|
+
<h3 style="display:flex;align-items:center;gap:8px">
|
|
10277
|
+
Cost Optimization
|
|
10278
|
+
${costReload === 'reloading'
|
|
10279
|
+
? html`<span style="font-size:12px;color:#888;font-weight:normal">⏳ Reloading…</span>`
|
|
10280
|
+
: (costReload && costReload.appliedAt)
|
|
10281
|
+
? html`<span style="font-size:12px;color:#16a34a;font-weight:normal">✅ Applied at ${costReload.appliedAt}</span>`
|
|
10282
|
+
: null}
|
|
10283
|
+
</h3>
|
|
10284
|
+
|
|
10285
|
+
<!-- Per-item USD cap (FR-3/FR-5): hard spend ceiling per item. -->
|
|
10286
|
+
<div class="setting-row">
|
|
10287
|
+
<div>
|
|
10288
|
+
<div class="setting-label">Per-Item Cost Cap (USD)</div>
|
|
10289
|
+
<div class="setting-desc">Hard ceiling on Claude cost per item in USD. Set to 0 to disable. When exceeded, the item transitions to Stuck on next dispatch.</div>
|
|
10290
|
+
<div class="setting-desc" style="margin-top:4px">Current item with highest cost: ${highestItemCostLabel}</div>
|
|
10291
|
+
</div>
|
|
10292
|
+
<div class="setting-control">
|
|
10293
|
+
<input type="number" class="form-input" min="0" max="10000" step="1" style="width:90px;text-align:center"
|
|
10294
|
+
placeholder="50"
|
|
10295
|
+
value=${cost.per_item_usd_cap !== undefined ? cost.per_item_usd_cap : ''}
|
|
10296
|
+
onInput=${e => {
|
|
10297
|
+
const raw = e.target.value;
|
|
10298
|
+
if (raw === '') { updateField('cost.per_item_usd_cap', undefined); return; }
|
|
10299
|
+
const v = parseFloat(raw);
|
|
10300
|
+
updateField('cost.per_item_usd_cap', Number.isFinite(v) ? Math.max(0, Math.min(10000, v)) : undefined);
|
|
10301
|
+
}} />
|
|
10302
|
+
${cost.per_item_usd_cap === undefined ? html`<span style="font-size:11px;color:#aaa;font-style:italic">(default)</span>` : null}
|
|
10303
|
+
</div>
|
|
10304
|
+
</div>
|
|
10305
|
+
|
|
10238
10306
|
<div class="setting-row">
|
|
10239
10307
|
<div>
|
|
10240
10308
|
<div class="setting-label">Model Tiering</div>
|
|
10241
|
-
<div class="setting-desc">Use cheaper models for
|
|
10309
|
+
<div class="setting-desc">Use cheaper models for cheaper sub-tasks.</div>
|
|
10242
10310
|
</div>
|
|
10243
10311
|
<div class="setting-control">
|
|
10244
10312
|
<label class="toggle-switch">
|
|
@@ -10246,12 +10314,14 @@ function SettingsPage() {
|
|
|
10246
10314
|
onChange=${e => updateField('cost.model_tiering_enabled', e.target.checked)} />
|
|
10247
10315
|
<span class="toggle-slider"></span>
|
|
10248
10316
|
</label>
|
|
10317
|
+
${cost.model_tiering_enabled === undefined ? html`<span style="font-size:11px;color:#aaa;font-style:italic">(default)</span>` : null}
|
|
10249
10318
|
</div>
|
|
10250
10319
|
</div>
|
|
10320
|
+
|
|
10251
10321
|
<div class="setting-row">
|
|
10252
10322
|
<div>
|
|
10253
10323
|
<div class="setting-label">Incremental Verification</div>
|
|
10254
|
-
<div class="setting-desc">
|
|
10324
|
+
<div class="setting-desc">Re-verify only changed scenarios between iterations.</div>
|
|
10255
10325
|
</div>
|
|
10256
10326
|
<div class="setting-control">
|
|
10257
10327
|
<label class="toggle-switch">
|
|
@@ -10259,19 +10329,112 @@ function SettingsPage() {
|
|
|
10259
10329
|
onChange=${e => updateField('cost.incremental_verification', e.target.checked)} />
|
|
10260
10330
|
<span class="toggle-slider"></span>
|
|
10261
10331
|
</label>
|
|
10332
|
+
${cost.incremental_verification === undefined ? html`<span style="font-size:11px;color:#aaa;font-style:italic">(default)</span>` : null}
|
|
10333
|
+
</div>
|
|
10334
|
+
</div>
|
|
10335
|
+
|
|
10336
|
+
<div class="setting-row">
|
|
10337
|
+
<div>
|
|
10338
|
+
<div class="setting-label">Full Re-Verify Interval</div>
|
|
10339
|
+
<div class="setting-desc">Run a full re-verification every N iterations.</div>
|
|
10340
|
+
</div>
|
|
10341
|
+
<div class="setting-control">
|
|
10342
|
+
<input type="number" class="form-input" min="1" max="20" style="width:80px;text-align:center"
|
|
10343
|
+
placeholder="3"
|
|
10344
|
+
value=${cost.full_reverify_interval !== undefined ? cost.full_reverify_interval : ''}
|
|
10345
|
+
onInput=${e => {
|
|
10346
|
+
const raw = e.target.value;
|
|
10347
|
+
if (raw === '') { updateField('cost.full_reverify_interval', undefined); return; }
|
|
10348
|
+
updateField('cost.full_reverify_interval', parseInt(raw) || 3);
|
|
10349
|
+
}} />
|
|
10350
|
+
${cost.full_reverify_interval === undefined ? html`<span style="font-size:11px;color:#aaa;font-style:italic">(default)</span>` : null}
|
|
10262
10351
|
</div>
|
|
10263
10352
|
</div>
|
|
10353
|
+
|
|
10354
|
+
<!-- Build Check toggle (FR-6): disabling needs an explicit confirm so a
|
|
10355
|
+
broken build can't silently slip past verification. -->
|
|
10264
10356
|
<div class="setting-row">
|
|
10265
10357
|
<div>
|
|
10266
10358
|
<div class="setting-label">Build Check</div>
|
|
10267
|
-
<div class="setting-desc">
|
|
10359
|
+
<div class="setting-desc">Whether to run the build/compile check between iterations.</div>
|
|
10268
10360
|
</div>
|
|
10269
10361
|
<div class="setting-control">
|
|
10270
10362
|
<label class="toggle-switch">
|
|
10271
10363
|
<input type="checkbox" checked=${cost.build_check_enabled !== false}
|
|
10272
|
-
onChange=${e =>
|
|
10364
|
+
onChange=${e => {
|
|
10365
|
+
if (!e.target.checked && !window.confirm('Disable the build check? Broken builds will no longer be caught before verification.')) {
|
|
10366
|
+
e.target.checked = true; return;
|
|
10367
|
+
}
|
|
10368
|
+
updateField('cost.build_check_enabled', e.target.checked);
|
|
10369
|
+
}} />
|
|
10370
|
+
<span class="toggle-slider"></span>
|
|
10371
|
+
</label>
|
|
10372
|
+
${cost.build_check_enabled === undefined ? html`<span style="font-size:11px;color:#aaa;font-style:italic">(default)</span>` : null}
|
|
10373
|
+
</div>
|
|
10374
|
+
</div>
|
|
10375
|
+
|
|
10376
|
+
<div class="setting-row">
|
|
10377
|
+
<div>
|
|
10378
|
+
<div class="setting-label">Build Command</div>
|
|
10379
|
+
<div class="setting-desc">Shell command used for the incremental build check.</div>
|
|
10380
|
+
</div>
|
|
10381
|
+
<div class="setting-control">
|
|
10382
|
+
<input type="text" class="form-input" style="width:180px"
|
|
10383
|
+
placeholder="npm run build"
|
|
10384
|
+
value=${cost.build_check_command !== undefined ? cost.build_check_command : ''}
|
|
10385
|
+
onInput=${e => updateField('cost.build_check_command', e.target.value || undefined)} />
|
|
10386
|
+
${cost.build_check_command === undefined ? html`<span style="font-size:11px;color:#aaa;font-style:italic">(default)</span>` : null}
|
|
10387
|
+
</div>
|
|
10388
|
+
</div>
|
|
10389
|
+
|
|
10390
|
+
<div class="setting-row">
|
|
10391
|
+
<div>
|
|
10392
|
+
<div class="setting-label">Build Timeout (seconds)</div>
|
|
10393
|
+
<div class="setting-desc">Timeout in seconds for the build check command.</div>
|
|
10394
|
+
</div>
|
|
10395
|
+
<div class="setting-control">
|
|
10396
|
+
<input type="number" class="form-input" min="1" max="3600" style="width:90px;text-align:center"
|
|
10397
|
+
placeholder="90"
|
|
10398
|
+
value=${cost.build_check_timeout_seconds !== undefined ? cost.build_check_timeout_seconds : ''}
|
|
10399
|
+
onInput=${e => {
|
|
10400
|
+
const raw = e.target.value;
|
|
10401
|
+
if (raw === '') { updateField('cost.build_check_timeout_seconds', undefined); return; }
|
|
10402
|
+
updateField('cost.build_check_timeout_seconds', parseInt(raw) || 90);
|
|
10403
|
+
}} />
|
|
10404
|
+
${cost.build_check_timeout_seconds === undefined ? html`<span style="font-size:11px;color:#aaa;font-style:italic">(default)</span>` : null}
|
|
10405
|
+
</div>
|
|
10406
|
+
</div>
|
|
10407
|
+
|
|
10408
|
+
<div class="setting-row">
|
|
10409
|
+
<div>
|
|
10410
|
+
<div class="setting-label">NLSpec Pre-Check</div>
|
|
10411
|
+
<div class="setting-desc">Cheap NLSpec compliance precheck before full verify.</div>
|
|
10412
|
+
</div>
|
|
10413
|
+
<div class="setting-control">
|
|
10414
|
+
<label class="toggle-switch">
|
|
10415
|
+
<input type="checkbox" checked=${cost.nlspec_precheck_enabled !== false}
|
|
10416
|
+
onChange=${e => updateField('cost.nlspec_precheck_enabled', e.target.checked)} />
|
|
10273
10417
|
<span class="toggle-slider"></span>
|
|
10274
10418
|
</label>
|
|
10419
|
+
${cost.nlspec_precheck_enabled === undefined ? html`<span style="font-size:11px;color:#aaa;font-style:italic">(default)</span>` : null}
|
|
10420
|
+
</div>
|
|
10421
|
+
</div>
|
|
10422
|
+
|
|
10423
|
+
<!-- NLSpec pre-check model (FR-6): a fixed dropdown, no free text. -->
|
|
10424
|
+
<div class="setting-row">
|
|
10425
|
+
<div>
|
|
10426
|
+
<div class="setting-label">NLSpec Pre-Check Model</div>
|
|
10427
|
+
<div class="setting-desc">Model used for the NLSpec precheck.</div>
|
|
10428
|
+
</div>
|
|
10429
|
+
<div class="setting-control">
|
|
10430
|
+
<select class="form-input" style="width:220px"
|
|
10431
|
+
value=${cost.nlspec_precheck_model || 'claude-haiku-4-5-20251001'}
|
|
10432
|
+
onChange=${e => updateField('cost.nlspec_precheck_model', e.target.value)}>
|
|
10433
|
+
${[...new Set([cost.nlspec_precheck_model || 'claude-haiku-4-5-20251001', ...MODEL_OPTIONS])]
|
|
10434
|
+
.filter(Boolean)
|
|
10435
|
+
.map(m => html`<option key=${m} value=${m}>${m}</option>`)}
|
|
10436
|
+
</select>
|
|
10437
|
+
${cost.nlspec_precheck_model === undefined ? html`<span style="font-size:11px;color:#aaa;font-style:italic">(default)</span>` : null}
|
|
10275
10438
|
</div>
|
|
10276
10439
|
</div>
|
|
10277
10440
|
</div>
|
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
4ae0705a9bfd26f4f9f724535b2c9a1b8b6ac051
|
package/dist/web/build-stamp.txt
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
20260605-
|
|
1
|
+
20260605-034414-4ae0705
|