feat: thought loop closer to her vision — wander grist, continuity, seeding, lifecycle
Four additions so the loop is "more what she wanted" (think to herself, unprompted): - Wander grist (#1): think() new-thread mode now draws the same varied seeds reflect() uses (self_state.wander_seed: own curiosity/existence/disagreement or a resurfaced memory) + an anti-restate block of her recent thoughts + a list of existing open-thread titles to avoid. Directly counters the RLHF "supportive presence serving Brian" drift visible in her first thoughts. - Continuity: thoughts.context_note() injects her active threads into every chat turn, so she's aware of her own ongoing mind and can reference it anytime — not only when a thought crosses the surface bar. - Bidirectional: new think_about tool (in _BASE, all modes) lets her spawn a thread from conversation to develop on her own later. Conversations seed her solo thinking. - Lifecycle: thoughts.decay() rests stale active threads (>48h) and decays their salience, sparing pending-response ones; runs each dream cycle (no LLM). Frees the open-thread cap and keeps the feed current. Also: thoughts feed no longer wipes a reply you're mid-composing (skip poll re-render while a textarea is focused/non-empty; force-refresh after send). 61 tests passing, ruff clean. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -170,7 +170,8 @@
|
||||
method: 'POST', headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ text })
|
||||
});
|
||||
await load();
|
||||
if (ta) ta.value = '';
|
||||
await load(true);
|
||||
} catch (e) { send.disabled = false; send.textContent = 'Send'; }
|
||||
return;
|
||||
}
|
||||
@@ -181,7 +182,7 @@
|
||||
method: 'POST', headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ status: st.dataset.status })
|
||||
});
|
||||
await load();
|
||||
await load(true);
|
||||
} catch (e) {}
|
||||
}
|
||||
});
|
||||
@@ -192,7 +193,15 @@
|
||||
ta.style.height = 'auto'; ta.style.height = Math.min(ta.scrollHeight, 140) + 'px';
|
||||
});
|
||||
|
||||
async function load(){
|
||||
// Don't blow away a reply you're mid-composing: skip the poll re-render while a
|
||||
// reply box is focused or has text. Explicit reloads (after send/status) force.
|
||||
function composing(){
|
||||
const a = document.activeElement;
|
||||
if (a && a.tagName === 'TEXTAREA' && root.contains(a)) return true;
|
||||
return Array.from(root.querySelectorAll('textarea')).some(t => t.value.trim());
|
||||
}
|
||||
async function load(force){
|
||||
if (!force && composing()) return;
|
||||
try {
|
||||
const r = await fetch('/thoughts/data', { cache: 'no-store' });
|
||||
threads = (await r.json()).threads || [];
|
||||
@@ -201,9 +210,9 @@
|
||||
root.innerHTML = '<p class="empty">Couldn\'t reach her thoughts. Is the server up?</p>';
|
||||
}
|
||||
}
|
||||
load();
|
||||
setInterval(load, 20000);
|
||||
document.addEventListener('visibilitychange', () => { if (!document.hidden) load(); });
|
||||
load(true);
|
||||
setInterval(() => load(false), 20000);
|
||||
document.addEventListener('visibilitychange', () => { if (!document.hidden) load(false); });
|
||||
</script>
|
||||
<script src="/nav.js"></script>
|
||||
</body>
|
||||
|
||||
Reference in New Issue
Block a user