pokertools-arena 0.4.1 → 0.4.4
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 +1 -1
- package/dist/app.js +20 -6
- package/dist/index.html +5 -5
- package/dist/pokertools-arena.html +31 -17
- package/dist/styles.css +6 -6
- package/docs/diagnostics/SUMMARY.md +1 -1
- package/docs/diagnostics/summary.json +1 -1
- package/docs/releases/RELEASE_NOTES.md +28 -0
- package/package.json +1 -1
- package/src/app.js +28 -7
- package/src/index.html +5 -5
- package/src/lib/decision-core.js +0 -2
- package/src/styles.css +6 -6
package/README.md
CHANGED
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
| **Package** | `npx pokertools-arena` |
|
|
18
18
|
| **Runtime** | Node.js ≥ 24 for tooling; any modern browser for the app |
|
|
19
19
|
|
|
20
|
-
> **Current release: 0.4.
|
|
20
|
+
> **Current release: 0.4.4.** A maintenance release on top of the 0.4.0 methodology work: modal tooltips float above overflow, the launcher starts exactly once when its default port is busy, real-API diagnostics skip cleanly when repository secrets are absent, the Decision sanity suite attributes results to the correct model, seat editors show the model-derived name, close buttons use centered SVG icons, and decision logs no longer repeat the typed-decision caption.
|
|
21
21
|
|
|
22
22
|
---
|
|
23
23
|
|
package/dist/app.js
CHANGED
|
@@ -115138,7 +115138,6 @@ function decide(agent, connection, args) {
|
|
|
115138
115138
|
}
|
|
115139
115139
|
var FAMILY_OBJECTIVE = "Choose exactly one legal action family that best maximizes tournament chip EV from the supplied state. Do not choose an amount; a size is chosen in a separate step.";
|
|
115140
115140
|
var SIZE_OBJECTIVE = (family) => `The action family ${String(family).toUpperCase()} has already been selected. Choose exactly one legal ${family === ACTION_FAMILY.RAISE ? "raise size" : "bet size"} that best maximizes tournament chip EV. Do not change the action family.`;
|
|
115141
|
-
var SPECTATOR_NOTE = "This model returns typed decisions rather than a text rationale.";
|
|
115142
115141
|
function buildJevFamilyQuestions(families, { instructions = FAMILY_OBJECTIVE, criteria = null } = {}) {
|
|
115143
115142
|
return { type: "choice", instructions, criteria: criteria ?? familyCriteria(families) };
|
|
115144
115143
|
}
|
|
@@ -115594,6 +115593,7 @@ var lastProcessedEventId = null;
|
|
|
115594
115593
|
var MAX_LOBBY_SEATS = 10;
|
|
115595
115594
|
var seatAssignments = Array(MAX_LOBBY_SEATS).fill(null);
|
|
115596
115595
|
var editingSeatIndex = null;
|
|
115596
|
+
var seatNameAuto = false;
|
|
115597
115597
|
var lobbyVisible = true;
|
|
115598
115598
|
var pendingAutostart = false;
|
|
115599
115599
|
var arenaMaxDecisions = 0;
|
|
@@ -116863,19 +116863,28 @@ function updateSeatSummary() {
|
|
|
116863
116863
|
const count = seatAssignments.filter(Boolean).length;
|
|
116864
116864
|
if (els.seatSummary) els.seatSummary.textContent = `${count} / ${MAX_LOBBY_SEATS} seated`;
|
|
116865
116865
|
}
|
|
116866
|
+
function syncSeatNameFromModel() {
|
|
116867
|
+
if (!seatNameAuto || editingSeatIndex == null) return;
|
|
116868
|
+
const model = els.seatModel.value.trim();
|
|
116869
|
+
els.seatName.value = model ? displayModelName(model) : `Player ${editingSeatIndex + 1}`;
|
|
116870
|
+
}
|
|
116866
116871
|
function openSeatEditor(seatIndex) {
|
|
116867
116872
|
if (!Number.isInteger(seatIndex) || seatIndex < 0 || seatIndex >= MAX_LOBBY_SEATS) return;
|
|
116868
116873
|
editingSeatIndex = seatIndex;
|
|
116869
116874
|
const locked = Boolean(director && ["RUNNING", "PAUSED"].includes(director.status));
|
|
116870
116875
|
const draft = seatAssignments[seatIndex] ? { ...seatAssignments[seatIndex] } : defaultSeatDraft(seatIndex);
|
|
116871
116876
|
els.seatDialogTitle.textContent = `Seat ${seatIndex + 1}`;
|
|
116872
|
-
|
|
116877
|
+
const placeholderName = `Player ${seatIndex + 1}`;
|
|
116878
|
+
const storedName = String(draft.name || "").trim();
|
|
116879
|
+
seatNameAuto = !storedName || /^player\s+\d+$/i.test(storedName) || Boolean(draft.model) && storedName === displayModelName(draft.model);
|
|
116880
|
+
els.seatName.value = seatNameAuto && draft.model ? displayModelName(draft.model) : storedName || placeholderName;
|
|
116873
116881
|
refreshSeatConnectionSelect(draft.connectionId || "");
|
|
116874
116882
|
if (draft.connectionId && [...els.seatConnection.options].some((option) => option.value === draft.connectionId)) els.seatConnection.value = draft.connectionId;
|
|
116875
116883
|
els.seatModel.value = draft.model || "";
|
|
116876
116884
|
els.seatProtocol.value = draft.protocol || "tool";
|
|
116877
116885
|
els.seatProvider.value = draft.provider || "";
|
|
116878
116886
|
applySeatProtocolRules();
|
|
116887
|
+
syncSeatNameFromModel();
|
|
116879
116888
|
void refreshSeatModelCatalog();
|
|
116880
116889
|
els.seatError.classList.add("hidden");
|
|
116881
116890
|
els.seatLockNotice.classList.toggle("hidden", !locked);
|
|
@@ -117317,8 +117326,6 @@ function decisionTelemetryHtml(event, { limit = 4 } = {}) {
|
|
|
117317
117326
|
const reasoningTokens = event?.usage?.completion_tokens_details?.reasoning_tokens;
|
|
117318
117327
|
if (reasoningTokens) facts.push(`<span>Reasoning <b>${reasoningTokens} tokens</b></span>`);
|
|
117319
117328
|
if (facts.length) parts.push(`<div class="decision-facts">${facts.join("")}</div>`);
|
|
117320
|
-
const isTypedDecision = meta.family != null || meta.aggression != null || meta.bluffSpot != null || meta.method === "openrouter-decisions" || meta.method === "jev-choice" || String(meta.method || "").includes("hierarchical");
|
|
117321
|
-
if (isTypedDecision) parts.push(`<div class="decision-facts-note">${escapeHtml(SPECTATOR_NOTE)} Bluff opportunity is a property of the spot, not the reason for the chosen action.</div>`);
|
|
117322
117329
|
return parts.join("");
|
|
117323
117330
|
}
|
|
117324
117331
|
function setDecisionContext({ hand = "\u2014", street = "\u2014", position = "\u2014", options = "\u2014", label = "Legal actions", hint = "Choose one", labels = null } = {}) {
|
|
@@ -117576,7 +117583,7 @@ function cloneJson(value) {
|
|
|
117576
117583
|
}
|
|
117577
117584
|
function sanityAgents() {
|
|
117578
117585
|
const connections = readConnections();
|
|
117579
|
-
return readSeatPlayers().map((player) => ({ ...player, connection: connections.find((c) => c.id === player.connectionId) })).filter((row) => row.connection);
|
|
117586
|
+
return readSeatPlayers().map((player) => ({ ...player, id: player.id || `player-${player.lobbySeat + 1}`, connection: connections.find((c) => c.id === player.connectionId) })).filter((row) => row.connection);
|
|
117580
117587
|
}
|
|
117581
117588
|
function sanityProtocolLabel(agent) {
|
|
117582
117589
|
const protocol = effectiveProtocol(agent, agent.connection);
|
|
@@ -118288,9 +118295,16 @@ for (const dialog of [els.setupDialog, els.seatDialog, els.testsDialog, els.repl
|
|
|
118288
118295
|
}
|
|
118289
118296
|
els.seatConnection.addEventListener("change", () => {
|
|
118290
118297
|
applySeatProtocolRules();
|
|
118298
|
+
syncSeatNameFromModel();
|
|
118291
118299
|
void refreshSeatModelCatalog();
|
|
118292
118300
|
});
|
|
118293
|
-
els.seatModel.addEventListener("input",
|
|
118301
|
+
els.seatModel.addEventListener("input", () => {
|
|
118302
|
+
applySeatProtocolRules();
|
|
118303
|
+
syncSeatNameFromModel();
|
|
118304
|
+
});
|
|
118305
|
+
els.seatName.addEventListener("input", () => {
|
|
118306
|
+
seatNameAuto = false;
|
|
118307
|
+
});
|
|
118294
118308
|
els.seatProtocol.addEventListener("change", applySeatProtocolRules);
|
|
118295
118309
|
els.refreshModelsBtn?.addEventListener("click", () => void refreshSeatModelCatalog({ force: true }));
|
|
118296
118310
|
els.removeSeatBtn.addEventListener("click", () => {
|
package/dist/index.html
CHANGED
|
@@ -165,7 +165,7 @@
|
|
|
165
165
|
<h2>Settings</h2>
|
|
166
166
|
<button class="info-button" type="button" aria-label="About settings" data-tip="API keys stay in this browser tab and are never written to saved configuration or tournament logs.">i</button>
|
|
167
167
|
</div>
|
|
168
|
-
<button id="closeSetup" type="button" class="icon-button" aria-label="Close settings"
|
|
168
|
+
<button id="closeSetup" type="button" class="icon-button" aria-label="Close settings"><svg class="icon-glyph" viewBox="0 0 16 16" aria-hidden="true" focusable="false"><path d="M4.5 4.5l7 7M11.5 4.5l-7 7" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round"/></svg></button>
|
|
169
169
|
</div>
|
|
170
170
|
|
|
171
171
|
<div class="dialog-body setup-body">
|
|
@@ -246,7 +246,7 @@
|
|
|
246
246
|
<h2 id="seatDialogTitle">Seat 1</h2>
|
|
247
247
|
<button class="info-button" type="button" aria-label="About seat context" data-tip="Each model receives only its own hole cards plus the same canonical public table state, recent public hands, opponent statistics, and legal actions.">i</button>
|
|
248
248
|
</div>
|
|
249
|
-
<button id="closeSeat" type="button" class="icon-button" aria-label="Close seat editor"
|
|
249
|
+
<button id="closeSeat" type="button" class="icon-button" aria-label="Close seat editor"><svg class="icon-glyph" viewBox="0 0 16 16" aria-hidden="true" focusable="false"><path d="M4.5 4.5l7 7M11.5 4.5l-7 7" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round"/></svg></button>
|
|
250
250
|
</div>
|
|
251
251
|
<div class="dialog-body seat-editor-body">
|
|
252
252
|
<div id="seatLockNotice" class="seat-lock-notice hidden">Seat assignments are locked during a tournament.</div>
|
|
@@ -284,7 +284,7 @@
|
|
|
284
284
|
<div class="dialog-title-row"><h2>Decision sanity suite</h2><button class="info-button" type="button" aria-label="About decision tests" data-tip="A small comprehension and reliability check. It is intentionally not a GTO solver, Elo score, or tournament-strength ranking.">i</button></div>
|
|
285
285
|
<div class="dialog-subtitle">The same 10 fixed poker spots are sent independently to every seated model.</div>
|
|
286
286
|
</div>
|
|
287
|
-
<button id="closeTests" type="button" class="icon-button" aria-label="Close tests"
|
|
287
|
+
<button id="closeTests" type="button" class="icon-button" aria-label="Close tests"><svg class="icon-glyph" viewBox="0 0 16 16" aria-hidden="true" focusable="false"><path d="M4.5 4.5l7 7M11.5 4.5l-7 7" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round"/></svg></button>
|
|
288
288
|
</div>
|
|
289
289
|
<div class="tests-body">
|
|
290
290
|
<div class="tests-how" aria-label="How the suite works">
|
|
@@ -317,7 +317,7 @@
|
|
|
317
317
|
<div class="dialog-title-row"><h2 id="replayTitle">Decision replay</h2><span id="replayBadge" class="replay-badge">HAND —</span></div>
|
|
318
318
|
<div id="replaySubtitle" class="dialog-subtitle">Snapshot captured immediately before the model acted.</div>
|
|
319
319
|
</div>
|
|
320
|
-
<button id="closeReplay" type="button" class="icon-button" aria-label="Close decision replay"
|
|
320
|
+
<button id="closeReplay" type="button" class="icon-button" aria-label="Close decision replay"><svg class="icon-glyph" viewBox="0 0 16 16" aria-hidden="true" focusable="false"><path d="M4.5 4.5l7 7M11.5 4.5l-7 7" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round"/></svg></button>
|
|
321
321
|
</div>
|
|
322
322
|
<div class="dialog-body replay-body">
|
|
323
323
|
<section class="replay-table-wrap">
|
|
@@ -361,7 +361,7 @@
|
|
|
361
361
|
<span class="field-label-row">Preset <button class="info-button inline-info" type="button" aria-label="About connection presets" data-tip="OpenAI-compatible keeps Base URL fully editable. OpenRouter only fills its standard URL and enables OpenRouter-specific routing. TypeSafe uses the native System One endpoint.">i</button></span>
|
|
362
362
|
<select data-field="kind"><option value="openai">OpenAI-compatible</option><option value="openrouter">OpenRouter</option><option value="typesafe">TypeSafe System One</option></select>
|
|
363
363
|
</label>
|
|
364
|
-
<button type="button" class="remove-connection icon-button" title="Remove connection" aria-label="Remove connection"
|
|
364
|
+
<button type="button" class="remove-connection icon-button" title="Remove connection" aria-label="Remove connection"><svg class="icon-glyph" viewBox="0 0 16 16" aria-hidden="true" focusable="false"><path d="M4.5 4.5l7 7M11.5 4.5l-7 7" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round"/></svg></button>
|
|
365
365
|
</div>
|
|
366
366
|
<div class="connection-main-grid">
|
|
367
367
|
<label class="connection-url">Base URL<input data-field="baseUrl" spellcheck="false" placeholder="https://api.example.com/v1" /></label>
|
|
@@ -895,7 +895,11 @@ input::placeholder{color:#4e5862}
|
|
|
895
895
|
.button,.icon-button,.field-icon-button,input,select{box-sizing:border-box}
|
|
896
896
|
.button{min-height:var(--control-h);padding:0 12px;border-radius:var(--radius-sm);font-size:10px;font-weight:800;line-height:1;white-space:nowrap}
|
|
897
897
|
.small-button{min-height:30px;height:30px;padding-inline:10px;font-size:9px}
|
|
898
|
-
.icon-button{width:30px;height:30px;min-width:30px;border-radius:var(--radius-sm);display:grid;place-items:center;padding:0}
|
|
898
|
+
.icon-button{width:30px;height:30px;min-width:30px;border-radius:var(--radius-sm);display:grid;place-items:center;padding:0;line-height:1}
|
|
899
|
+
/* SVG glyphs center geometrically; a text "×" sits on its baseline and looks
|
|
900
|
+
vertically off-centre inside these small buttons. */
|
|
901
|
+
.icon-button svg{width:14px;height:14px;display:block;pointer-events:none}
|
|
902
|
+
.dialog-head .icon-button svg{width:13px;height:13px}
|
|
899
903
|
input,select{width:100%;min-width:0;height:var(--control-h);padding:0 9px;border-radius:var(--radius-sm);font-size:11px;background:#0a0d10;border:1px solid var(--modal-border);color:var(--text)}
|
|
900
904
|
input:focus,select:focus{outline:none;border-color:rgba(146,201,164,.34);box-shadow:0 0 0 3px rgba(146,201,164,.05)}
|
|
901
905
|
label{min-width:0}
|
|
@@ -2321,10 +2325,7 @@ a.button{text-decoration:none}
|
|
|
2321
2325
|
.card .card-corner.bottom{display:none!important}
|
|
2322
2326
|
}
|
|
2323
2327
|
|
|
2324
|
-
/* 0.2.23 — decision diagnostics + deterministic hand evaluation
|
|
2325
|
-
Spectator-only annotations. The note explicitly separates typed model
|
|
2326
|
-
answers from any fabricated rationale. */
|
|
2327
|
-
.decision-facts-note{margin-top:6px;font-size:10px;line-height:1.4;color:var(--muted,#8aa098);border-left:2px solid rgba(184,236,111,.35);padding-left:8px}
|
|
2328
|
+
/* 0.2.23 — decision diagnostics + deterministic hand evaluation. */
|
|
2328
2329
|
.replay-reason-text{margin-bottom:10px;line-height:1.45}
|
|
2329
2330
|
.replay-summary span b{white-space:nowrap}
|
|
2330
2331
|
|
|
@@ -2339,7 +2340,6 @@ a.button{text-decoration:none}
|
|
|
2339
2340
|
.stage-chip{white-space:nowrap}
|
|
2340
2341
|
.telemetry-group{margin-top:6px}
|
|
2341
2342
|
.telemetry-caption{display:block;font-size:9px;letter-spacing:.08em;text-transform:uppercase;color:var(--muted,#8aa098);margin-bottom:3px}
|
|
2342
|
-
.decision-facts-note{white-space:normal}
|
|
2343
2343
|
|
|
2344
2344
|
/* 0.4.0 — methodology, reproducibility and repository-organization release.
|
|
2345
2345
|
Browser application source moved under src/; visual behavior is unchanged. */
|
|
@@ -2456,7 +2456,7 @@ a.button{text-decoration:none}
|
|
|
2456
2456
|
<h2>Settings</h2>
|
|
2457
2457
|
<button class="info-button" type="button" aria-label="About settings" data-tip="API keys stay in this browser tab and are never written to saved configuration or tournament logs.">i</button>
|
|
2458
2458
|
</div>
|
|
2459
|
-
<button id="closeSetup" type="button" class="icon-button" aria-label="Close settings"
|
|
2459
|
+
<button id="closeSetup" type="button" class="icon-button" aria-label="Close settings"><svg class="icon-glyph" viewBox="0 0 16 16" aria-hidden="true" focusable="false"><path d="M4.5 4.5l7 7M11.5 4.5l-7 7" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round"/></svg></button>
|
|
2460
2460
|
</div>
|
|
2461
2461
|
|
|
2462
2462
|
<div class="dialog-body setup-body">
|
|
@@ -2537,7 +2537,7 @@ a.button{text-decoration:none}
|
|
|
2537
2537
|
<h2 id="seatDialogTitle">Seat 1</h2>
|
|
2538
2538
|
<button class="info-button" type="button" aria-label="About seat context" data-tip="Each model receives only its own hole cards plus the same canonical public table state, recent public hands, opponent statistics, and legal actions.">i</button>
|
|
2539
2539
|
</div>
|
|
2540
|
-
<button id="closeSeat" type="button" class="icon-button" aria-label="Close seat editor"
|
|
2540
|
+
<button id="closeSeat" type="button" class="icon-button" aria-label="Close seat editor"><svg class="icon-glyph" viewBox="0 0 16 16" aria-hidden="true" focusable="false"><path d="M4.5 4.5l7 7M11.5 4.5l-7 7" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round"/></svg></button>
|
|
2541
2541
|
</div>
|
|
2542
2542
|
<div class="dialog-body seat-editor-body">
|
|
2543
2543
|
<div id="seatLockNotice" class="seat-lock-notice hidden">Seat assignments are locked during a tournament.</div>
|
|
@@ -2575,7 +2575,7 @@ a.button{text-decoration:none}
|
|
|
2575
2575
|
<div class="dialog-title-row"><h2>Decision sanity suite</h2><button class="info-button" type="button" aria-label="About decision tests" data-tip="A small comprehension and reliability check. It is intentionally not a GTO solver, Elo score, or tournament-strength ranking.">i</button></div>
|
|
2576
2576
|
<div class="dialog-subtitle">The same 10 fixed poker spots are sent independently to every seated model.</div>
|
|
2577
2577
|
</div>
|
|
2578
|
-
<button id="closeTests" type="button" class="icon-button" aria-label="Close tests"
|
|
2578
|
+
<button id="closeTests" type="button" class="icon-button" aria-label="Close tests"><svg class="icon-glyph" viewBox="0 0 16 16" aria-hidden="true" focusable="false"><path d="M4.5 4.5l7 7M11.5 4.5l-7 7" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round"/></svg></button>
|
|
2579
2579
|
</div>
|
|
2580
2580
|
<div class="tests-body">
|
|
2581
2581
|
<div class="tests-how" aria-label="How the suite works">
|
|
@@ -2608,7 +2608,7 @@ a.button{text-decoration:none}
|
|
|
2608
2608
|
<div class="dialog-title-row"><h2 id="replayTitle">Decision replay</h2><span id="replayBadge" class="replay-badge">HAND —</span></div>
|
|
2609
2609
|
<div id="replaySubtitle" class="dialog-subtitle">Snapshot captured immediately before the model acted.</div>
|
|
2610
2610
|
</div>
|
|
2611
|
-
<button id="closeReplay" type="button" class="icon-button" aria-label="Close decision replay"
|
|
2611
|
+
<button id="closeReplay" type="button" class="icon-button" aria-label="Close decision replay"><svg class="icon-glyph" viewBox="0 0 16 16" aria-hidden="true" focusable="false"><path d="M4.5 4.5l7 7M11.5 4.5l-7 7" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round"/></svg></button>
|
|
2612
2612
|
</div>
|
|
2613
2613
|
<div class="dialog-body replay-body">
|
|
2614
2614
|
<section class="replay-table-wrap">
|
|
@@ -2652,7 +2652,7 @@ a.button{text-decoration:none}
|
|
|
2652
2652
|
<span class="field-label-row">Preset <button class="info-button inline-info" type="button" aria-label="About connection presets" data-tip="OpenAI-compatible keeps Base URL fully editable. OpenRouter only fills its standard URL and enables OpenRouter-specific routing. TypeSafe uses the native System One endpoint.">i</button></span>
|
|
2653
2653
|
<select data-field="kind"><option value="openai">OpenAI-compatible</option><option value="openrouter">OpenRouter</option><option value="typesafe">TypeSafe System One</option></select>
|
|
2654
2654
|
</label>
|
|
2655
|
-
<button type="button" class="remove-connection icon-button" title="Remove connection" aria-label="Remove connection"
|
|
2655
|
+
<button type="button" class="remove-connection icon-button" title="Remove connection" aria-label="Remove connection"><svg class="icon-glyph" viewBox="0 0 16 16" aria-hidden="true" focusable="false"><path d="M4.5 4.5l7 7M11.5 4.5l-7 7" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round"/></svg></button>
|
|
2656
2656
|
</div>
|
|
2657
2657
|
<div class="connection-main-grid">
|
|
2658
2658
|
<label class="connection-url">Base URL<input data-field="baseUrl" spellcheck="false" placeholder="https://api.example.com/v1" /></label>
|
|
@@ -117813,7 +117813,6 @@ function decide(agent, connection, args) {
|
|
|
117813
117813
|
}
|
|
117814
117814
|
var FAMILY_OBJECTIVE = "Choose exactly one legal action family that best maximizes tournament chip EV from the supplied state. Do not choose an amount; a size is chosen in a separate step.";
|
|
117815
117815
|
var SIZE_OBJECTIVE = (family) => `The action family ${String(family).toUpperCase()} has already been selected. Choose exactly one legal ${family === ACTION_FAMILY.RAISE ? "raise size" : "bet size"} that best maximizes tournament chip EV. Do not change the action family.`;
|
|
117816
|
-
var SPECTATOR_NOTE = "This model returns typed decisions rather than a text rationale.";
|
|
117817
117816
|
function buildJevFamilyQuestions(families, { instructions = FAMILY_OBJECTIVE, criteria = null } = {}) {
|
|
117818
117817
|
return { type: "choice", instructions, criteria: criteria ?? familyCriteria(families) };
|
|
117819
117818
|
}
|
|
@@ -118269,6 +118268,7 @@ var lastProcessedEventId = null;
|
|
|
118269
118268
|
var MAX_LOBBY_SEATS = 10;
|
|
118270
118269
|
var seatAssignments = Array(MAX_LOBBY_SEATS).fill(null);
|
|
118271
118270
|
var editingSeatIndex = null;
|
|
118271
|
+
var seatNameAuto = false;
|
|
118272
118272
|
var lobbyVisible = true;
|
|
118273
118273
|
var pendingAutostart = false;
|
|
118274
118274
|
var arenaMaxDecisions = 0;
|
|
@@ -119538,19 +119538,28 @@ function updateSeatSummary() {
|
|
|
119538
119538
|
const count = seatAssignments.filter(Boolean).length;
|
|
119539
119539
|
if (els.seatSummary) els.seatSummary.textContent = `${count} / ${MAX_LOBBY_SEATS} seated`;
|
|
119540
119540
|
}
|
|
119541
|
+
function syncSeatNameFromModel() {
|
|
119542
|
+
if (!seatNameAuto || editingSeatIndex == null) return;
|
|
119543
|
+
const model = els.seatModel.value.trim();
|
|
119544
|
+
els.seatName.value = model ? displayModelName(model) : `Player ${editingSeatIndex + 1}`;
|
|
119545
|
+
}
|
|
119541
119546
|
function openSeatEditor(seatIndex) {
|
|
119542
119547
|
if (!Number.isInteger(seatIndex) || seatIndex < 0 || seatIndex >= MAX_LOBBY_SEATS) return;
|
|
119543
119548
|
editingSeatIndex = seatIndex;
|
|
119544
119549
|
const locked = Boolean(director && ["RUNNING", "PAUSED"].includes(director.status));
|
|
119545
119550
|
const draft = seatAssignments[seatIndex] ? { ...seatAssignments[seatIndex] } : defaultSeatDraft(seatIndex);
|
|
119546
119551
|
els.seatDialogTitle.textContent = `Seat ${seatIndex + 1}`;
|
|
119547
|
-
|
|
119552
|
+
const placeholderName = `Player ${seatIndex + 1}`;
|
|
119553
|
+
const storedName = String(draft.name || "").trim();
|
|
119554
|
+
seatNameAuto = !storedName || /^player\s+\d+$/i.test(storedName) || Boolean(draft.model) && storedName === displayModelName(draft.model);
|
|
119555
|
+
els.seatName.value = seatNameAuto && draft.model ? displayModelName(draft.model) : storedName || placeholderName;
|
|
119548
119556
|
refreshSeatConnectionSelect(draft.connectionId || "");
|
|
119549
119557
|
if (draft.connectionId && [...els.seatConnection.options].some((option) => option.value === draft.connectionId)) els.seatConnection.value = draft.connectionId;
|
|
119550
119558
|
els.seatModel.value = draft.model || "";
|
|
119551
119559
|
els.seatProtocol.value = draft.protocol || "tool";
|
|
119552
119560
|
els.seatProvider.value = draft.provider || "";
|
|
119553
119561
|
applySeatProtocolRules();
|
|
119562
|
+
syncSeatNameFromModel();
|
|
119554
119563
|
void refreshSeatModelCatalog();
|
|
119555
119564
|
els.seatError.classList.add("hidden");
|
|
119556
119565
|
els.seatLockNotice.classList.toggle("hidden", !locked);
|
|
@@ -119992,8 +120001,6 @@ function decisionTelemetryHtml(event, { limit = 4 } = {}) {
|
|
|
119992
120001
|
const reasoningTokens = event?.usage?.completion_tokens_details?.reasoning_tokens;
|
|
119993
120002
|
if (reasoningTokens) facts.push(`<span>Reasoning <b>${reasoningTokens} tokens</b></span>`);
|
|
119994
120003
|
if (facts.length) parts.push(`<div class="decision-facts">${facts.join("")}</div>`);
|
|
119995
|
-
const isTypedDecision = meta.family != null || meta.aggression != null || meta.bluffSpot != null || meta.method === "openrouter-decisions" || meta.method === "jev-choice" || String(meta.method || "").includes("hierarchical");
|
|
119996
|
-
if (isTypedDecision) parts.push(`<div class="decision-facts-note">${escapeHtml(SPECTATOR_NOTE)} Bluff opportunity is a property of the spot, not the reason for the chosen action.</div>`);
|
|
119997
120004
|
return parts.join("");
|
|
119998
120005
|
}
|
|
119999
120006
|
function setDecisionContext({ hand = "\u2014", street = "\u2014", position = "\u2014", options = "\u2014", label = "Legal actions", hint = "Choose one", labels = null } = {}) {
|
|
@@ -120251,7 +120258,7 @@ function cloneJson(value) {
|
|
|
120251
120258
|
}
|
|
120252
120259
|
function sanityAgents() {
|
|
120253
120260
|
const connections = readConnections();
|
|
120254
|
-
return readSeatPlayers().map((player) => ({ ...player, connection: connections.find((c) => c.id === player.connectionId) })).filter((row) => row.connection);
|
|
120261
|
+
return readSeatPlayers().map((player) => ({ ...player, id: player.id || `player-${player.lobbySeat + 1}`, connection: connections.find((c) => c.id === player.connectionId) })).filter((row) => row.connection);
|
|
120255
120262
|
}
|
|
120256
120263
|
function sanityProtocolLabel(agent) {
|
|
120257
120264
|
const protocol = effectiveProtocol(agent, agent.connection);
|
|
@@ -120963,9 +120970,16 @@ for (const dialog of [els.setupDialog, els.seatDialog, els.testsDialog, els.repl
|
|
|
120963
120970
|
}
|
|
120964
120971
|
els.seatConnection.addEventListener("change", () => {
|
|
120965
120972
|
applySeatProtocolRules();
|
|
120973
|
+
syncSeatNameFromModel();
|
|
120966
120974
|
void refreshSeatModelCatalog();
|
|
120967
120975
|
});
|
|
120968
|
-
els.seatModel.addEventListener("input",
|
|
120976
|
+
els.seatModel.addEventListener("input", () => {
|
|
120977
|
+
applySeatProtocolRules();
|
|
120978
|
+
syncSeatNameFromModel();
|
|
120979
|
+
});
|
|
120980
|
+
els.seatName.addEventListener("input", () => {
|
|
120981
|
+
seatNameAuto = false;
|
|
120982
|
+
});
|
|
120969
120983
|
els.seatProtocol.addEventListener("change", applySeatProtocolRules);
|
|
120970
120984
|
els.refreshModelsBtn?.addEventListener("click", () => void refreshSeatModelCatalog({ force: true }));
|
|
120971
120985
|
els.removeSeatBtn.addEventListener("click", () => {
|
package/dist/styles.css
CHANGED
|
@@ -839,7 +839,11 @@ input::placeholder{color:#4e5862}
|
|
|
839
839
|
.button,.icon-button,.field-icon-button,input,select{box-sizing:border-box}
|
|
840
840
|
.button{min-height:var(--control-h);padding:0 12px;border-radius:var(--radius-sm);font-size:10px;font-weight:800;line-height:1;white-space:nowrap}
|
|
841
841
|
.small-button{min-height:30px;height:30px;padding-inline:10px;font-size:9px}
|
|
842
|
-
.icon-button{width:30px;height:30px;min-width:30px;border-radius:var(--radius-sm);display:grid;place-items:center;padding:0}
|
|
842
|
+
.icon-button{width:30px;height:30px;min-width:30px;border-radius:var(--radius-sm);display:grid;place-items:center;padding:0;line-height:1}
|
|
843
|
+
/* SVG glyphs center geometrically; a text "×" sits on its baseline and looks
|
|
844
|
+
vertically off-centre inside these small buttons. */
|
|
845
|
+
.icon-button svg{width:14px;height:14px;display:block;pointer-events:none}
|
|
846
|
+
.dialog-head .icon-button svg{width:13px;height:13px}
|
|
843
847
|
input,select{width:100%;min-width:0;height:var(--control-h);padding:0 9px;border-radius:var(--radius-sm);font-size:11px;background:#0a0d10;border:1px solid var(--modal-border);color:var(--text)}
|
|
844
848
|
input:focus,select:focus{outline:none;border-color:rgba(146,201,164,.34);box-shadow:0 0 0 3px rgba(146,201,164,.05)}
|
|
845
849
|
label{min-width:0}
|
|
@@ -2265,10 +2269,7 @@ a.button{text-decoration:none}
|
|
|
2265
2269
|
.card .card-corner.bottom{display:none!important}
|
|
2266
2270
|
}
|
|
2267
2271
|
|
|
2268
|
-
/* 0.2.23 — decision diagnostics + deterministic hand evaluation
|
|
2269
|
-
Spectator-only annotations. The note explicitly separates typed model
|
|
2270
|
-
answers from any fabricated rationale. */
|
|
2271
|
-
.decision-facts-note{margin-top:6px;font-size:10px;line-height:1.4;color:var(--muted,#8aa098);border-left:2px solid rgba(184,236,111,.35);padding-left:8px}
|
|
2272
|
+
/* 0.2.23 — decision diagnostics + deterministic hand evaluation. */
|
|
2272
2273
|
.replay-reason-text{margin-bottom:10px;line-height:1.45}
|
|
2273
2274
|
.replay-summary span b{white-space:nowrap}
|
|
2274
2275
|
|
|
@@ -2283,7 +2284,6 @@ a.button{text-decoration:none}
|
|
|
2283
2284
|
.stage-chip{white-space:nowrap}
|
|
2284
2285
|
.telemetry-group{margin-top:6px}
|
|
2285
2286
|
.telemetry-caption{display:block;font-size:9px;letter-spacing:.08em;text-transform:uppercase;color:var(--muted,#8aa098);margin-bottom:3px}
|
|
2286
|
-
.decision-facts-note{white-space:normal}
|
|
2287
2287
|
|
|
2288
2288
|
/* 0.4.0 — methodology, reproducibility and repository-organization release.
|
|
2289
2289
|
Browser application source moved under src/; visual behavior is unchanged. */
|
|
@@ -1,5 +1,33 @@
|
|
|
1
1
|
# Release notes
|
|
2
2
|
|
|
3
|
+
## 0.4.4 — Maintenance
|
|
4
|
+
|
|
5
|
+
- **Cleaner decision logs.** Removed the repeated spectator caption ("This
|
|
6
|
+
model returns typed decisions rather than a text rationale…") that appeared on
|
|
7
|
+
every typed decision in the live feed and the replay panel. Typed decisions
|
|
8
|
+
already show their selected family/size plus confidence telemetry, so logs are
|
|
9
|
+
concise and no longer restate that a rationale is absent. `SPECTATOR_NOTE` and
|
|
10
|
+
its CSS were removed; release checks guard against reintroduction.
|
|
11
|
+
|
|
12
|
+
## 0.4.3 — Maintenance
|
|
13
|
+
|
|
14
|
+
- **Seat editor name.** Opening a seat restored from `.env` or saved
|
|
15
|
+
configuration now shows the model-derived name (for example `Gemma`) instead
|
|
16
|
+
of the generic `Player 1`, matching the table, lobby and stats surfaces. The
|
|
17
|
+
name follows the chosen model until it is edited by hand.
|
|
18
|
+
- **Close buttons.** Dialog close buttons and the remove-connection button use a
|
|
19
|
+
centered inline SVG cross instead of a baseline-aligned text `×`, so the glyph
|
|
20
|
+
is vertically centered.
|
|
21
|
+
|
|
22
|
+
## 0.4.2 — Maintenance
|
|
23
|
+
|
|
24
|
+
- **Decision sanity suite model attribution.** Seat assignments restored from
|
|
25
|
+
`.env` or saved configuration had no `id`, so every sanity agent shared an
|
|
26
|
+
undefined id. Each model summary then counted every model's decisions (for
|
|
27
|
+
example `24/10` when only 10 spots exist) and every scenario column rendered
|
|
28
|
+
the last model's result. Sanity agents now derive the same stable
|
|
29
|
+
`player-<seat>` id the tournament uses, so each model is reported separately.
|
|
30
|
+
|
|
3
31
|
## 0.4.1 — Maintenance
|
|
4
32
|
|
|
5
33
|
A maintenance release on top of the 0.4.0 methodology work.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pokertools-arena",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.4",
|
|
4
4
|
"description": "Browser-first AI poker benchmark powered by @pokertools/engine with generic OpenAI-compatible endpoints, optional OpenRouter/Jev Decisions routing, deterministic hierarchical decisions, paired fixed-state methodology, and reproducible reporting.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
package/src/app.js
CHANGED
|
@@ -14,7 +14,7 @@ import {
|
|
|
14
14
|
BENCHMARK_MODES, DECISION_ARCHITECTURES, DEFAULT_BENCHMARK_MODE, DEFAULT_DECISION_ARCHITECTURE,
|
|
15
15
|
REPRESENTATION_MODES, DEFAULT_REPRESENTATION_MODE, DECISION_ARCHITECTURE_VERSION,
|
|
16
16
|
legalActionFamilies, legalAggressiveSizes, buildHierarchicalDecision, familyCriteria, sizeCriteria,
|
|
17
|
-
applyBenchmarkMode, renderDecisionState, aggregateActionProbabilitiesByFamily, probabilityStats, SIZE_LABELS,
|
|
17
|
+
applyBenchmarkMode, renderDecisionState, aggregateActionProbabilitiesByFamily, probabilityStats, SIZE_LABELS,
|
|
18
18
|
isAggressiveType, familyForActionType, FAMILY_LABELS, decisionClockPhase,
|
|
19
19
|
} from './lib/decision-core.js';
|
|
20
20
|
|
|
@@ -47,6 +47,7 @@ let lastProcessedEventId = null;
|
|
|
47
47
|
const MAX_LOBBY_SEATS = 10;
|
|
48
48
|
let seatAssignments = Array(MAX_LOBBY_SEATS).fill(null);
|
|
49
49
|
let editingSeatIndex = null;
|
|
50
|
+
let seatNameAuto = false;
|
|
50
51
|
let lobbyVisible = true;
|
|
51
52
|
let pendingAutostart = false;
|
|
52
53
|
let arenaMaxDecisions = 0;
|
|
@@ -1065,19 +1066,31 @@ function updateSeatSummary() {
|
|
|
1065
1066
|
const count = seatAssignments.filter(Boolean).length;
|
|
1066
1067
|
if (els.seatSummary) els.seatSummary.textContent = `${count} / ${MAX_LOBBY_SEATS} seated`;
|
|
1067
1068
|
}
|
|
1069
|
+
// The seat name follows the chosen model until the user edits it by hand, so
|
|
1070
|
+
// seats restored from .env or saved config show "Gemma" instead of the generic
|
|
1071
|
+
// "Player 1" the other surfaces already hide via visiblePlayerName().
|
|
1072
|
+
function syncSeatNameFromModel() {
|
|
1073
|
+
if (!seatNameAuto || editingSeatIndex == null) return;
|
|
1074
|
+
const model = els.seatModel.value.trim();
|
|
1075
|
+
els.seatName.value = model ? displayModelName(model) : `Player ${editingSeatIndex + 1}`;
|
|
1076
|
+
}
|
|
1068
1077
|
function openSeatEditor(seatIndex) {
|
|
1069
1078
|
if (!Number.isInteger(seatIndex) || seatIndex < 0 || seatIndex >= MAX_LOBBY_SEATS) return;
|
|
1070
1079
|
editingSeatIndex = seatIndex;
|
|
1071
1080
|
const locked = Boolean(director && ['RUNNING', 'PAUSED'].includes(director.status));
|
|
1072
1081
|
const draft = seatAssignments[seatIndex] ? { ...seatAssignments[seatIndex] } : defaultSeatDraft(seatIndex);
|
|
1073
1082
|
els.seatDialogTitle.textContent = `Seat ${seatIndex + 1}`;
|
|
1074
|
-
|
|
1083
|
+
const placeholderName = `Player ${seatIndex + 1}`;
|
|
1084
|
+
const storedName = String(draft.name || '').trim();
|
|
1085
|
+
seatNameAuto = !storedName || /^player\s+\d+$/i.test(storedName) || (Boolean(draft.model) && storedName === displayModelName(draft.model));
|
|
1086
|
+
els.seatName.value = seatNameAuto && draft.model ? displayModelName(draft.model) : (storedName || placeholderName);
|
|
1075
1087
|
refreshSeatConnectionSelect(draft.connectionId || '');
|
|
1076
1088
|
if (draft.connectionId && [...els.seatConnection.options].some(option => option.value === draft.connectionId)) els.seatConnection.value = draft.connectionId;
|
|
1077
1089
|
els.seatModel.value = draft.model || '';
|
|
1078
1090
|
els.seatProtocol.value = draft.protocol || 'tool';
|
|
1079
1091
|
els.seatProvider.value = draft.provider || '';
|
|
1080
1092
|
applySeatProtocolRules();
|
|
1093
|
+
syncSeatNameFromModel();
|
|
1081
1094
|
void refreshSeatModelCatalog();
|
|
1082
1095
|
els.seatError.classList.add('hidden');
|
|
1083
1096
|
els.seatLockNotice.classList.toggle('hidden', !locked);
|
|
@@ -1522,8 +1535,9 @@ function decisionTelemetryHtml(event, { limit = 4 } = {}) {
|
|
|
1522
1535
|
const reasoningTokens = event?.usage?.completion_tokens_details?.reasoning_tokens;
|
|
1523
1536
|
if (reasoningTokens) facts.push(`<span>Reasoning <b>${reasoningTokens} tokens</b></span>`);
|
|
1524
1537
|
if (facts.length) parts.push(`<div class="decision-facts">${facts.join('')}</div>`);
|
|
1525
|
-
|
|
1526
|
-
|
|
1538
|
+
// Typed decisions (hierarchical / Jev / OpenRouter-decisions) have no prose
|
|
1539
|
+
// rationale; the telemetry above already shows family, size and confidence.
|
|
1540
|
+
// A repeated "no text rationale" caption was removed as log noise.
|
|
1527
1541
|
return parts.join('');
|
|
1528
1542
|
}
|
|
1529
1543
|
function setDecisionContext({ hand = '—', street = '—', position = '—', options = '—', label = 'Legal actions', hint = 'Choose one', labels = null } = {}) {
|
|
@@ -1746,7 +1760,13 @@ function openSetup({ preserveError = false } = {}) { if (!preserveError) els.set
|
|
|
1746
1760
|
function cloneJson(value) { return JSON.parse(JSON.stringify(value)); }
|
|
1747
1761
|
function sanityAgents() {
|
|
1748
1762
|
const connections = readConnections();
|
|
1749
|
-
|
|
1763
|
+
// Seat assignments restored from .env or saved config carry no id, so derive
|
|
1764
|
+
// the same stable id the tournament uses. Without it every agent.id is
|
|
1765
|
+
// undefined, so sanity results from all models collapse into one summary
|
|
1766
|
+
// (e.g. "24/10") and every grid column shows the last model's result.
|
|
1767
|
+
return readSeatPlayers()
|
|
1768
|
+
.map(player => ({ ...player, id: player.id || `player-${player.lobbySeat + 1}`, connection: connections.find(c => c.id === player.connectionId) }))
|
|
1769
|
+
.filter(row => row.connection);
|
|
1750
1770
|
}
|
|
1751
1771
|
function sanityProtocolLabel(agent) {
|
|
1752
1772
|
const protocol = effectiveProtocol(agent, agent.connection);
|
|
@@ -2326,8 +2346,9 @@ for (const dialog of [els.setupDialog, els.seatDialog, els.testsDialog, els.repl
|
|
|
2326
2346
|
dialog.addEventListener('click', event => { if (event.target === dialog) dialog.close(); });
|
|
2327
2347
|
dialog.addEventListener('close', hideTooltip);
|
|
2328
2348
|
}
|
|
2329
|
-
els.seatConnection.addEventListener('change', () => { applySeatProtocolRules(); void refreshSeatModelCatalog(); });
|
|
2330
|
-
els.seatModel.addEventListener('input', applySeatProtocolRules);
|
|
2349
|
+
els.seatConnection.addEventListener('change', () => { applySeatProtocolRules(); syncSeatNameFromModel(); void refreshSeatModelCatalog(); });
|
|
2350
|
+
els.seatModel.addEventListener('input', () => { applySeatProtocolRules(); syncSeatNameFromModel(); });
|
|
2351
|
+
els.seatName.addEventListener('input', () => { seatNameAuto = false; });
|
|
2331
2352
|
els.seatProtocol.addEventListener('change', applySeatProtocolRules);
|
|
2332
2353
|
els.refreshModelsBtn?.addEventListener('click', () => void refreshSeatModelCatalog({ force: true }));
|
|
2333
2354
|
els.removeSeatBtn.addEventListener('click', () => {
|
package/src/index.html
CHANGED
|
@@ -165,7 +165,7 @@
|
|
|
165
165
|
<h2>Settings</h2>
|
|
166
166
|
<button class="info-button" type="button" aria-label="About settings" data-tip="API keys stay in this browser tab and are never written to saved configuration or tournament logs.">i</button>
|
|
167
167
|
</div>
|
|
168
|
-
<button id="closeSetup" type="button" class="icon-button" aria-label="Close settings"
|
|
168
|
+
<button id="closeSetup" type="button" class="icon-button" aria-label="Close settings"><svg class="icon-glyph" viewBox="0 0 16 16" aria-hidden="true" focusable="false"><path d="M4.5 4.5l7 7M11.5 4.5l-7 7" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round"/></svg></button>
|
|
169
169
|
</div>
|
|
170
170
|
|
|
171
171
|
<div class="dialog-body setup-body">
|
|
@@ -246,7 +246,7 @@
|
|
|
246
246
|
<h2 id="seatDialogTitle">Seat 1</h2>
|
|
247
247
|
<button class="info-button" type="button" aria-label="About seat context" data-tip="Each model receives only its own hole cards plus the same canonical public table state, recent public hands, opponent statistics, and legal actions.">i</button>
|
|
248
248
|
</div>
|
|
249
|
-
<button id="closeSeat" type="button" class="icon-button" aria-label="Close seat editor"
|
|
249
|
+
<button id="closeSeat" type="button" class="icon-button" aria-label="Close seat editor"><svg class="icon-glyph" viewBox="0 0 16 16" aria-hidden="true" focusable="false"><path d="M4.5 4.5l7 7M11.5 4.5l-7 7" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round"/></svg></button>
|
|
250
250
|
</div>
|
|
251
251
|
<div class="dialog-body seat-editor-body">
|
|
252
252
|
<div id="seatLockNotice" class="seat-lock-notice hidden">Seat assignments are locked during a tournament.</div>
|
|
@@ -284,7 +284,7 @@
|
|
|
284
284
|
<div class="dialog-title-row"><h2>Decision sanity suite</h2><button class="info-button" type="button" aria-label="About decision tests" data-tip="A small comprehension and reliability check. It is intentionally not a GTO solver, Elo score, or tournament-strength ranking.">i</button></div>
|
|
285
285
|
<div class="dialog-subtitle">The same 10 fixed poker spots are sent independently to every seated model.</div>
|
|
286
286
|
</div>
|
|
287
|
-
<button id="closeTests" type="button" class="icon-button" aria-label="Close tests"
|
|
287
|
+
<button id="closeTests" type="button" class="icon-button" aria-label="Close tests"><svg class="icon-glyph" viewBox="0 0 16 16" aria-hidden="true" focusable="false"><path d="M4.5 4.5l7 7M11.5 4.5l-7 7" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round"/></svg></button>
|
|
288
288
|
</div>
|
|
289
289
|
<div class="tests-body">
|
|
290
290
|
<div class="tests-how" aria-label="How the suite works">
|
|
@@ -317,7 +317,7 @@
|
|
|
317
317
|
<div class="dialog-title-row"><h2 id="replayTitle">Decision replay</h2><span id="replayBadge" class="replay-badge">HAND —</span></div>
|
|
318
318
|
<div id="replaySubtitle" class="dialog-subtitle">Snapshot captured immediately before the model acted.</div>
|
|
319
319
|
</div>
|
|
320
|
-
<button id="closeReplay" type="button" class="icon-button" aria-label="Close decision replay"
|
|
320
|
+
<button id="closeReplay" type="button" class="icon-button" aria-label="Close decision replay"><svg class="icon-glyph" viewBox="0 0 16 16" aria-hidden="true" focusable="false"><path d="M4.5 4.5l7 7M11.5 4.5l-7 7" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round"/></svg></button>
|
|
321
321
|
</div>
|
|
322
322
|
<div class="dialog-body replay-body">
|
|
323
323
|
<section class="replay-table-wrap">
|
|
@@ -361,7 +361,7 @@
|
|
|
361
361
|
<span class="field-label-row">Preset <button class="info-button inline-info" type="button" aria-label="About connection presets" data-tip="OpenAI-compatible keeps Base URL fully editable. OpenRouter only fills its standard URL and enables OpenRouter-specific routing. TypeSafe uses the native System One endpoint.">i</button></span>
|
|
362
362
|
<select data-field="kind"><option value="openai">OpenAI-compatible</option><option value="openrouter">OpenRouter</option><option value="typesafe">TypeSafe System One</option></select>
|
|
363
363
|
</label>
|
|
364
|
-
<button type="button" class="remove-connection icon-button" title="Remove connection" aria-label="Remove connection"
|
|
364
|
+
<button type="button" class="remove-connection icon-button" title="Remove connection" aria-label="Remove connection"><svg class="icon-glyph" viewBox="0 0 16 16" aria-hidden="true" focusable="false"><path d="M4.5 4.5l7 7M11.5 4.5l-7 7" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round"/></svg></button>
|
|
365
365
|
</div>
|
|
366
366
|
<div class="connection-main-grid">
|
|
367
367
|
<label class="connection-url">Base URL<input data-field="baseUrl" spellcheck="false" placeholder="https://api.example.com/v1" /></label>
|
package/src/lib/decision-core.js
CHANGED
|
@@ -1000,8 +1000,6 @@ export function decide(agent, connection, args) {
|
|
|
1000
1000
|
// ===========================================================================
|
|
1001
1001
|
export const FAMILY_OBJECTIVE = 'Choose exactly one legal action family that best maximizes tournament chip EV from the supplied state. Do not choose an amount; a size is chosen in a separate step.';
|
|
1002
1002
|
export const SIZE_OBJECTIVE = family => `The action family ${String(family).toUpperCase()} has already been selected. Choose exactly one legal ${family === ACTION_FAMILY.RAISE ? 'raise size' : 'bet size'} that best maximizes tournament chip EV. Do not change the action family.`;
|
|
1003
|
-
export const SPECTATOR_NOTE = 'This model returns typed decisions rather than a text rationale.';
|
|
1004
|
-
|
|
1005
1003
|
export function buildJevFamilyQuestions(families, { instructions = FAMILY_OBJECTIVE, criteria = null } = {}) {
|
|
1006
1004
|
return { type: 'choice', instructions, criteria: criteria ?? familyCriteria(families) };
|
|
1007
1005
|
}
|
package/src/styles.css
CHANGED
|
@@ -839,7 +839,11 @@ input::placeholder{color:#4e5862}
|
|
|
839
839
|
.button,.icon-button,.field-icon-button,input,select{box-sizing:border-box}
|
|
840
840
|
.button{min-height:var(--control-h);padding:0 12px;border-radius:var(--radius-sm);font-size:10px;font-weight:800;line-height:1;white-space:nowrap}
|
|
841
841
|
.small-button{min-height:30px;height:30px;padding-inline:10px;font-size:9px}
|
|
842
|
-
.icon-button{width:30px;height:30px;min-width:30px;border-radius:var(--radius-sm);display:grid;place-items:center;padding:0}
|
|
842
|
+
.icon-button{width:30px;height:30px;min-width:30px;border-radius:var(--radius-sm);display:grid;place-items:center;padding:0;line-height:1}
|
|
843
|
+
/* SVG glyphs center geometrically; a text "×" sits on its baseline and looks
|
|
844
|
+
vertically off-centre inside these small buttons. */
|
|
845
|
+
.icon-button svg{width:14px;height:14px;display:block;pointer-events:none}
|
|
846
|
+
.dialog-head .icon-button svg{width:13px;height:13px}
|
|
843
847
|
input,select{width:100%;min-width:0;height:var(--control-h);padding:0 9px;border-radius:var(--radius-sm);font-size:11px;background:#0a0d10;border:1px solid var(--modal-border);color:var(--text)}
|
|
844
848
|
input:focus,select:focus{outline:none;border-color:rgba(146,201,164,.34);box-shadow:0 0 0 3px rgba(146,201,164,.05)}
|
|
845
849
|
label{min-width:0}
|
|
@@ -2265,10 +2269,7 @@ a.button{text-decoration:none}
|
|
|
2265
2269
|
.card .card-corner.bottom{display:none!important}
|
|
2266
2270
|
}
|
|
2267
2271
|
|
|
2268
|
-
/* 0.2.23 — decision diagnostics + deterministic hand evaluation
|
|
2269
|
-
Spectator-only annotations. The note explicitly separates typed model
|
|
2270
|
-
answers from any fabricated rationale. */
|
|
2271
|
-
.decision-facts-note{margin-top:6px;font-size:10px;line-height:1.4;color:var(--muted,#8aa098);border-left:2px solid rgba(184,236,111,.35);padding-left:8px}
|
|
2272
|
+
/* 0.2.23 — decision diagnostics + deterministic hand evaluation. */
|
|
2272
2273
|
.replay-reason-text{margin-bottom:10px;line-height:1.45}
|
|
2273
2274
|
.replay-summary span b{white-space:nowrap}
|
|
2274
2275
|
|
|
@@ -2283,7 +2284,6 @@ a.button{text-decoration:none}
|
|
|
2283
2284
|
.stage-chip{white-space:nowrap}
|
|
2284
2285
|
.telemetry-group{margin-top:6px}
|
|
2285
2286
|
.telemetry-caption{display:block;font-size:9px;letter-spacing:.08em;text-transform:uppercase;color:var(--muted,#8aa098);margin-bottom:3px}
|
|
2286
|
-
.decision-facts-note{white-space:normal}
|
|
2287
2287
|
|
|
2288
2288
|
/* 0.4.0 — methodology, reproducibility and repository-organization release.
|
|
2289
2289
|
Browser application source moved under src/; visual behavior is unchanged. */
|