import { resetEnvironment, stepEnvironment } from './api.js';
import {
ACTION_KINDS,
AUTHORITIES,
DATA_TYPES,
RESOURCE_TYPES,
SEVERITIES,
buildAction,
formatAction,
labelize,
normalizeRegionSelection,
resourceValue,
} from './actions.js';
import { narrateCouncil } from './council-narration.js';
import { goToFrame, setReplayTrace, stopReplay, toggleReplayPlayback } from './replay.js';
import { SAMPLE_TRACE } from './samples.js';
import { AppState, addTraceFrame, setState, showToast, subscribe } from './state.js';
const app = document.getElementById('app');
let liveAutoplayTimer = null;
function init() {
const first = SAMPLE_TRACE[0];
setState({
replayTrace: SAMPLE_TRACE,
replayIndex: 0,
observation: first.observation,
reward: first.reward,
done: first.done,
council: narrateCouncil(first.observation, first.reward),
});
subscribe(render);
render();
}
function render() {
const regionPatch = normalizeRegionSelection(AppState);
if (Object.keys(regionPatch).length) {
setState(regionPatch);
return;
}
const council = narrateCouncil(AppState.observation, AppState.reward || 0);
AppState.council = council;
app.innerHTML = `
${renderTopbar()}
${renderCommandbar()}
${renderPhasePanel(council)}
${renderWorldPanel()}
${renderResourcesPanel()}
${renderActionPanel(council)}
${renderTimelinePanel()}
${renderCouncilPanel(council)}
${renderReplayPanel()}
`;
bindEvents();
}
function renderTopbar() {
return `
`;
}
function renderCommandbar() {
return `
`;
}
function renderPhasePanel(council) {
const obs = AppState.observation;
return `
${metric('Tick', obs?.tick ?? '-', `${obs?.ticks_remaining ?? '-'} left`)}
${metric('Phase', council.phase, `round ${council.round}`)}
${metric('Budget', council.budget || '-', 'cognition')}
${metric('Reward', formatReward(AppState.reward), `total ${formatReward(AppState.totalReward)}`)}
`;
}
function renderWorldPanel() {
const obs = AppState.observation;
if (!obs) return panel('World State', '
No observation loaded.
');
const regions = obs.regions || [];
const maxScore = Math.max(...regions.map((region) => pressure(region)), 1);
const html = `
${regions.map((region) => renderRegion(region, pressure(region) / maxScore)).join('')}
`;
return panel('World State', html);
}
function renderRegion(region, heat) {
const hot = heat > 0.78 ? 'hot' : '';
return `
${region.region}
${Math.round(heat * 100)} pressure
${statline('Cases', clamp(region.reported_cases_d_ago / 180, 0, 1), region.reported_cases_d_ago)}
${statline('Load', region.hospital_load, pct(region.hospital_load), 'load')}
${statline('Comply', region.compliance_proxy, pct(region.compliance_proxy), 'comp')}
`;
}
function renderResourcesPanel() {
const obs = AppState.observation;
const resources = obs?.resources || {};
const constraints = obs?.legal_constraints || [];
const restrictions = obs?.active_restrictions || [];
return panel('Resources and Constraints', `
${RESOURCE_TYPES.map((type) => `
${labelize(type)}
${resourceValue(resources, type)}
`).join('')}
Active restrictions
${restrictions.length ? restrictions.map((item) => `
${item.region}
${labelize(item.severity)} movement limits
${item.ticks_remaining} ticks
`).join('') : '
None active.
'}
Legal constraints
${constraints.length ? constraints.map((item) => `
${item.rule_id}
Blocks ${item.blocked_action}
${item.unlock_via}
`).join('') : '
No active legal blockers.
'}
`);
}
function renderActionPanel(council) {
const selected = AppState.selectedActionKind;
return panel('Task Controls', `
${ACTION_KINDS.map((kind) => `
`).join('')}
`);
}
function renderActionInputs(kind) {
const regions = AppState.observation?.regions || [{ region: AppState.selectedRegion || 'R1' }];
const regionSelect = `
`;
const resourceSelect = (bindName, label) => `
`;
const quantity = `
`;
if (kind === 'deploy_resource') {
return `${regionSelect}${resourceSelect('selectedResource', 'Resource')}${quantity}
`;
}
if (kind === 'request_data') {
return `
${regionSelect}
`;
}
if (kind === 'restrict_movement') {
return `
${regionSelect}
`;
}
if (kind === 'escalate') {
return `
`;
}
if (kind === 'reallocate_budget') {
return `${resourceSelect('selectedResource', 'From')}${resourceSelect('selectedToResource', 'To')}${quantity}
`;
}
return 'No parameters required.
';
}
function renderCouncilPanel(council) {
return panel('Narrated Council', `
Frontend visualization derived from observations. It is not a live Cortex runtime or LLM council.
Converged action
${formatAction(council.decision)}
${council.rationale}
${council.recommendations.map((report) => renderBrain(report)).join('')}
Preserved dissent
${council.preservedDissent.length ? council.preservedDissent.map((item, index) => `
D${index + 1}
${item}
`).join('') : '
No dissent preserved on this frame.
'}
`);
}
function renderBrain(report) {
return `
${report.name}
${Math.round(report.confidence * 100)} conf
${formatAction(report.action)}
${report.summary}
Challenge
${report.challenge}
Minority
${formatAction(report.minority)}
`;
}
function renderTimelinePanel() {
const log = AppState.observation?.recent_action_log || [];
const liveTrace = AppState.liveTrace || [];
return panel('Final Action Timeline', `
${log.length ? log.map((entry) => `
T${entry.tick}
${formatAction(entry.action)}
${entry.accepted ? 'accepted' : 'rejected'}
`).join('') : '
No actions have been submitted yet.
'}
Live trace frames captured this session: ${liveTrace.length}
`);
}
function renderReplayPanel() {
const max = Math.max(0, AppState.replayTrace.length - 1);
return panel('Replay', `
${AppState.replayIndex + 1}/${AppState.replayTrace.length || 1}
Replay changes the displayed frame only. It does not step the environment.
`);
}
function panel(title, body) {
return `
`;
}
function metric(label, value, hint) {
return `
${label}
${value}
${hint}
`;
}
function statline(label, value, display, extraClass = '') {
return `
${label}
${display}
`;
}
function bindEvents() {
document.querySelectorAll('[data-bind]').forEach((element) => {
element.addEventListener('change', () => {
const key = element.dataset.bind;
const value = element.type === 'number' || element.type === 'range'
? Number.parseInt(element.value, 10)
: element.value;
setState({ [key]: value });
});
if (element.type === 'range') {
element.addEventListener('input', () => {
const key = element.dataset.bind;
setState({ [key]: Number.parseInt(element.value, 10) });
});
}
});
document.querySelectorAll('[data-kind]').forEach((button) => {
button.addEventListener('click', () => setState({ selectedActionKind: button.dataset.kind }));
});
document.querySelectorAll('[data-action]').forEach((element) => {
element.addEventListener('click', () => handleAction(element.dataset.action, element));
if (element.dataset.action === 'scrub-replay') {
element.addEventListener('input', () => {
stopReplay();
goToFrame(Number.parseInt(element.value, 10));
});
}
});
}
async function handleAction(actionName, element) {
if (actionName === 'open-web') {
window.location.href = '/web/';
return;
}
if (actionName === 'load-sample') {
stopLiveAutoplay();
setReplayTrace(SAMPLE_TRACE);
setState({
mode: 'sample',
statusMessage: 'Sample trace loaded',
council: narrateCouncil(SAMPLE_TRACE[0].observation, SAMPLE_TRACE[0].reward),
});
showToast('Sample trace loaded', 'success');
return;
}
if (actionName === 'use-live-trace') {
if (!AppState.liveTrace.length) return;
setReplayTrace(AppState.liveTrace);
setState({ mode: 'replay', statusMessage: 'Live trace replay' });
return;
}
if (actionName === 'toggle-replay') {
toggleReplayPlayback();
return;
}
if (actionName === 'scrub-replay') {
stopReplay();
goToFrame(Number.parseInt(element.value, 10));
return;
}
if (actionName === 'reset-live') {
await resetLive();
return;
}
if (actionName === 'submit-action') {
await submitAction(buildAction(AppState));
return;
}
if (actionName === 'apply-recommendation') {
await submitAction(AppState.council?.decision || { kind: 'no_op' });
return;
}
if (actionName === 'toggle-live-autoplay') {
toggleLiveAutoplay();
}
}
async function resetLive() {
stopReplay();
stopLiveAutoplay();
setState({ connection: 'resetting', statusMessage: 'Resetting live episode' });
try {
const data = await resetEnvironment({
taskName: AppState.taskName,
seed: AppState.seed,
maxTicks: AppState.maxTicks,
});
const frame = {
label: `Live reset: ${AppState.taskName}`,
action: null,
observation: data.observation,
reward: data.reward ?? 0,
done: Boolean(data.done),
};
setState({
mode: 'live',
connection: 'connected',
observation: data.observation,
reward: data.reward ?? 0,
done: Boolean(data.done),
totalReward: 0,
liveTrace: [frame],
replayTrace: [frame],
replayIndex: 0,
statusMessage: 'Live episode ready',
});
showToast('Live episode reset', 'success');
} catch (error) {
setState({ connection: 'error', statusMessage: 'Reset failed' });
showToast(`Reset failed: ${error.message}`, 'error');
}
}
async function submitAction(payload) {
if (AppState.mode !== 'live') {
showToast('Reset a live episode before submitting actions.', 'error');
return;
}
if (AppState.done) {
showToast('Episode is terminal. Reset to continue.', 'error');
return;
}
setState({ connection: 'stepping', statusMessage: `Submitting ${payload.kind}` });
try {
const data = await stepEnvironment(payload);
const reward = Number(data.reward ?? 0);
const frame = {
label: `Submitted ${formatAction(payload)}`,
action: payload,
observation: data.observation,
reward,
done: Boolean(data.done),
};
addTraceFrame(frame);
setState({
mode: 'live',
connection: 'connected',
observation: data.observation,
reward,
done: Boolean(data.done),
totalReward: AppState.totalReward + reward,
statusMessage: data.done ? 'Episode complete' : 'Action accepted',
});
if (data.done) {
stopLiveAutoplay();
showToast('Episode complete', 'success');
}
} catch (error) {
stopLiveAutoplay();
setState({ connection: 'error', statusMessage: 'Step failed' });
showToast(`Step failed: ${error.message}`, 'error');
}
}
function toggleLiveAutoplay() {
if (AppState.autoplayLive) {
stopLiveAutoplay();
return;
}
if (AppState.mode !== 'live' || AppState.done) return;
setState({ autoplayLive: true });
liveAutoplayTimer = window.setInterval(async () => {
if (AppState.done || AppState.mode !== 'live') {
stopLiveAutoplay();
return;
}
await submitAction(AppState.council?.decision || { kind: 'no_op' });
}, AppState.replaySpeedMs);
}
function stopLiveAutoplay() {
if (liveAutoplayTimer) {
window.clearInterval(liveAutoplayTimer);
liveAutoplayTimer = null;
}
if (AppState.autoplayLive) setState({ autoplayLive: false });
}
function option(value, label, selected) {
return ``;
}
function pressure(region) {
return region.reported_cases_d_ago / 1000 + region.hospital_load * 1.55 + (1 - region.compliance_proxy) * 0.75;
}
function pct(value) {
return `${Math.round((value || 0) * 100)}%`;
}
function formatReward(value) {
return typeof value === 'number' ? value.toFixed(2) : '-';
}
function clamp(value, min, max) {
return Math.max(min, Math.min(max, value));
}
init();