server-side TurnState in the harness, exposed via /api/state
new TurnState { Idle, Thinking, Compacting } on hive_ag3nt::events::Bus
with set_state + state_snapshot. the turn loops in hive-ag3nt and
hive-m1nd flip Thinking before drive_turn and Idle after; the
web_ui's /api/compact handler flips Compacting around compact_session.
per-agent /api/state grows turn_state + turn_state_since (unix
seconds). frontend prefers the server-reported state over the
client-derived one — setStateAbs takes the absolute since-time so
the 'last turn' chip reads the actual server-side duration instead
of the client's perceived gap between SSE events. SSE turn_start /
turn_end still drive state instantly between renders; /api/state
re-anchors on each turn_end refresh.
new compacting state gets its own purple badge with pulse
animation (mirrors thinking's amber). napping will slot in the
same way once the nap tool lands.
This commit is contained in:
parent
0385d96bf3
commit
637085644d
7 changed files with 94 additions and 32 deletions
|
|
@ -291,10 +291,11 @@
|
|||
// each second so the "· 12s" suffix stays current. State changes
|
||||
// trigger a short flash animation via .state-just-changed.
|
||||
const STATE_LABELS = {
|
||||
loading: { glyph: '…', text: 'booting' },
|
||||
offline: { glyph: '○', text: 'offline' },
|
||||
idle: { glyph: '💤', text: 'idle' },
|
||||
thinking: { glyph: '🧠', text: 'thinking' },
|
||||
loading: { glyph: '…', text: 'booting' },
|
||||
offline: { glyph: '○', text: 'offline' },
|
||||
idle: { glyph: '💤', text: 'idle' },
|
||||
thinking: { glyph: '🧠', text: 'thinking' },
|
||||
compacting: { glyph: '📦', text: 'compacting' },
|
||||
};
|
||||
let stateName = 'loading';
|
||||
let stateSince = Date.now();
|
||||
|
|
@ -318,19 +319,22 @@
|
|||
if (cancelBtn) cancelBtn.hidden = stateName !== 'thinking';
|
||||
}
|
||||
function setState(next) {
|
||||
if (next === stateName) return;
|
||||
// Capture the just-ending state's duration when leaving 'thinking'
|
||||
// so the operator can eyeball turn length without scrolling the
|
||||
// terminal back.
|
||||
setStateAbs(next, Math.floor(Date.now() / 1000));
|
||||
}
|
||||
/// Set state with an authoritative since-unix from the server. Lets
|
||||
/// `last turn` track the actual server-side duration rather than
|
||||
/// whatever the client perceived between SSE events.
|
||||
function setStateAbs(next, sinceUnix) {
|
||||
if (next === stateName && sinceUnix * 1000 === stateSince) return;
|
||||
if (stateName === 'thinking' && next !== 'thinking') {
|
||||
const elapsedMs = Date.now() - stateSince;
|
||||
renderLastTurn(elapsedMs);
|
||||
}
|
||||
const flashing = next !== stateName;
|
||||
stateName = next;
|
||||
stateSince = Date.now();
|
||||
stateSince = sinceUnix * 1000;
|
||||
const badge = $('state-badge');
|
||||
if (badge) {
|
||||
// Re-add the flash class so the animation replays.
|
||||
if (badge && flashing) {
|
||||
badge.classList.remove('state-just-changed');
|
||||
void badge.offsetWidth;
|
||||
badge.classList.add('state-just-changed');
|
||||
|
|
@ -411,11 +415,15 @@
|
|||
if (!headerSet) { setHeader(s.label, s.dashboard_port); headerSet = true; }
|
||||
renderTermInput(s.label, s.status === 'online');
|
||||
renderInbox(s.inbox || []);
|
||||
// Drive the state badge from the harness status. Live SSE events
|
||||
// override to 'thinking' / 'idle' as turns start/end; this only
|
||||
// kicks in for the not-online (offline) case and the initial seed.
|
||||
if (s.status !== 'online') setState('offline');
|
||||
else if (stateName === 'loading' || stateName === 'offline') setState('idle');
|
||||
// Authoritative state comes from the harness via /api/state.
|
||||
// Login-not-yet → 'offline'; otherwise use the server-reported
|
||||
// turn_state (idle / thinking / compacting). stateSince in
|
||||
// unix-seconds is converted to a client-side Date.now() anchor.
|
||||
if (s.status !== 'online') {
|
||||
setState('offline');
|
||||
} else if (s.turn_state) {
|
||||
setStateAbs(s.turn_state, s.turn_state_since);
|
||||
}
|
||||
// Skip the re-render if nothing structurally changed. The most
|
||||
// common case is `online` polling itself — without this guard, the
|
||||
// operator's <input value> gets clobbered every cycle.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue