pokertools-arena 0.16.2 → 0.17.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/README.md +1 -1
- package/dist/app.js +272 -36
- package/dist/index.html +5 -0
- package/dist/pokertools-arena.html +278 -37
- package/dist/styles.css +1 -1
- package/docs/diagnostics/SUMMARY.md +1 -1
- package/docs/diagnostics/summary.json +1 -1
- package/docs/releases/RELEASE_NOTES.md +57 -0
- package/package.json +2 -2
- package/src/app.js +180 -22
- package/src/index.html +5 -0
- package/src/lib/decision-core.js +103 -9
- package/src/styles.css +1 -1
|
@@ -548,7 +548,7 @@ label input, label select { margin-top: 5px; font-weight: 450; }
|
|
|
548
548
|
.split-footer { justify-content: space-between; }
|
|
549
549
|
.seat-footer-actions, .replay-share-actions { display: flex; gap: 7px; }
|
|
550
550
|
.seat-editor-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 12px; }
|
|
551
|
-
.seat-model-field { grid-column: 1 / -1; }
|
|
551
|
+
.seat-model-field, .seat-effort-field { grid-column: 1 / -1; }
|
|
552
552
|
.model-input-wrap { margin-top: 6px; display: grid; grid-template-columns: minmax(0, 1fr) 38px; gap: 8px; }
|
|
553
553
|
.model-input-wrap input { margin: 0; }
|
|
554
554
|
.field-icon-button, .dialog-body .remove-connection { width: 38px; height: 38px; min-height: 38px; border-radius: 8px; }
|
|
@@ -1041,6 +1041,11 @@ label input, label select { margin-top: 5px; font-weight: 450; }
|
|
|
1041
1041
|
<span class="checkbox-field"><input id="seatCaptureReasoning" type="checkbox" /> Capture raw model reasoning</span>
|
|
1042
1042
|
<span class="field-help seat-reasoning-help hidden">This model is not a reasoning model by default. Capturing its reasoning can be far slower (Gemma measured ~2s → 27–48s per decision) and is outside the standard benchmark track. Raise the action clock before using it.</span>
|
|
1043
1043
|
</label>
|
|
1044
|
+
<label class="seat-effort-field">
|
|
1045
|
+
<span class="field-label-row">Reasoning effort <button class="info-button inline-info" type="button" aria-label="About reasoning effort" data-tip="How much hidden thinking the model may spend. Higher effort is smarter but slower; a slow reasoner can exhaust the action clock and be forced to fold. Options come from the OpenRouter model catalogue, and an unset value keeps the arena's fixed reasoning cap.">i</button></span>
|
|
1046
|
+
<select id="seatReasoningEffort"></select>
|
|
1047
|
+
<span id="seatReasoningEffortHelp" class="field-help"></span>
|
|
1048
|
+
</label>
|
|
1044
1049
|
</div>
|
|
1045
1050
|
</div>
|
|
1046
1051
|
<div id="seatError" class="setup-error hidden"></div>
|
|
@@ -115591,8 +115596,15 @@ async function fetchStreamingJson(url, options, { maxRetries = 1, onDelta = null
|
|
|
115591
115596
|
throw new ArenaRequestError(message, { status: response.status, category: requestErrorCategory(response.status), payload, incidents });
|
|
115592
115597
|
}
|
|
115593
115598
|
}
|
|
115599
|
+
function requestErrorDetail(err) {
|
|
115600
|
+
const parts = [summarizeError(err)];
|
|
115601
|
+
const nested = err?.payload?.error?.metadata?.raw ?? err?.payload?.metadata?.raw;
|
|
115602
|
+
if (typeof nested === "string") parts.push(nested);
|
|
115603
|
+
else if (nested) parts.push(String(JSON.stringify(nested)));
|
|
115604
|
+
return parts.join(" ");
|
|
115605
|
+
}
|
|
115594
115606
|
function isUnsupportedToolChoiceError(err) {
|
|
115595
|
-
const message =
|
|
115607
|
+
const message = requestErrorDetail(err);
|
|
115596
115608
|
return [400, 404, 422].includes(Number(err?.status)) && /tool[_ -]?choice|tool call|tools?.*(?:unsupported|support)|no endpoints?.*support/i.test(message);
|
|
115597
115609
|
}
|
|
115598
115610
|
function mapGet(mapish, key, fallback = 0) {
|
|
@@ -115618,13 +115630,23 @@ function jsonSafe(value) {
|
|
|
115618
115630
|
}
|
|
115619
115631
|
var protocolCapabilityCache = /* @__PURE__ */ new Map();
|
|
115620
115632
|
var modelSupportedParameters = /* @__PURE__ */ new Map();
|
|
115633
|
+
var modelReasoningCapabilities = /* @__PURE__ */ new Map();
|
|
115621
115634
|
var DEFAULT_TEMPERATURE = 0.3;
|
|
115622
115635
|
function registerModelCapabilities(models = []) {
|
|
115623
115636
|
for (const entry of models ?? []) {
|
|
115624
115637
|
const id2 = typeof entry === "string" ? entry : entry?.id;
|
|
115638
|
+
if (!id2) continue;
|
|
115625
115639
|
const parameters = typeof entry === "object" && entry ? entry.supported_parameters : null;
|
|
115626
|
-
if (
|
|
115627
|
-
|
|
115640
|
+
if (Array.isArray(parameters)) modelSupportedParameters.set(String(id2), new Set(parameters.map(String)));
|
|
115641
|
+
const reasoning = typeof entry === "object" && entry ? entry.reasoning : null;
|
|
115642
|
+
if (!reasoning || typeof reasoning !== "object") continue;
|
|
115643
|
+
const efforts = Array.isArray(reasoning.supported_efforts) ? reasoning.supported_efforts.map((value) => String(value).trim().toLowerCase()).filter(Boolean) : [];
|
|
115644
|
+
const declaredDefault = String(reasoning.default_effort || "").trim().toLowerCase();
|
|
115645
|
+
modelReasoningCapabilities.set(String(id2), {
|
|
115646
|
+
mandatory: reasoning.mandatory === true,
|
|
115647
|
+
supportedEfforts: efforts,
|
|
115648
|
+
defaultEffort: efforts.includes(declaredDefault) ? declaredDefault : null
|
|
115649
|
+
});
|
|
115628
115650
|
}
|
|
115629
115651
|
}
|
|
115630
115652
|
function modelSupportsParameter(model, parameter) {
|
|
@@ -115632,6 +115654,34 @@ function modelSupportsParameter(model, parameter) {
|
|
|
115632
115654
|
if (!supported) return null;
|
|
115633
115655
|
return supported.has(String(parameter));
|
|
115634
115656
|
}
|
|
115657
|
+
function reasoningCapabilities(model) {
|
|
115658
|
+
return modelReasoningCapabilities.get(String(model || "")) ?? null;
|
|
115659
|
+
}
|
|
115660
|
+
function reasoningEffortsFor(model) {
|
|
115661
|
+
return reasoningCapabilities(model)?.supportedEfforts ?? [];
|
|
115662
|
+
}
|
|
115663
|
+
function defaultReasoningEffort(model) {
|
|
115664
|
+
return reasoningCapabilities(model)?.defaultEffort ?? null;
|
|
115665
|
+
}
|
|
115666
|
+
function requiresReasoning(model) {
|
|
115667
|
+
return reasoningCapabilities(model)?.mandatory === true;
|
|
115668
|
+
}
|
|
115669
|
+
function resolveReasoningEffort(model, requested) {
|
|
115670
|
+
const value = String(requested || "").trim().toLowerCase();
|
|
115671
|
+
if (!value) return null;
|
|
115672
|
+
const capabilities = reasoningCapabilities(model);
|
|
115673
|
+
if (!capabilities) return value;
|
|
115674
|
+
return capabilities.supportedEfforts.includes(value) ? value : null;
|
|
115675
|
+
}
|
|
115676
|
+
function reasoningOverrideFor(agent) {
|
|
115677
|
+
const effort = resolveReasoningEffort(agent?.model, agent?.reasoningEffort);
|
|
115678
|
+
return effort ? { effort } : { max_tokens: 256, exclude: false };
|
|
115679
|
+
}
|
|
115680
|
+
function truncationReasoningOverride(model) {
|
|
115681
|
+
const capabilities = reasoningCapabilities(model);
|
|
115682
|
+
if (!capabilities) return { effort: "low" };
|
|
115683
|
+
return capabilities.supportedEfforts.includes("low") ? { effort: "low" } : { max_tokens: 4096, exclude: false };
|
|
115684
|
+
}
|
|
115635
115685
|
function resolveTemperature(model, requested) {
|
|
115636
115686
|
if (modelSupportsParameter(model, "temperature") === false) return void 0;
|
|
115637
115687
|
return Number.isFinite(Number(requested)) ? Number(requested) : DEFAULT_TEMPERATURE;
|
|
@@ -115670,7 +115720,7 @@ function isReasoningModel(model) {
|
|
|
115670
115720
|
return /(?:^|[\/._:-])(qwen3|qwq|deepseek-[rv]\d|magistral|glm-(?:\d+[a-z]*|latest|flash-latest)|gpt-oss|nemotron|reason(?:ing)?|thinking|o1|o3|o4)(?:$|[\/._:-])/.test(id2);
|
|
115671
115721
|
}
|
|
115672
115722
|
function wantsReasoning(agent) {
|
|
115673
|
-
return isReasoningModel(agent?.model) || agent?.captureReasoning === true;
|
|
115723
|
+
return isReasoningModel(agent?.model) || agent?.captureReasoning === true || reasoningCapabilities(agent?.model)?.mandatory === true || Boolean(resolveReasoningEffort(agent?.model, agent?.reasoningEffort));
|
|
115674
115724
|
}
|
|
115675
115725
|
function effectiveProtocol(agent, connection) {
|
|
115676
115726
|
if (isOpenRouterConnection(connection) && isJevModel(agent?.model)) return "jev_decisions";
|
|
@@ -115776,6 +115826,13 @@ function playerCards(player) {
|
|
|
115776
115826
|
function playerStack(player) {
|
|
115777
115827
|
return asNumber(player?.stack ?? player?.chips ?? 0);
|
|
115778
115828
|
}
|
|
115829
|
+
function playersStillInTournament(players, eliminatedPlayerIds = []) {
|
|
115830
|
+
const eliminated = new Set(eliminatedPlayerIds ?? []);
|
|
115831
|
+
return (players ?? []).filter((player) => player?.id && !eliminated.has(player.id));
|
|
115832
|
+
}
|
|
115833
|
+
function playersWithChips(players) {
|
|
115834
|
+
return (players ?? []).filter((player) => player && playerStack(player) > 0);
|
|
115835
|
+
}
|
|
115779
115836
|
function currentBet(state, seat) {
|
|
115780
115837
|
return asNumber(mapGet(state?.currentBets, seat, state?.players?.[seat]?.bet ?? 0));
|
|
115781
115838
|
}
|
|
@@ -116023,7 +116080,7 @@ function assertDecisionState(state) {
|
|
|
116023
116080
|
}
|
|
116024
116081
|
}
|
|
116025
116082
|
const expectedRemaining = 1 + state.opponents.length;
|
|
116026
|
-
if (Number(state.tournament?.playersRemaining) !== expectedRemaining) throw new Error(`playersRemaining mismatch: expected ${expectedRemaining}, got ${state.tournament?.playersRemaining}`);
|
|
116083
|
+
if (Number(state.tournament?.playersRemaining) !== expectedRemaining) throw new Error(`playersRemaining mismatch: expected ${expectedRemaining} (hero plus ${state.opponents.length} non-eliminated opponents), got ${state.tournament?.playersRemaining}`);
|
|
116027
116084
|
return state;
|
|
116028
116085
|
}
|
|
116029
116086
|
function extractTextContent(message) {
|
|
@@ -116446,7 +116503,7 @@ Respond with one of: ${criteriaKeys.map((k) => `${k} = ${criteria[k]}`).join(";
|
|
|
116446
116503
|
}
|
|
116447
116504
|
if (isOpenRouter) {
|
|
116448
116505
|
body.provider = agent.provider ? { only: [agent.provider], allow_fallbacks: false, require_parameters: true } : { allow_fallbacks: true, require_parameters: true };
|
|
116449
|
-
if (wantsReasoning(agent)) body.reasoning = reasoning ??
|
|
116506
|
+
if (wantsReasoning(agent)) body.reasoning = reasoning ?? reasoningOverrideFor(agent);
|
|
116450
116507
|
}
|
|
116451
116508
|
if (stream) {
|
|
116452
116509
|
body.stream = true;
|
|
@@ -116509,7 +116566,7 @@ async function decideOpenAICompatible({ agent, connection, state, legalActions,
|
|
|
116509
116566
|
result = await execute(actualProtocol);
|
|
116510
116567
|
} else if (err?.truncated) {
|
|
116511
116568
|
truncationRetry = true;
|
|
116512
|
-
result = await execute(actualProtocol, { maxTokens: 4096, reasoning:
|
|
116569
|
+
result = await execute(actualProtocol, { maxTokens: 4096, reasoning: truncationReasoningOverride(agent.model) });
|
|
116513
116570
|
} else throw err;
|
|
116514
116571
|
}
|
|
116515
116572
|
const normalized = normalizeDecisionObject(result.obj, legalActions, decisionId);
|
|
@@ -116685,7 +116742,7 @@ async function requestChatStage({ agent, connection, schema, toolName, systemPro
|
|
|
116685
116742
|
}
|
|
116686
116743
|
if (isOpenRouter) {
|
|
116687
116744
|
body.provider = agent.provider ? { only: [agent.provider], allow_fallbacks: false, require_parameters: true } : { allow_fallbacks: true, require_parameters: true };
|
|
116688
|
-
if (wantsReasoning(agent)) body.reasoning = override.reasoning ??
|
|
116745
|
+
if (wantsReasoning(agent)) body.reasoning = override.reasoning ?? reasoningOverrideFor(agent);
|
|
116689
116746
|
}
|
|
116690
116747
|
if (onDelta) {
|
|
116691
116748
|
body.stream = true;
|
|
@@ -116729,7 +116786,7 @@ async function requestChatStage({ agent, connection, schema, toolName, systemPro
|
|
|
116729
116786
|
return { ...result, protocolFallback: "tool\u2192json_schema" };
|
|
116730
116787
|
}
|
|
116731
116788
|
if (err?.truncated) {
|
|
116732
|
-
const result = await execute(actual, { maxTokens: 4096, reasoning:
|
|
116789
|
+
const result = await execute(actual, { maxTokens: 4096, reasoning: truncationReasoningOverride(agent.model) });
|
|
116733
116790
|
return { ...result, protocolFallback: "truncated\u2192retry" };
|
|
116734
116791
|
}
|
|
116735
116792
|
throw err;
|
|
@@ -117002,6 +117059,8 @@ var els = {
|
|
|
117002
117059
|
seatProtocol: $("#seatProtocol"),
|
|
117003
117060
|
seatProvider: $("#seatProvider"),
|
|
117004
117061
|
seatCaptureReasoning: $("#seatCaptureReasoning"),
|
|
117062
|
+
seatReasoningEffort: $("#seatReasoningEffort"),
|
|
117063
|
+
seatReasoningEffortHelp: $("#seatReasoningEffortHelp"),
|
|
117005
117064
|
seatError: $("#seatError"),
|
|
117006
117065
|
removeSeatBtn: $("#removeSeatBtn"),
|
|
117007
117066
|
cancelSeatBtn: $("#cancelSeatBtn"),
|
|
@@ -117052,6 +117111,7 @@ var WINNER_REVIEW_MS = 2e3;
|
|
|
117052
117111
|
var seatAssignments = Array(MAX_LOBBY_SEATS).fill(null);
|
|
117053
117112
|
var editingSeatIndex = null;
|
|
117054
117113
|
var seatNameAuto = false;
|
|
117114
|
+
var seatReasoningEffortDraft = "";
|
|
117055
117115
|
var lobbyVisible = true;
|
|
117056
117116
|
var pendingAutostart = false;
|
|
117057
117117
|
var arenaMaxDecisions = 0;
|
|
@@ -117357,7 +117417,8 @@ function injectedEnvironmentConfig() {
|
|
|
117357
117417
|
model: String(player.model || ""),
|
|
117358
117418
|
protocol: String(player.protocol || "tool"),
|
|
117359
117419
|
provider: String(player.provider || ""),
|
|
117360
|
-
captureReasoning: Boolean(player.captureReasoning)
|
|
117420
|
+
captureReasoning: Boolean(player.captureReasoning),
|
|
117421
|
+
reasoningEffort: String(player.reasoningEffort || "").trim().toLowerCase()
|
|
117361
117422
|
}))
|
|
117362
117423
|
};
|
|
117363
117424
|
}
|
|
@@ -117674,7 +117735,7 @@ function normalizeConfig(input = {}) {
|
|
|
117674
117735
|
if (!connIds.has(raw.connectionId)) throw new Error(`Connection missing for ${name}`);
|
|
117675
117736
|
const model = String(raw.model || "").trim();
|
|
117676
117737
|
if (!model) throw new Error(`Model missing for ${name}`);
|
|
117677
|
-
return { id: `player-${lobbySeat + 1}`, seat: index, lobbySeat, name, connectionId: raw.connectionId, model, protocol: raw.protocol || "tool", provider: String(raw.provider || "").trim(), captureReasoning: raw.captureReasoning === true, temperature: clamp(Number(raw.temperature) || 0.3, 0, 2) };
|
|
117738
|
+
return { id: `player-${lobbySeat + 1}`, seat: index, lobbySeat, name, connectionId: raw.connectionId, model, protocol: raw.protocol || "tool", provider: String(raw.provider || "").trim(), captureReasoning: raw.captureReasoning === true, reasoningEffort: String(raw.reasoningEffort || "").trim().toLowerCase(), temperature: clamp(Number(raw.temperature) || 0.3, 0, 2) };
|
|
117678
117739
|
})
|
|
117679
117740
|
};
|
|
117680
117741
|
}
|
|
@@ -117750,7 +117811,7 @@ var TournamentDirector = class {
|
|
|
117750
117811
|
for (const player of this.config.players) {
|
|
117751
117812
|
this.engine.sit(player.seat, player.id, player.name, this.config.startingStack);
|
|
117752
117813
|
this.timeBanks[player.id] = this.config.timeBankSeconds * 1e3;
|
|
117753
|
-
this.stats[player.id] = { decisions: 0, invalid: 0, modelErrors: 0, providerErrors: 0, rateLimits: 0, timeouts: 0, autoFallbacks: 0, protocolFallbacks: 0, retries: 0, totalLatencyMs: 0, lastAction: null, lastActionType: null, lastActionAmount: null, lastReason: "" };
|
|
117814
|
+
this.stats[player.id] = { decisions: 0, invalid: 0, modelErrors: 0, providerErrors: 0, rateLimits: 0, timeouts: 0, autoFallbacks: 0, contextErrors: 0, protocolFallbacks: 0, retries: 0, totalLatencyMs: 0, lastAction: null, lastActionType: null, lastActionAmount: null, lastReason: "" };
|
|
117754
117815
|
}
|
|
117755
117816
|
this.logEvent("TOURNAMENT_START", { config: this.publicConfig() });
|
|
117756
117817
|
this.broadcast();
|
|
@@ -117794,8 +117855,15 @@ var TournamentDirector = class {
|
|
|
117794
117855
|
while (this.status === "PAUSED") await new Promise((resolve) => this.pauseResolvers.push(resolve));
|
|
117795
117856
|
if (this.status === "STOPPED") throw new Error("Tournament stopped");
|
|
117796
117857
|
}
|
|
117858
|
+
// Decision-context/table count: seated players that are not eliminated. Must
|
|
117859
|
+
// equal `1 + non-eliminated opponents` in serializeForAgent, so it must NOT be
|
|
117860
|
+
// derived from stacks — an all-in player has stack 0 mid-hand and is still in.
|
|
117797
117861
|
playersRemaining() {
|
|
117798
|
-
return (this.engine?.state?.players
|
|
117862
|
+
return playersStillInTournament(this.engine?.state?.players, this.eliminations.map((e) => e.playerId));
|
|
117863
|
+
}
|
|
117864
|
+
// Tournament-end / place accounting: seats that can still contest chips.
|
|
117865
|
+
playersWithChips() {
|
|
117866
|
+
return playersWithChips(this.engine?.state?.players);
|
|
117799
117867
|
}
|
|
117800
117868
|
tournamentMeta() {
|
|
117801
117869
|
const startingPlayers = this.config?.players.length ?? 0;
|
|
@@ -117862,9 +117930,9 @@ var TournamentDirector = class {
|
|
|
117862
117930
|
async run() {
|
|
117863
117931
|
while (["RUNNING", "PAUSED"].includes(this.status)) {
|
|
117864
117932
|
await this.waitIfPaused();
|
|
117865
|
-
const remaining = this.
|
|
117933
|
+
const remaining = this.playersWithChips();
|
|
117866
117934
|
if (remaining.length <= 1) {
|
|
117867
|
-
this.finish(remaining[0]
|
|
117935
|
+
this.finish(remaining[0] ?? null);
|
|
117868
117936
|
return;
|
|
117869
117937
|
}
|
|
117870
117938
|
if (this.handNumber > 0 && this.handNumber % this.config.handsPerLevel === 0) {
|
|
@@ -117875,10 +117943,18 @@ var TournamentDirector = class {
|
|
|
117875
117943
|
this.logEvent("BLINDS_UP_FAILED", { error: summarizeError(err) });
|
|
117876
117944
|
}
|
|
117877
117945
|
}
|
|
117878
|
-
|
|
117879
|
-
|
|
117880
|
-
|
|
117881
|
-
|
|
117946
|
+
try {
|
|
117947
|
+
await this.startHand();
|
|
117948
|
+
await this.playHand();
|
|
117949
|
+
this.completeHand();
|
|
117950
|
+
this.broadcast();
|
|
117951
|
+
this.handErrorStreak = 0;
|
|
117952
|
+
} catch (err) {
|
|
117953
|
+
if (this.status === "STOPPED" || err?.name === "AbortError") throw err;
|
|
117954
|
+
await this.recoverHand(err);
|
|
117955
|
+
this.handErrorStreak = (this.handErrorStreak || 0) + 1;
|
|
117956
|
+
if (this.handErrorStreak >= 3) throw err;
|
|
117957
|
+
}
|
|
117882
117958
|
if (this.budgetReached) {
|
|
117883
117959
|
this.logEvent("DECISION_BUDGET_REACHED", { decisions: this.decisionCount, budget: this.decisionBudget });
|
|
117884
117960
|
this.stop();
|
|
@@ -117888,6 +117964,52 @@ var TournamentDirector = class {
|
|
|
117888
117964
|
await sleep(Math.max(WINNER_REVIEW_MS, this.config.betweenHandsMs));
|
|
117889
117965
|
}
|
|
117890
117966
|
}
|
|
117967
|
+
// Last-resort safety net for a hand. A model failure or a decision-context
|
|
117968
|
+
// failure is already handled inside the hand loop; anything else that escapes
|
|
117969
|
+
// (a future regression) would otherwise abort the whole tournament mid-game.
|
|
117970
|
+
// Here the hand is finished with deterministic safe actions so the tournament
|
|
117971
|
+
// continues. Recovery is bounded by the hand-action guard and by the caller's
|
|
117972
|
+
// streak counter, so a genuinely broken engine still surfaces as an error
|
|
117973
|
+
// instead of looping forever.
|
|
117974
|
+
async recoverHand(triggerError) {
|
|
117975
|
+
this.currentDecision = null;
|
|
117976
|
+
if (this.handComplete()) {
|
|
117977
|
+
this.broadcast();
|
|
117978
|
+
return;
|
|
117979
|
+
}
|
|
117980
|
+
this.logEvent("HAND_RECOVERY", { handNumber: this.handNumber, street: this.engine?.state?.street ?? null, error: summarizeError(triggerError) });
|
|
117981
|
+
this.broadcast();
|
|
117982
|
+
let guard = 0;
|
|
117983
|
+
while (!this.handComplete()) {
|
|
117984
|
+
if (++guard > 500) throw new Error("Hand recovery guard exceeded 500 actions");
|
|
117985
|
+
await this.waitIfPaused();
|
|
117986
|
+
const state = this.engine.state, seat = state.actionTo;
|
|
117987
|
+
if (seat == null) {
|
|
117988
|
+
if (Array.isArray(state.winners) && state.winners.length) break;
|
|
117989
|
+
await sleep(10);
|
|
117990
|
+
continue;
|
|
117991
|
+
}
|
|
117992
|
+
const player = state.players?.[seat];
|
|
117993
|
+
if (!player) break;
|
|
117994
|
+
if (state.street === "SHOWDOWN") {
|
|
117995
|
+
const show = { type: ACTION.SHOW, playerId: player.id, cardIndices: [0, 1] };
|
|
117996
|
+
if (this.engine.validate(show)?.valid) {
|
|
117997
|
+
this.engine.act(show);
|
|
117998
|
+
continue;
|
|
117999
|
+
}
|
|
118000
|
+
const muck = { type: ACTION.MUCK, playerId: player.id };
|
|
118001
|
+
if (this.engine.validate(muck)?.valid) {
|
|
118002
|
+
this.engine.act(muck);
|
|
118003
|
+
continue;
|
|
118004
|
+
}
|
|
118005
|
+
}
|
|
118006
|
+
if (!this.forceSafeAction(seat, { error: triggerError, errorCategory: "recovery" })) break;
|
|
118007
|
+
this.broadcast();
|
|
118008
|
+
}
|
|
118009
|
+
if (!this.handComplete()) throw triggerError;
|
|
118010
|
+
this.completeHand();
|
|
118011
|
+
this.broadcast();
|
|
118012
|
+
}
|
|
117891
118013
|
pruneBustedSeats() {
|
|
117892
118014
|
const failures = [];
|
|
117893
118015
|
for (const p of this.engine?.state?.players ?? []) {
|
|
@@ -117968,22 +118090,31 @@ var TournamentDirector = class {
|
|
|
117968
118090
|
}
|
|
117969
118091
|
async takeDecision(agent, seat, legalActions) {
|
|
117970
118092
|
const decisionId = id(`d-h${this.handNumber}-s${seat + 1}`), baseMs = this.config.actionSeconds * 1e3, bankBefore = this.timeBanks[agent.id] ?? 0, totalMs = baseMs + bankBefore;
|
|
118093
|
+
const stats = this.stats[agent.id];
|
|
118094
|
+
const connection = this.config.connections.find((c) => c.id === agent.connectionId);
|
|
118095
|
+
const architecture = this.config.decisionArchitecture === DECISION_ARCHITECTURES.FLAT ? "flat" : "hierarchical";
|
|
117971
118096
|
const recentHands = buildPublicTournamentMemory(this.events, this.handNumber);
|
|
117972
118097
|
const publicPlayerStats = buildPublicPlayerStats(this.events, this.config.players, this.handNumber);
|
|
117973
118098
|
const actionHistory = buildCurrentHandPublicActions(this.events, this.handNumber);
|
|
117974
|
-
const baseState2 = serializeForAgent(this.engine, seat, this.tournamentMeta(), legalActions, recentHands, publicPlayerStats, actionHistory);
|
|
117975
|
-
const stateForAgent = assertDecisionState(applyBenchmarkMode(baseState2, this.config.benchmarkMode)), startedAt = Date.now();
|
|
117976
|
-
const connection = this.config.connections.find((c) => c.id === agent.connectionId);
|
|
117977
|
-
const architecture = this.config.decisionArchitecture === DECISION_ARCHITECTURES.FLAT ? "flat" : "hierarchical";
|
|
117978
118099
|
const sizesForFamily = (family) => legalAggressiveSizes(this.engine, seat, family);
|
|
117979
|
-
let hierarchy = null;
|
|
117980
|
-
|
|
117981
|
-
|
|
117982
|
-
|
|
117983
|
-
|
|
117984
|
-
|
|
118100
|
+
let stateForAgent = null, hierarchy = null;
|
|
118101
|
+
try {
|
|
118102
|
+
stateForAgent = assertDecisionState(applyBenchmarkMode(
|
|
118103
|
+
serializeForAgent(this.engine, seat, this.tournamentMeta(), legalActions, recentHands, publicPlayerStats, actionHistory),
|
|
118104
|
+
this.config.benchmarkMode
|
|
118105
|
+
));
|
|
118106
|
+
if (architecture === "hierarchical") {
|
|
118107
|
+
hierarchy = buildHierarchicalDecision(stateForAgent, { legalActions: stateForAgent.legalActions });
|
|
118108
|
+
if (hierarchy.aggressiveFamily) {
|
|
118109
|
+
const engineSizes = sizesForFamily(hierarchy.aggressiveFamily);
|
|
118110
|
+
hierarchy.stage2 = { ...hierarchy.stage2, sizes: engineSizes, criteria: sizeCriteria(engineSizes) };
|
|
118111
|
+
}
|
|
117985
118112
|
}
|
|
118113
|
+
} catch (contextError) {
|
|
118114
|
+
if (!this.forceSafeAction(seat, { error: contextError, errorCategory: "context", decisionId })) throw contextError;
|
|
118115
|
+
return;
|
|
117986
118116
|
}
|
|
118117
|
+
const startedAt = Date.now();
|
|
117987
118118
|
this.currentDecision = {
|
|
117988
118119
|
id: decisionId,
|
|
117989
118120
|
playerId: agent.id,
|
|
@@ -117992,6 +118123,7 @@ var TournamentDirector = class {
|
|
|
117992
118123
|
model: agent.model,
|
|
117993
118124
|
connection: connection?.name ?? agent.connectionId,
|
|
117994
118125
|
provider: agent.provider || "auto",
|
|
118126
|
+
reasoningEffort: agent.reasoningEffort || null,
|
|
117995
118127
|
protocol: effectiveProtocol(agent, connection),
|
|
117996
118128
|
handNumber: this.handNumber,
|
|
117997
118129
|
street: this.engine.state.street,
|
|
@@ -118009,7 +118141,6 @@ var TournamentDirector = class {
|
|
|
118009
118141
|
};
|
|
118010
118142
|
this.logEvent("DECISION_START", this.currentDecision);
|
|
118011
118143
|
this.broadcast();
|
|
118012
|
-
const stats = this.stats[agent.id];
|
|
118013
118144
|
let result = null, error = null, errorCategory = null, elapsed = 0;
|
|
118014
118145
|
const pausedTotal = () => (this.currentDecision?.pausedMs || 0) + (this.currentDecision?.pausedAt ? Math.max(0, Date.now() - this.currentDecision.pausedAt) : 0);
|
|
118015
118146
|
const recordIncident = (incident) => {
|
|
@@ -118140,6 +118271,7 @@ var TournamentDirector = class {
|
|
|
118140
118271
|
configuredModel: agent.model,
|
|
118141
118272
|
resolvedModel: result?.model ?? agent.model,
|
|
118142
118273
|
provider: agent.provider || "auto",
|
|
118274
|
+
reasoningEffort: agent.reasoningEffort || null,
|
|
118143
118275
|
action: { id: chosen.id, type: chosen.type, amount: chosen.amount, description: chosen.description },
|
|
118144
118276
|
forced,
|
|
118145
118277
|
legalActions: legalActions.map((a) => ({ id: a.id, type: a.type, amount: a.amount ?? null, description: a.description })),
|
|
@@ -118182,6 +118314,72 @@ var TournamentDirector = class {
|
|
|
118182
118314
|
}
|
|
118183
118315
|
this.currentDecision = null;
|
|
118184
118316
|
}
|
|
118317
|
+
// Deterministic safe action for one seat, used whenever a decision cannot be
|
|
118318
|
+
// produced (context failure or emergency hand recovery). It always picks a
|
|
118319
|
+
// legal engine action, records a normal DECISION event so public action
|
|
118320
|
+
// history, replay and stats stay complete, and marks the row `forced`. It
|
|
118321
|
+
// never consults a model and never sees masked-out information.
|
|
118322
|
+
// Returns the chosen action, or null when the seat has no legal action.
|
|
118323
|
+
forceSafeAction(seat, { error = null, errorCategory = "context", decisionId = null } = {}) {
|
|
118324
|
+
const state = this.engine.state;
|
|
118325
|
+
const player = state.players?.[seat];
|
|
118326
|
+
if (!player) return null;
|
|
118327
|
+
const legalActions = legalActionCandidates(this.engine, seat);
|
|
118328
|
+
const chosen = fallbackAction(legalActions);
|
|
118329
|
+
if (!chosen) return null;
|
|
118330
|
+
this.engine.act(chosen.engineAction);
|
|
118331
|
+
const agent = (this.config?.players ?? []).find((p) => p.id === player.id) ?? null;
|
|
118332
|
+
const connection = agent ? this.config.connections.find((c) => c.id === agent.connectionId) ?? null : null;
|
|
118333
|
+
const stats = this.stats?.[player.id];
|
|
118334
|
+
if (stats) {
|
|
118335
|
+
stats.decisions++;
|
|
118336
|
+
stats.autoFallbacks++;
|
|
118337
|
+
stats.contextErrors = (stats.contextErrors || 0) + 1;
|
|
118338
|
+
stats.lastAction = chosen.description;
|
|
118339
|
+
stats.lastActionType = chosen.type;
|
|
118340
|
+
stats.lastActionAmount = chosen.amount ?? null;
|
|
118341
|
+
stats.lastReason = `Automatic ${chosen.description} after ${errorCategory} error.`;
|
|
118342
|
+
}
|
|
118343
|
+
this.decisionCount++;
|
|
118344
|
+
if (this.decisionBudget > 0 && this.decisionCount >= this.decisionBudget) this.budgetReached = true;
|
|
118345
|
+
this.currentDecision = null;
|
|
118346
|
+
const protocol = agent ? effectiveProtocol(agent, connection) : "none";
|
|
118347
|
+
this.logEvent("DECISION", {
|
|
118348
|
+
decisionId: decisionId ?? id(`d-h${this.handNumber}-s${seat + 1}`),
|
|
118349
|
+
handNumber: this.handNumber,
|
|
118350
|
+
street: state.street,
|
|
118351
|
+
position: positionForSeat(state, seat),
|
|
118352
|
+
potBefore: totalPot(state),
|
|
118353
|
+
playerId: player.id,
|
|
118354
|
+
playerName: player.name,
|
|
118355
|
+
connection: connection?.name ?? agent?.connectionId ?? null,
|
|
118356
|
+
protocol,
|
|
118357
|
+
requestedProtocol: protocol,
|
|
118358
|
+
protocolFallback: null,
|
|
118359
|
+
configuredModel: agent?.model ?? null,
|
|
118360
|
+
resolvedModel: agent?.model ?? null,
|
|
118361
|
+
provider: agent?.provider || "auto",
|
|
118362
|
+
reasoningEffort: agent?.reasoningEffort || null,
|
|
118363
|
+
action: { id: chosen.id, type: chosen.type, amount: chosen.amount, description: chosen.description },
|
|
118364
|
+
forced: true,
|
|
118365
|
+
legalActions: legalActions.map((a) => ({ id: a.id, type: a.type, amount: a.amount ?? null, description: a.description })),
|
|
118366
|
+
latencyMs: 0,
|
|
118367
|
+
primaryDecisionLatencyMs: 0,
|
|
118368
|
+
timeBankUsedMs: 0,
|
|
118369
|
+
timeBankRemainingMs: this.timeBanks?.[player.id] ?? 0,
|
|
118370
|
+
publicReason: stats?.lastReason ?? "",
|
|
118371
|
+
usage: null,
|
|
118372
|
+
decisionMeta: { method: "safe-fallback", decisionArchitecture: "safe-fallback-v1", reason: errorCategory, error: error ? summarizeError(error) : null },
|
|
118373
|
+
reasoning: null,
|
|
118374
|
+
benchmarkMode: this.config?.benchmarkMode ?? DEFAULT_BENCHMARK_MODE,
|
|
118375
|
+
decisionArchitecture: this.config?.decisionArchitecture ?? DEFAULT_DECISION_ARCHITECTURE,
|
|
118376
|
+
representation: this.config?.representation ?? DEFAULT_REPRESENTATION_MODE,
|
|
118377
|
+
replay: null,
|
|
118378
|
+
errorCategory,
|
|
118379
|
+
error: error ? summarizeError(error) : null
|
|
118380
|
+
});
|
|
118381
|
+
return chosen;
|
|
118382
|
+
}
|
|
118185
118383
|
enqueueSpectatorExplanation({ decisionId, agent, connection, stateForAgent, chosen }) {
|
|
118186
118384
|
if (!connection || !["openai", "openrouter"].includes(connection.kind) || isJevModel(agent.model)) return this.explanationChain;
|
|
118187
118385
|
this.explanationChain = this.explanationChain.then(async () => {
|
|
@@ -118222,7 +118420,7 @@ var TournamentDirector = class {
|
|
|
118222
118420
|
const already = this.eliminations.some((e) => e.playerId === player.id);
|
|
118223
118421
|
return p && playerStack(p) <= 0 && !already ? { player, seat, startStack: asNumber(this.handStartStacks?.[player.id]) } : null;
|
|
118224
118422
|
}).filter(Boolean).sort((a, b) => b.startStack - a.startStack || a.seat - b.seat);
|
|
118225
|
-
const survivors = this.
|
|
118423
|
+
const survivors = this.playersWithChips().length;
|
|
118226
118424
|
newlyEliminated.forEach((row, index) => {
|
|
118227
118425
|
const elimination = { playerId: row.player.id, playerName: row.player.name, place: Math.max(2, survivors + 1 + index), handNumber: this.handNumber, at: Date.now() };
|
|
118228
118426
|
this.eliminations.push(elimination);
|
|
@@ -118380,6 +118578,7 @@ function renderModelOptions(models, currentValue = "") {
|
|
|
118380
118578
|
if (currentValue && !byId.has(currentValue)) byId.set(currentValue, { id: currentValue, name: currentValue });
|
|
118381
118579
|
const rows = [...byId.values()].sort((a, b) => a.id.localeCompare(b.id));
|
|
118382
118580
|
els.seatModelOptions.innerHTML = rows.map((model) => `<option value="${escapeHtml(model.id)}" label="${escapeHtml(model.name || model.id)}"></option>`).join("");
|
|
118581
|
+
updateSeatReasoningEffortOptions();
|
|
118383
118582
|
}
|
|
118384
118583
|
async function refreshSeatModelCatalog({ force = false } = {}) {
|
|
118385
118584
|
const connection = fullConnectionById(els.seatConnection?.value);
|
|
@@ -118433,9 +118632,41 @@ function defaultSeatDraft(seatIndex) {
|
|
|
118433
118632
|
model: conn?.kind === "typesafe" ? "jev-latest" : "",
|
|
118434
118633
|
protocol: conn?.kind === "typesafe" ? "jev_native" : "tool",
|
|
118435
118634
|
provider: "",
|
|
118436
|
-
captureReasoning: false
|
|
118635
|
+
captureReasoning: false,
|
|
118636
|
+
reasoningEffort: ""
|
|
118437
118637
|
};
|
|
118438
118638
|
}
|
|
118639
|
+
function updateSeatReasoningEffortOptions(selected) {
|
|
118640
|
+
const select = els.seatReasoningEffort;
|
|
118641
|
+
if (!select) return;
|
|
118642
|
+
const openRouter = isOpenRouterConnection(connectionById(els.seatConnection.value));
|
|
118643
|
+
const model = els.seatModel.value.trim();
|
|
118644
|
+
const efforts = openRouter ? reasoningEffortsFor(model) : [];
|
|
118645
|
+
const modelDefault = efforts.length ? defaultReasoningEffort(model) : null;
|
|
118646
|
+
const current = String(selected ?? seatReasoningEffortDraft ?? "").trim().toLowerCase();
|
|
118647
|
+
select.innerHTML = [
|
|
118648
|
+
'<option value="">Arena default (capped)</option>',
|
|
118649
|
+
...efforts.map((effort) => `<option value="${escapeHtml(effort)}">${escapeHtml(effort)}${effort === modelDefault ? " \xB7 model default" : ""}</option>`)
|
|
118650
|
+
].join("");
|
|
118651
|
+
if (efforts.includes(current)) {
|
|
118652
|
+
select.value = current;
|
|
118653
|
+
seatReasoningEffortDraft = current;
|
|
118654
|
+
} else {
|
|
118655
|
+
select.value = "";
|
|
118656
|
+
if (!current) seatReasoningEffortDraft = "";
|
|
118657
|
+
}
|
|
118658
|
+
select.disabled = !efforts.length;
|
|
118659
|
+
const help = els.seatReasoningEffortHelp;
|
|
118660
|
+
if (!help) return;
|
|
118661
|
+
help.dataset.tone = "";
|
|
118662
|
+
if (!openRouter) help.textContent = "Reasoning effort is available for OpenRouter models that advertise it.";
|
|
118663
|
+
else if (!efforts.length) help.textContent = "This model does not advertise reasoning efforts; the capped default is used.";
|
|
118664
|
+
else if (!select.value && requiresReasoning(model)) {
|
|
118665
|
+
help.textContent = `This model always reasons and ignores the capped default, so it can exceed the action clock and auto-fold. Pick an effort \u2014 ${efforts.includes("minimal") ? "minimal" : efforts[0]} is usually fastest.`;
|
|
118666
|
+
help.dataset.tone = "bad";
|
|
118667
|
+
} else if (modelDefault) help.textContent = `Model default is ${modelDefault}. Lower it when a slow reasoner runs out of action clock and auto-folds.`;
|
|
118668
|
+
else help.textContent = "Lower it when a slow reasoner runs out of action clock and auto-folds.";
|
|
118669
|
+
}
|
|
118439
118670
|
function applySeatProtocolRules() {
|
|
118440
118671
|
const conn = connectionById(els.seatConnection.value);
|
|
118441
118672
|
const kind = conn?.kind || "openrouter";
|
|
@@ -118444,6 +118675,7 @@ function applySeatProtocolRules() {
|
|
|
118444
118675
|
const jevSeat = kind === "typesafe" || jevViaOpenRouter;
|
|
118445
118676
|
document.querySelector(".seat-provider-field")?.classList.toggle("hidden", !openRouter || jevViaOpenRouter);
|
|
118446
118677
|
document.querySelector(".seat-reasoning-field")?.classList.toggle("hidden", jevSeat);
|
|
118678
|
+
document.querySelector(".seat-effort-field")?.classList.toggle("hidden", jevSeat);
|
|
118447
118679
|
if (els.seatCaptureReasoning) {
|
|
118448
118680
|
els.seatCaptureReasoning.disabled = jevSeat;
|
|
118449
118681
|
const alreadyReasoning = isReasoningModel(els.seatModel.value.trim());
|
|
@@ -118464,6 +118696,7 @@ function applySeatProtocolRules() {
|
|
|
118464
118696
|
els.seatProvider.disabled = !openRouter;
|
|
118465
118697
|
if (els.seatModel.value === "jev-latest") els.seatModel.value = "";
|
|
118466
118698
|
}
|
|
118699
|
+
updateSeatReasoningEffortOptions();
|
|
118467
118700
|
}
|
|
118468
118701
|
function updateSeatSummary() {
|
|
118469
118702
|
const count = seatAssignments.filter(Boolean).length;
|
|
@@ -118490,13 +118723,16 @@ function openSeatEditor(seatIndex) {
|
|
|
118490
118723
|
els.seatProtocol.value = draft.protocol || "tool";
|
|
118491
118724
|
els.seatProvider.value = draft.provider || "";
|
|
118492
118725
|
if (els.seatCaptureReasoning) els.seatCaptureReasoning.checked = Boolean(draft.captureReasoning);
|
|
118726
|
+
seatReasoningEffortDraft = String(draft.reasoningEffort || "").trim().toLowerCase();
|
|
118493
118727
|
applySeatProtocolRules();
|
|
118728
|
+
updateSeatReasoningEffortOptions(seatReasoningEffortDraft);
|
|
118494
118729
|
syncSeatNameFromModel();
|
|
118495
118730
|
void refreshSeatModelCatalog();
|
|
118496
118731
|
els.seatError.classList.add("hidden");
|
|
118497
118732
|
els.seatLockNotice.classList.toggle("hidden", !locked);
|
|
118498
118733
|
for (const control of [els.seatName, els.seatConnection, els.seatModel, els.seatProtocol, els.seatProvider]) control.disabled = locked || control === els.seatProvider && (!isOpenRouterConnection(connectionById(els.seatConnection.value)) || isJevModel(els.seatModel.value));
|
|
118499
118734
|
if (els.seatCaptureReasoning) els.seatCaptureReasoning.disabled = locked || els.seatCaptureReasoning.disabled;
|
|
118735
|
+
if (els.seatReasoningEffort) els.seatReasoningEffort.disabled = locked || els.seatReasoningEffort.disabled;
|
|
118500
118736
|
els.saveSeatBtn.disabled = locked;
|
|
118501
118737
|
els.removeSeatBtn.disabled = locked || !seatAssignments[seatIndex];
|
|
118502
118738
|
if (!els.seatDialog.open) els.seatDialog.showModal();
|
|
@@ -118514,7 +118750,8 @@ function readSeatDraft() {
|
|
|
118514
118750
|
model: els.seatModel.value.trim(),
|
|
118515
118751
|
protocol: els.seatProtocol.value,
|
|
118516
118752
|
provider: els.seatProvider.disabled ? "" : els.seatProvider.value.trim(),
|
|
118517
|
-
captureReasoning: Boolean(els.seatCaptureReasoning && !els.seatCaptureReasoning.disabled && els.seatCaptureReasoning.checked)
|
|
118753
|
+
captureReasoning: Boolean(els.seatCaptureReasoning && !els.seatCaptureReasoning.disabled && els.seatCaptureReasoning.checked),
|
|
118754
|
+
reasoningEffort: els.seatReasoningEffort && !els.seatReasoningEffort.disabled ? els.seatReasoningEffort.value.trim().toLowerCase() : ""
|
|
118518
118755
|
};
|
|
118519
118756
|
}
|
|
118520
118757
|
function readSeatPlayers() {
|
|
@@ -118543,7 +118780,8 @@ function restoreSeatAssignments(rows = []) {
|
|
|
118543
118780
|
model: player.model || "",
|
|
118544
118781
|
protocol: player.protocol || "tool",
|
|
118545
118782
|
provider: player.provider || "",
|
|
118546
|
-
captureReasoning: player.captureReasoning === true
|
|
118783
|
+
captureReasoning: player.captureReasoning === true,
|
|
118784
|
+
reasoningEffort: String(player.reasoningEffort || "").trim().toLowerCase()
|
|
118547
118785
|
};
|
|
118548
118786
|
});
|
|
118549
118787
|
updateSeatSummary();
|
|
@@ -119269,13 +119507,13 @@ function renderStats(s) {
|
|
|
119269
119507
|
const pc = (value) => `${Math.round(Number(value || 0) * 100)}%`;
|
|
119270
119508
|
return `<div class="stat-card"><div class="stat-top"><div><div class="stat-name">${escapeHtml(visiblePlayerName(p.name, p.model))}</div><div class="stat-model mono">${escapeHtml(p.model)} \xB7 ${escapeHtml(effectiveProtocol(p, s.config.connections.find((c) => c.id === p.connectionId)))}</div></div><strong>${fmt(tableP?.stack || 0)}</strong></div>
|
|
119271
119509
|
<div class="poker-profile"><div title="Voluntarily put chips in pot"><b>${pc(poker.vpipPct)}</b><span>VPIP</span></div><div title="Preflop raise"><b>${pc(poker.pfrPct)}</b><span>PFR</span></div><div title="Aggression frequency"><b>${pc(poker.aggressionPct)}</b><span>AFq</span></div><div title="Fold when folding was legal"><b>${pc(poker.foldPct)}</b><span>FOLD</span></div><div><b>${poker.sampleHands || 0}</b><span>HANDS</span></div></div>
|
|
119272
|
-
<div class="stat-values"><div class="metric"><b>${st.decisions || 0}</b><span>moves</span></div><div class="metric"><b>${avg}ms</b><span>avg</span></div><div class="metric"><b>${st.autoFallbacks || 0}</b><span>auto</span></div></div><div class="stat-reliability"><span><b>${st.modelErrors || 0}</b> model</span><span><b>${st.providerErrors || 0}</b> provider</span><span><b>${st.rateLimits || 0}</b> rate</span><span><b>${st.timeouts || 0}</b> timeout</span><span><b>${st.protocolFallbacks || 0}</b> protocol</span><span><b>${st.retries || 0}</b> retry</span></div></div>`;
|
|
119510
|
+
<div class="stat-values"><div class="metric"><b>${st.decisions || 0}</b><span>moves</span></div><div class="metric"><b>${avg}ms</b><span>avg</span></div><div class="metric"><b>${st.autoFallbacks || 0}</b><span>auto</span></div></div><div class="stat-reliability"><span><b>${st.modelErrors || 0}</b> model</span><span><b>${st.providerErrors || 0}</b> provider</span><span><b>${st.rateLimits || 0}</b> rate</span><span><b>${st.timeouts || 0}</b> timeout</span><span><b>${st.contextErrors || 0}</b> context</span><span><b>${st.protocolFallbacks || 0}</b> protocol</span><span><b>${st.retries || 0}</b> retry</span></div></div>`;
|
|
119273
119511
|
}).join("");
|
|
119274
119512
|
}
|
|
119275
119513
|
function tableRenderSignature(s) {
|
|
119276
119514
|
const running = ["RUNNING", "PAUSED"].includes(s?.status);
|
|
119277
119515
|
if (lobbyVisible && !running) {
|
|
119278
|
-
return JSON.stringify(["lobby", seatAssignments.map((p) => p ? [p.name, p.model, p.protocol, p.connectionId, p.provider, Boolean(p.captureReasoning)] : null)]);
|
|
119516
|
+
return JSON.stringify(["lobby", seatAssignments.map((p) => p ? [p.name, p.model, p.protocol, p.connectionId, p.provider, Boolean(p.captureReasoning), p.reasoningEffort || ""] : null)]);
|
|
119279
119517
|
}
|
|
119280
119518
|
const t = s?.table;
|
|
119281
119519
|
if (!t) return JSON.stringify(["empty", s?.status || "IDLE"]);
|
|
@@ -120659,6 +120897,9 @@ els.seatName.addEventListener("input", () => {
|
|
|
120659
120897
|
});
|
|
120660
120898
|
els.seatProtocol.addEventListener("change", applySeatProtocolRules);
|
|
120661
120899
|
els.seatCaptureReasoning?.addEventListener("change", applySeatProtocolRules);
|
|
120900
|
+
els.seatReasoningEffort?.addEventListener("change", () => {
|
|
120901
|
+
seatReasoningEffortDraft = els.seatReasoningEffort.value;
|
|
120902
|
+
});
|
|
120662
120903
|
els.refreshModelsBtn?.addEventListener("click", () => void refreshSeatModelCatalog({ force: true }));
|
|
120663
120904
|
els.removeSeatBtn.addEventListener("click", () => {
|
|
120664
120905
|
if (editingSeatIndex == null || director && ["RUNNING", "PAUSED"].includes(director.status)) return;
|
package/dist/styles.css
CHANGED
|
@@ -492,7 +492,7 @@ label input, label select { margin-top: 5px; font-weight: 450; }
|
|
|
492
492
|
.split-footer { justify-content: space-between; }
|
|
493
493
|
.seat-footer-actions, .replay-share-actions { display: flex; gap: 7px; }
|
|
494
494
|
.seat-editor-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 12px; }
|
|
495
|
-
.seat-model-field { grid-column: 1 / -1; }
|
|
495
|
+
.seat-model-field, .seat-effort-field { grid-column: 1 / -1; }
|
|
496
496
|
.model-input-wrap { margin-top: 6px; display: grid; grid-template-columns: minmax(0, 1fr) 38px; gap: 8px; }
|
|
497
497
|
.model-input-wrap input { margin: 0; }
|
|
498
498
|
.field-icon-button, .dialog-body .remove-connection { width: 38px; height: 38px; min-height: 38px; border-radius: 8px; }
|