Something went wrong. Try again.
small gleam coding and persistent agent daemon with a detachable cli and a canvas ui forked from okami.mom/albedo
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106import { listFamily, type Session } from './api';import { FAMILY_MS, familyDelay } from './agentTree';import { busy } from './live.svelte';import { sessions } from './sessions.svelte';
// The agents a session started (robot-docs/agents.md). A family is read whole// from its root, so a button in any window of it and the tree window show the// same agents, and they share one read of it rather than each making their own// (`familyDelay` says how often; a turn starting or ending reads it at once, `pokeFamily`).
/** Gets the family, or null when the first read failed and there is nothing to show yet. */type Listener = (family: Session[] | null) => void;
interface Watch { listeners: Set<Listener>; /** The newest family read, and its text: a read that changes nothing tells no one. */ last: Session[] | null; signature: string; poke: () => void; stop: () => void;}
const watches = new Map<string, Watch>();
function start(rootId: string): Watch { let timer: ReturnType<typeof setTimeout>; let reading = false; let again = false; const abort = new AbortController(); const watch: Watch = { listeners: new Set(), last: null, signature: '', poke: () => tick(), stop: () => {} };
const tick = async () => { clearTimeout(timer); // A tab that is hidden reads nothing; its return does (`shown`). if (document.hidden) return; if (reading) { again = true; return; } let delay = FAMILY_MS.retry; reading = true; const family = await listFamily(rootId, abort.signal).catch(() => null); reading = false; if (abort.signal.aborted) return; if (family) { // A session with no live worker (a closed agent) is stamped with the read's own time; that is no change. const signature = JSON.stringify(family, (key, value) => (key === 'observed_at' ? undefined : value)); if (signature !== watch.signature) { watch.signature = signature; watch.last = family; sessions.refreshAgents(family); for (const listener of watch.listeners) listener(family); } delay = familyDelay(family, rootId, busy, busy(sessions.byId.get(rootId))); } else if (!watch.last) { for (const listener of watch.listeners) listener(null); } if (again) { again = false; delay = 0; } timer = setTimeout(tick, delay); };
// A tab coming back reads the family now rather than after the long wait. const shown = () => !document.hidden && tick(); document.addEventListener('visibilitychange', shown); watch.stop = () => { clearTimeout(timer); abort.abort(); document.removeEventListener('visibilitychange', shown); }; tick(); return watch;}
/** * Follows a family while something shows it; `on` gets the newest read at * once if there is one, and each read that changes something after. Every * window of a family shares the one read. */export function watchFamily(rootId: string, on: Listener): () => void { let watch = watches.get(rootId); if (!watch) watches.set(rootId, (watch = start(rootId))); watch.listeners.add(on); if (watch.last) on(watch.last); const mine = watch; return () => { mine.listeners.delete(on); if (!mine.listeners.size) { mine.stop(); if (watches.get(rootId) === mine) watches.delete(rootId); } };}
/** Reads a family now, if anything is following it. */export function pokeFamily(rootId: string) { watches.get(rootId)?.poke();}
/** What an agent is doing, in a word. */export function stateOf(session: Session): 'working' | 'idle' | 'closed' { return session.closed ? 'closed' : busy(session) ? 'working' : 'idle';}