/dmn, /sleep, /wake, /pause now lock MindState directly from the
UI event loop. No MindMessage roundtrip needed — they're just
state transitions + info display.
MindMessage reduced to: Hotkey (Interrupt, CycleAutonomy),
NewSession, Score. Everything else handled directly by UI.
Co-Authored-By: Kent Overstreet <kent.overstreet@linux.dev>
MindState (behind Arc<Mutex<>>) holds all cognitive state: DMN,
turn tracking, pending input, scoring, error counters. Pure state
transition methods (take_pending_input, complete_turn, dmn_tick)
return Action values instead of directly spawning turns.
Mind is now just the event loop: lock MindState, call state methods,
execute returned actions (spawn turns, send UiMessages). No state
of its own except agent handle, turn handle, and watch channel.
mind/mod.rs: 957 → 586 lines.
Co-Authored-By: Kent Overstreet <kent.overstreet@linux.dev>
Add MindState behind Arc<Mutex<>> for state shared between Mind
and UI. Pending user input goes through shared state instead of
MindMessage::UserInput — UI pushes, Mind consumes.
Mind checks for pending input after every event (message received,
turn completed, DMN tick). User input is prioritized over DMN ticks.
This enables the UI to display/edit/cancel queued messages, and
removes the last MindMessage variant that carried data.
Co-Authored-By: Kent Overstreet <kent.overstreet@linux.dev>
cycle_reasoning, kill_processes, and AdjustSampling only need the
Agent lock — they're pure Agent operations. Handle them directly
in the UI event loop instead of routing through Mind.
Mind now only receives Interrupt and CycleAutonomy as hotkeys,
which genuinely need Mind state (turn handles, DMN state).
mind/mod.rs: 957 → 688 lines across the session.
Co-Authored-By: Kent Overstreet <kent.overstreet@linux.dev>
All slash command routing now lives in user/event_loop.rs. Mind
receives typed messages (NewSession, Score, DmnSleep, etc.) and
handles them as named methods. No more handle_command() dispatch
table or Command enum.
Commands that only need Agent state (/model, /retry) run directly
in the UI task. Commands that need Mind state (/new, /score, /dmn,
/sleep, /wake, /pause) send a MindMessage.
Mind is now purely: turn lifecycle, DMN state machine, and the
named handlers for each message type.
Co-Authored-By: Kent Overstreet <kent.overstreet@linux.dev>
Add tokio::sync::watch for turn_in_progress state. Commands in the
UI event loop can wait for turns to complete via wait_for() instead
of checking-and-bailing.
Move /retry to event_loop: waits for turn completion, pops agent
history, sends retried text as MindMessage::UserInput. Mind doesn't
need to know about retry — it just sees a new input message.
Make agent field pub on Mind for UI access.
Co-Authored-By: Kent Overstreet <kent.overstreet@linux.dev>
/quit, /help, /save handled directly in the UI event loop.
/model and /model <name> moved to event_loop as cmd_switch_model().
Mind no longer needs tui::App for any command handling.
Mind's handle_command now only has commands that genuinely need
Mind state: /new, /retry, /score (turn_in_progress, DMN, scoring).
Co-Authored-By: Kent Overstreet <kent.overstreet@linux.dev>
Mind::run() owns the cognitive event loop: user input, turn results,
DMN ticks, hotkey actions. The UI event loop (user/event_loop.rs) owns
the terminal: key events, render ticks, channel status display.
They communicate through channels: UI sends MindMessage (user input,
hotkey actions) to Mind. Mind sends UiMessage (status, info) to UI.
UI reads shared state (active tools, context) directly for rendering.
Removes direct coupling between Mind and App:
- cycle_reasoning no longer takes &mut App
- AdjustSampling updates agent only, UI reads from shared state
- /quit handled by UI directly, not routed through Mind
Co-Authored-By: Kent Overstreet <kent.overstreet@linux.dev>
The cognitive state machine is a Mind, not a Session. This is the
struct that AgentCycle will hang off of when we add subagent forking.
Co-Authored-By: Kent Overstreet <kent.overstreet@linux.dev>
Memory scoring now uses the graph as source of truth:
- last_scored timestamp on each node (new capnp field @22)
- Nodes scored when older than scoring_interval_secs (default 1hr)
- Oldest-scored-first ordering
- Window: scoring_response_window assistant responses (default 100)
- First-quarter memories scored even without full window
- Per-response normalization (raw divergence / response count)
- Asymmetric weight update: alpha=0.5 up, alpha=0.1 down
(responds fast to importance, decays slowly — memories stay
surfaced even if only useful 1/4 of the time)
Graph writes disabled pending normalization calibration.
Also: configurable scoring_interval_secs and scoring_response_window.
Co-Authored-By: Proof of Concept <poc@bcachefs.org>
Disables memory scoring, surface, and observe agents when set.
Useful for testing with external backends (e.g. OpenRouter) where
background agent traffic would be slow and unnecessary.
Co-Authored-By: Kent Overstreet <kent.overstreet@linux.dev>
F5 screen now shows temperature, top_p, top_k with interactive
adjustment:
- Up/down: select parameter
- Left/right: adjust value (0.05 steps for temp/top_p, 5 for top_k)
- Updates Agent and display immediately via HotkeyAction
Co-Authored-By: Proof of Concept <poc@bcachefs.org>
The agent lock was held for the entire duration of turn() — including
API streaming and tool dispatch awaits. This blocked the UI thread
whenever it needed the lock (render tick, compaction check, etc.),
causing 20+ second freezes.
Fix: turn() takes Arc<Mutex<Agent>> and manages locking internally.
Lock is held briefly for prepare/process phases, released during all
I/O (streaming, tool awaits, sleep retries). Also:
- check_compaction: spawns task instead of awaiting on event loop
- start_memory_scoring: already spawned, no change needed
- dispatch_tool_call_unlocked: drops lock before tool handle await
- Subconscious screen: renders all agents from state dynamically
(no more hardcoded SUBCONSCIOUS_AGENTS list)
- Memory scoring shows n/m progress in snapshots
Co-Authored-By: Proof of Concept <poc@bcachefs.org>