Spaces:
Running
Running
Update app.js
Browse files
app.js
CHANGED
|
@@ -77,6 +77,26 @@ function applyAwardsAndMilestones(ps, winner, winnerTeam) {
|
|
| 77 |
stats[winner.id].awards.master_debater = (stats[winner.id].awards.master_debater || 0) + 1;
|
| 78 |
}
|
| 79 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 80 |
const ADAPTIVE_CYCLE = ['attack', 'defense', 'evidence', 'socratic', 'counterattack'];
|
| 81 |
const PHASES = [
|
| 82 |
{ id: 'opening', label: 'Opening' },
|
|
@@ -283,6 +303,7 @@ function renderRoster() {
|
|
| 283 |
<h3>${esc(a.name)}</h3>
|
| 284 |
<div class="spec">${esc(a.specialty)}</div>
|
| 285 |
<div class="style">${esc(a.style)} · estrategia ${a.strategy.toUpperCase()}</div>
|
|
|
|
| 286 |
${[['Lógica', a.stats.logic], ['Creatividad', a.stats.creativity], ['Agresividad', a.stats.aggression]].map(([k, v]) =>
|
| 287 |
`<div class="stat"><span>${k}</span><div class="bar"><div style="width:${v}%"></div></div><b>${v}</b></div>`).join('')}
|
| 288 |
<div class="record">🏆 ${st.wins}V · ${st.losses}D · ${st.draws}E · media ${avg} · ${st.matches} batallas</div>
|
|
@@ -512,6 +533,25 @@ function addSystemMsg(text) {
|
|
| 512 |
div.innerHTML = `<div class="mbody" style="width:100%"><div class="mtext">${esc(text)}</div></div>`;
|
| 513 |
log.appendChild(div); log.scrollTop = log.scrollHeight;
|
| 514 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 515 |
async function agentIntervention(agent, phase, round, extraInstr) {
|
| 516 |
const strat = strategyOf(agent, round);
|
| 517 |
const system = agent.system + ' ' + (STRATEGY_TEXT[strat] || '') + ' ' + RULES;
|
|
@@ -529,8 +569,9 @@ async function agentIntervention(agent, phase, round, extraInstr) {
|
|
| 529 |
const rivals = battle.participants.filter(x => x.team !== agent.team).map(x => x.name);
|
| 530 |
teamCtx = `\nEstás en el EQUIPO ${agent.team}${mates.length ? ' junto a ' + mates.join(', ') : ''}. Equipo rival (EQUIPO ${agent.team === 'A' ? 'B' : 'A'}): ${rivals.join(', ')}. Coordina con tu equipo: refuerza y extiende los puntos de tus compañeros, no los contradigas; concentra tus ataques en el equipo rival.`;
|
| 531 |
}
|
|
|
|
| 532 |
const rmCtx = battle.rematch ? `\nREVANCHA: ya se disputó una batalla anterior sobre este tema con estos participantes.\n${battle.rematch}\nMantén tu personalidad y estilo, pero AJUSTA tu estrategia: corrige los errores señalados, refuerza lo que funcionó y no repitas literalmente tus intervenciones anteriores.\n` : '';
|
| 533 |
-
const user = `Tema del debate: ${battle.topic}\nRonda ${round} de ${battle.rounds}.\n${phaseInstr}\n${extraInstr || ''}${teamCtx}${rmCtx}\n\n` +
|
| 534 |
(view ? `Intervenciones hasta ahora:\n${view}\n\n` : 'Aún no hay intervenciones.\n\n') +
|
| 535 |
`Ahora intervienes tú, ${agent.name}.`;
|
| 536 |
const div = document.createElement('div');
|
|
@@ -815,8 +856,12 @@ function finalizeBattle(verdict) {
|
|
| 815 |
battle.winnerTeam = winnerTeam;
|
| 816 |
battle.verdict = verdict;
|
| 817 |
battle.finishedAt = new Date().toISOString();
|
| 818 |
-
//
|
| 819 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 820 |
ps.forEach(a => {
|
| 821 |
const st = stats[a.id] = stats[a.id] || { wins: 0, losses: 0, draws: 0, scoreSum: 0, matches: 0, awards: {} };
|
| 822 |
st.matches++;
|
|
@@ -832,7 +877,7 @@ function finalizeBattle(verdict) {
|
|
| 832 |
applyAwardsAndMilestones(ps, winner, winnerTeam);
|
| 833 |
}
|
| 834 |
// guardar batalla (Arena Memory)
|
| 835 |
-
matches.unshift({ id: battle.id, topic: battle.topic, mode: battle.mode, rounds: battle.rounds, participants: ps.map(a => a.id), teams: battle.mode === 'team' ? Object.fromEntries(ps.map(a => [a.id, a.team])) : undefined, events: battle.events, scores: battle.scores, winner: battle.winner, winnerTeam, verdict, heuristic: !!battle.heuristicEval, simulated: MOCK || undefined, engine: MOCK ? 'simulated' : 'wllama', date: battle.finishedAt });
|
| 836 |
if (matches.length > 50) matches.length = 50;
|
| 837 |
persistAll();
|
| 838 |
renderResults(totals, winner, verdict);
|
|
@@ -885,11 +930,12 @@ function renderTournamentResults(champ) {
|
|
| 885 |
function renderResults(totals, winner, verdict) {
|
| 886 |
const heur = !!battle.heuristicEval;
|
| 887 |
$('#verdict-hero').innerHTML = `
|
| 888 |
-
<div class="crown">${heur ? '⚠️' : '👑'}</div>
|
| 889 |
-
<h1 style="font-size:1.8rem;font-weight:900">${heur ? '⚠️ Evaluación no fiable' : (battle.mode === 'team' ? (battle.winnerTeam ? `🛡️ EQUIPO ${battle.winnerTeam} gana la batalla` : '🤝 EMPATE ENTRE EQUIPOS') : (winner ? `<span style="color:${winner.color}">${winner.emoji} ${esc(winner.name)}</span> gana la batalla` : '🤝 EMPATE'))}</h1>
|
| 890 |
${!heur && battle.mode === 'team' ? `<p style="margin-top:6px;font-size:.95rem">Equipo A <b>${teamTotals().A}</b> — Equipo B <b>${teamTotals().B}</b></p>` : ''}
|
| 891 |
<p style="color:var(--muted);margin-top:8px;font-size:.9rem">${esc(battle.topic)}</p>
|
| 892 |
${heur ? `<p style="max-width:660px;margin:12px auto 0;font-size:.88rem;line-height:1.6;color:#f59e0b">El modelo no produjo evaluaciones estructuradas: no hay puntuaciones fiables para esta batalla. Solo se muestran métricas mecánicas (número y longitud de intervenciones), que NO son una valoración del debate. Esta batalla no cuenta para el Hall of Fame ni otorga premios.</p>` : ''}
|
|
|
|
| 893 |
${!heur && verdict?.justification ? `<p style="max-width:640px;margin:12px auto 0;font-size:.9rem;line-height:1.6">${esc(verdict.justification)}</p>` : ''}
|
| 894 |
${MOCK ? '<p style="margin-top:10px"><span class="mock-pill">🧪 Resultado SIMULADO (pruebas, sin modelo real)</span></p>' : ''}`;
|
| 895 |
document.querySelector('#mech-metrics')?.remove();
|
|
@@ -918,8 +964,8 @@ function renderResults(totals, winner, verdict) {
|
|
| 918 |
${verdict?.decisive ? `<p style="margin-top:6px"><b>Decisivo:</b> ${verdict.decisive.map(esc).join(' · ')}</p>` : ''}
|
| 919 |
${verdict?.mistakes ? `<p style="margin-top:6px"><b>Errores:</b> ${verdict.mistakes.map(esc).join(' · ')}</p>` : ''}</div>`;
|
| 920 |
// awards por criterio: solo con evaluación real; la heurística no otorga méritos
|
| 921 |
-
if (heur) {
|
| 922 |
-
$('#awards').innerHTML = '<span class="award">⚠️ Sin premios: evaluación no
|
| 923 |
} else {
|
| 924 |
const awards = [`<span class="award">🏆 ${battle.mode === 'team' ? (battle.winnerTeam ? 'Equipo ' + battle.winnerTeam + ' — Master Debaters' : 'Empate honorable') : (winner ? esc(winner.name) + ' — Master Debater' : 'Empate honorable')}</span>`];
|
| 925 |
[['logica', '🧠 Logic Master'], ['evidencia', '📚 Evidence Champion'], ['refutacion', '🛡️ Best Refutation']].forEach(([k, label]) => {
|
|
@@ -1119,7 +1165,7 @@ function renderHall() {
|
|
| 1119 |
return `<div class="match-item" data-match="${m.id}">
|
| 1120 |
<div class="mtopic">${esc(m.topic)}</div>
|
| 1121 |
<div class="mmeta">${MODES.find(x => x.id === m.mode)?.name || m.mode} · ${m.events.length} intervenciones · ${new Date(m.date).toLocaleString('es')}</div>
|
| 1122 |
-
<div class="mmeta">${m.simulated ? '🧪 Resultado simulado (pruebas, sin modelo real)' : m.heuristic ? '⚠️ sin evaluación fiable (no cuenta para el Hall)' : (m.mode === 'tournament' ? '🏆 Campeón: ' + (w ? esc(w.name) : '—') : m.winnerTeam ? '🛡️ Equipo ' + m.winnerTeam : '👑 ' + (w ? esc(w.name) : 'Empate'))}</div>
|
| 1123 |
<button class="btn ghost small" data-replay="${m.id}">▶ Replay</button>
|
| 1124 |
</div>`;
|
| 1125 |
}).join('') : '<p style="color:var(--muted);font-size:.85rem">Aún no hay batallas guardadas.</p>';
|
|
@@ -1180,5 +1226,5 @@ if (params.get('topic')) $('#topic').value = params.get('topic');
|
|
| 1180 |
|
| 1181 |
// gancho de pruebas (solo modo mock): inspección de estado interno
|
| 1182 |
IDB.init();
|
| 1183 |
-
if (MOCK) window.__arenaTest = { setBattle: b => { battle = b; }, getBattle: () => battle, agents: () => agents, stats: () => stats, finalizeBattle, applyAwardsAndMilestones };
|
| 1184 |
updateModelInUse();
|
|
|
|
| 77 |
stats[winner.id].awards.master_debater = (stats[winner.id].awards.master_debater || 0) + 1;
|
| 78 |
}
|
| 79 |
}
|
| 80 |
+
/* Calidad del veredicto: la RUTA fiable (!heuristic && !MOCK) no acredita CALIDAD.
|
| 81 |
+
Esquema: nota numérica 0-10 para cada participante y criterio.
|
| 82 |
+
Consistencia: el ganador declarado por el veredicto debe estar entre los máximos totales. */
|
| 83 |
+
function evaluationQualityOK(scores, verdict, ps, mode, winner) {
|
| 84 |
+
for (const a of ps) {
|
| 85 |
+
const sa = scores?.[a.id];
|
| 86 |
+
if (!sa) return false;
|
| 87 |
+
for (const [k] of WEIGHTS) {
|
| 88 |
+
const v = sa[k];
|
| 89 |
+
if (typeof v !== 'number' || isNaN(v) || v < 0 || v > 10) return false;
|
| 90 |
+
}
|
| 91 |
+
}
|
| 92 |
+
if (mode !== 'team' && verdict?.winner && verdict.winner !== 'Empate' && winner) {
|
| 93 |
+
const totals = ps.map(a => totalScore(a.id, scores));
|
| 94 |
+
const top = Math.max(...totals);
|
| 95 |
+
const topIds = ps.filter((a, i) => totals[i] === top).map(a => a.id);
|
| 96 |
+
if (!topIds.includes(winner.id)) return false;
|
| 97 |
+
}
|
| 98 |
+
return true;
|
| 99 |
+
}
|
| 100 |
const ADAPTIVE_CYCLE = ['attack', 'defense', 'evidence', 'socratic', 'counterattack'];
|
| 101 |
const PHASES = [
|
| 102 |
{ id: 'opening', label: 'Opening' },
|
|
|
|
| 303 |
<h3>${esc(a.name)}</h3>
|
| 304 |
<div class="spec">${esc(a.specialty)}</div>
|
| 305 |
<div class="style">${esc(a.style)} · estrategia ${a.strategy.toUpperCase()}</div>
|
| 306 |
+
${realBattleCount(a.id) ? `<div class="style" style="color:var(--accent2)">🧠 memoria: ${realBattleCount(a.id)} batalla(s) real(es)</div>` : ''}
|
| 307 |
${[['Lógica', a.stats.logic], ['Creatividad', a.stats.creativity], ['Agresividad', a.stats.aggression]].map(([k, v]) =>
|
| 308 |
`<div class="stat"><span>${k}</span><div class="bar"><div style="width:${v}%"></div></div><b>${v}</b></div>`).join('')}
|
| 309 |
<div class="record">🏆 ${st.wins}V · ${st.losses}D · ${st.draws}E · media ${avg} · ${st.matches} batallas</div>
|
|
|
|
| 533 |
div.innerHTML = `<div class="mbody" style="width:100%"><div class="mtext">${esc(text)}</div></div>`;
|
| 534 |
log.appendChild(div); log.scrollTop = log.scrollHeight;
|
| 535 |
}
|
| 536 |
+
function agentMemory(agentId) {
|
| 537 |
+
// memoria persistente: SOLO batallas con motor y evaluación verificados (nunca mock/heurística/inconsistente ni registros legacy sin motor)
|
| 538 |
+
const real = matches.filter(m => m.engine === 'wllama' && !m.heuristic && !m.inconsistent && m.participants.includes(agentId));
|
| 539 |
+
if (!real.length) return '';
|
| 540 |
+
const st = stats[agentId];
|
| 541 |
+
const parts = [];
|
| 542 |
+
if (st && st.matches) parts.push(`${st.matches} batallas evaluadas (${st.wins} victorias, ${st.losses} derrotas, ${st.draws} empates)`);
|
| 543 |
+
const recent = real.slice(0, 3).map(m => {
|
| 544 |
+
const res = m.mode === 'tournament' ? (m.winner === agentId ? 'ganaste el torneo' : 'no ganaste el torneo')
|
| 545 |
+
: m.winnerTeam ? ((m.teams && m.teams[agentId]) === m.winnerTeam ? 'ganó tu equipo' : 'perdió tu equipo')
|
| 546 |
+
: m.winner === agentId ? 'ganaste' : (m.winner ? 'perdiste' : 'empate');
|
| 547 |
+
return `«${m.topic}»: ${res}`;
|
| 548 |
+
});
|
| 549 |
+
if (recent.length) parts.push('últimas: ' + recent.join(' · '));
|
| 550 |
+
return parts.length ? `\nTu historial en esta arena (memoria persistente): ${parts.join('. ')}. Aprende de tus batallas anteriores: mantén lo que te dio victorias y corrige lo que te hizo perder.\n` : '';
|
| 551 |
+
}
|
| 552 |
+
function realBattleCount(agentId) {
|
| 553 |
+
return matches.filter(m => m.engine === 'wllama' && !m.heuristic && !m.inconsistent && m.participants.includes(agentId)).length;
|
| 554 |
+
}
|
| 555 |
async function agentIntervention(agent, phase, round, extraInstr) {
|
| 556 |
const strat = strategyOf(agent, round);
|
| 557 |
const system = agent.system + ' ' + (STRATEGY_TEXT[strat] || '') + ' ' + RULES;
|
|
|
|
| 569 |
const rivals = battle.participants.filter(x => x.team !== agent.team).map(x => x.name);
|
| 570 |
teamCtx = `\nEstás en el EQUIPO ${agent.team}${mates.length ? ' junto a ' + mates.join(', ') : ''}. Equipo rival (EQUIPO ${agent.team === 'A' ? 'B' : 'A'}): ${rivals.join(', ')}. Coordina con tu equipo: refuerza y extiende los puntos de tus compañeros, no los contradigas; concentra tus ataques en el equipo rival.`;
|
| 571 |
}
|
| 572 |
+
const memCtx = agentMemory(agent.id);
|
| 573 |
const rmCtx = battle.rematch ? `\nREVANCHA: ya se disputó una batalla anterior sobre este tema con estos participantes.\n${battle.rematch}\nMantén tu personalidad y estilo, pero AJUSTA tu estrategia: corrige los errores señalados, refuerza lo que funcionó y no repitas literalmente tus intervenciones anteriores.\n` : '';
|
| 574 |
+
const user = `Tema del debate: ${battle.topic}\nRonda ${round} de ${battle.rounds}.\n${phaseInstr}\n${extraInstr || ''}${teamCtx}${rmCtx}${memCtx}\n\n` +
|
| 575 |
(view ? `Intervenciones hasta ahora:\n${view}\n\n` : 'Aún no hay intervenciones.\n\n') +
|
| 576 |
`Ahora intervienes tú, ${agent.name}.`;
|
| 577 |
const div = document.createElement('div');
|
|
|
|
| 856 |
battle.winnerTeam = winnerTeam;
|
| 857 |
battle.verdict = verdict;
|
| 858 |
battle.finishedAt = new Date().toISOString();
|
| 859 |
+
// calidad del veredicto: esquema + consistencia; sin ella no cuenta para stats/logros aunque la ruta fuese real
|
| 860 |
+
const qualityOK = !battle.heuristicEval && !MOCK && evaluationQualityOK(battle.scores, verdict, ps, battle.mode, winner);
|
| 861 |
+
battle.evalInconsistent = !battle.heuristicEval && !MOCK && !qualityOK;
|
| 862 |
+
if (battle.evalInconsistent) { winner = null; battle.winner = null; battle.winnerTeam = null; }
|
| 863 |
+
// stats: solo cuentan batallas con evaluación real Y consistente del modelo (ni heurísticas ni simuladas ni inconsistentes)
|
| 864 |
+
if (qualityOK) {
|
| 865 |
ps.forEach(a => {
|
| 866 |
const st = stats[a.id] = stats[a.id] || { wins: 0, losses: 0, draws: 0, scoreSum: 0, matches: 0, awards: {} };
|
| 867 |
st.matches++;
|
|
|
|
| 877 |
applyAwardsAndMilestones(ps, winner, winnerTeam);
|
| 878 |
}
|
| 879 |
// guardar batalla (Arena Memory)
|
| 880 |
+
matches.unshift({ id: battle.id, topic: battle.topic, mode: battle.mode, rounds: battle.rounds, participants: ps.map(a => a.id), teams: battle.mode === 'team' ? Object.fromEntries(ps.map(a => [a.id, a.team])) : undefined, events: battle.events, scores: battle.scores, winner: battle.winner, winnerTeam, verdict, heuristic: !!battle.heuristicEval, inconsistent: !!battle.evalInconsistent || undefined, simulated: MOCK || undefined, engine: MOCK ? 'simulated' : 'wllama', date: battle.finishedAt });
|
| 881 |
if (matches.length > 50) matches.length = 50;
|
| 882 |
persistAll();
|
| 883 |
renderResults(totals, winner, verdict);
|
|
|
|
| 930 |
function renderResults(totals, winner, verdict) {
|
| 931 |
const heur = !!battle.heuristicEval;
|
| 932 |
$('#verdict-hero').innerHTML = `
|
| 933 |
+
<div class="crown">${heur || battle.evalInconsistent ? '⚠️' : '👑'}</div>
|
| 934 |
+
<h1 style="font-size:1.8rem;font-weight:900">${heur ? '⚠️ Evaluación no fiable' : battle.evalInconsistent ? '⚠️ Veredicto inconsistente (no superó la validación)' : (battle.mode === 'team' ? (battle.winnerTeam ? `🛡️ EQUIPO ${battle.winnerTeam} gana la batalla` : '🤝 EMPATE ENTRE EQUIPOS') : (winner ? `<span style="color:${winner.color}">${winner.emoji} ${esc(winner.name)}</span> gana la batalla` : '🤝 EMPATE'))}</h1>
|
| 935 |
${!heur && battle.mode === 'team' ? `<p style="margin-top:6px;font-size:.95rem">Equipo A <b>${teamTotals().A}</b> — Equipo B <b>${teamTotals().B}</b></p>` : ''}
|
| 936 |
<p style="color:var(--muted);margin-top:8px;font-size:.9rem">${esc(battle.topic)}</p>
|
| 937 |
${heur ? `<p style="max-width:660px;margin:12px auto 0;font-size:.88rem;line-height:1.6;color:#f59e0b">El modelo no produjo evaluaciones estructuradas: no hay puntuaciones fiables para esta batalla. Solo se muestran métricas mecánicas (número y longitud de intervenciones), que NO son una valoración del debate. Esta batalla no cuenta para el Hall of Fame ni otorga premios.</p>` : ''}
|
| 938 |
+
${battle.evalInconsistent ? `<p style="max-width:660px;margin:12px auto 0;font-size:.88rem;line-height:1.6;color:#f59e0b">El veredicto del modelo no superó la validación de esquema/consistencia (ganador declarado ≠ puntuaciones o notas fuera de rango): esta batalla no cuenta para el Hall of Fame ni otorga premios.</p>` : ''}
|
| 939 |
${!heur && verdict?.justification ? `<p style="max-width:640px;margin:12px auto 0;font-size:.9rem;line-height:1.6">${esc(verdict.justification)}</p>` : ''}
|
| 940 |
${MOCK ? '<p style="margin-top:10px"><span class="mock-pill">🧪 Resultado SIMULADO (pruebas, sin modelo real)</span></p>' : ''}`;
|
| 941 |
document.querySelector('#mech-metrics')?.remove();
|
|
|
|
| 964 |
${verdict?.decisive ? `<p style="margin-top:6px"><b>Decisivo:</b> ${verdict.decisive.map(esc).join(' · ')}</p>` : ''}
|
| 965 |
${verdict?.mistakes ? `<p style="margin-top:6px"><b>Errores:</b> ${verdict.mistakes.map(esc).join(' · ')}</p>` : ''}</div>`;
|
| 966 |
// awards por criterio: solo con evaluación real; la heurística no otorga méritos
|
| 967 |
+
if (heur || battle.evalInconsistent) {
|
| 968 |
+
$('#awards').innerHTML = '<span class="award">⚠️ Sin premios: evaluación no fiable o inconsistente</span>';
|
| 969 |
} else {
|
| 970 |
const awards = [`<span class="award">🏆 ${battle.mode === 'team' ? (battle.winnerTeam ? 'Equipo ' + battle.winnerTeam + ' — Master Debaters' : 'Empate honorable') : (winner ? esc(winner.name) + ' — Master Debater' : 'Empate honorable')}</span>`];
|
| 971 |
[['logica', '🧠 Logic Master'], ['evidencia', '📚 Evidence Champion'], ['refutacion', '🛡️ Best Refutation']].forEach(([k, label]) => {
|
|
|
|
| 1165 |
return `<div class="match-item" data-match="${m.id}">
|
| 1166 |
<div class="mtopic">${esc(m.topic)}</div>
|
| 1167 |
<div class="mmeta">${MODES.find(x => x.id === m.mode)?.name || m.mode} · ${m.events.length} intervenciones · ${new Date(m.date).toLocaleString('es')}</div>
|
| 1168 |
+
<div class="mmeta">${m.simulated ? '🧪 Resultado simulado (pruebas, sin modelo real)' : m.heuristic ? '⚠️ sin evaluación fiable (no cuenta para el Hall)' : m.inconsistent ? '⚠️ veredicto inconsistente (no cuenta para el Hall)' : (m.mode === 'tournament' ? '🏆 Campeón: ' + (w ? esc(w.name) : '—') : m.winnerTeam ? '🛡️ Equipo ' + m.winnerTeam : '👑 ' + (w ? esc(w.name) : 'Empate'))}</div>
|
| 1169 |
<button class="btn ghost small" data-replay="${m.id}">▶ Replay</button>
|
| 1170 |
</div>`;
|
| 1171 |
}).join('') : '<p style="color:var(--muted);font-size:.85rem">Aún no hay batallas guardadas.</p>';
|
|
|
|
| 1226 |
|
| 1227 |
// gancho de pruebas (solo modo mock): inspección de estado interno
|
| 1228 |
IDB.init();
|
| 1229 |
+
if (MOCK) window.__arenaTest = { setBattle: b => { battle = b; }, getBattle: () => battle, agents: () => agents, stats: () => stats, finalizeBattle, applyAwardsAndMilestones, evaluationQualityOK, agentMemory };
|
| 1230 |
updateModelInUse();
|