Commit graph

820 commits

Author SHA1 Message Date
Kent Overstreet
3c4220c079 event_loop: run() takes &Mind instead of individual handles
Simplifies the interface — run() receives one reference to Mind
and extracts agent, shared, turn_watch locally. Reduces parameter
count from 7 to 5.

Also: command table data structure (SlashCommand) and commands()
function for single source of truth. send_help uses it. Dispatch
refactor to follow.

Also: fix input submission — diff before push, clone after push,
so prev_mind captures the input for consumption detection.

Co-Authored-By: Kent Overstreet <kent.overstreet@linux.dev>
2026-04-05 16:53:05 -04:00
Kent Overstreet
2b9aba0e5d fix shutdown hang and slow startup
Mind::run() breaks when input_rx channel closes (UI shut down).
Previously the DMN timer kept the loop alive forever.

UI renders immediately without blocking on agent lock. Conversation
replay happens lazily on the render tick via try_lock — the UI
shows "consciousness v0.3" instantly, fills in model info and
conversation history once Mind init completes.

Co-Authored-By: Kent Overstreet <kent.overstreet@linux.dev>
2026-04-05 16:18:10 -04:00
Kent Overstreet
71351574be event_loop: display user input when Mind consumes it
Show user text in the conversation window when the MindState diff
detects input was consumed (prev.input non-empty, cur.input empty).
Input stays editable in the text area until Mind takes it.

Co-Authored-By: Kent Overstreet <kent.overstreet@linux.dev>
2026-04-05 16:13:04 -04:00
Kent Overstreet
755a359078 supervisor: PID file to prevent duplicate daemon spawns
Multiple supervisor instances (Mind init + channel polling) could
both see no socket and start the same daemon. The socket hasn't
bound yet by the time the second check runs.

Write a PID file on spawn, check it in is_alive(). kill(pid, 0)
verifies the process is still running. Stale PID files are cleaned
up automatically.

Co-Authored-By: Kent Overstreet <kent.overstreet@linux.dev>
2026-04-05 13:30:56 -04:00
Kent Overstreet
58737a2cef channel_log: shared disk logging with round-trip
Move IRC's disk logging to thalamus/channel_log.rs as shared
functions: append_disk_log() and load_disk_log(). Any daemon
can use them — opt-in, not mandatory (tmux won't use them).

load_disk_log() populates a ChannelLog from disk on startup,
so history survives daemon restarts.

IRC daemon now uses the shared functions.

Co-Authored-By: Kent Overstreet <kent.overstreet@linux.dev>
2026-04-05 13:26:42 -04:00
Kent Overstreet
a6ffe9e086 telegram: move token to secrets file
Read token from channels/telegram.secrets/token instead of the
json5 config. Keeps secrets out of config files.

Co-Authored-By: Kent Overstreet <kent.overstreet@linux.dev>
2026-04-05 13:09:55 -04:00
Kent Overstreet
6d1411f2a1 move irc/telegram data under channels/
IRC logs → channels/irc.logs/
Telegram logs + offset → channels/telegram.logs/
Channel data now lives with channel infrastructure.

Co-Authored-By: Kent Overstreet <kent.overstreet@linux.dev>
2026-04-05 13:06:27 -04:00
Kent Overstreet
8c1fef3c69 poc-daemon: subscribe to channel notifications, drop config.rs
Wire poc-daemon into channel daemon notifications via subscribe_all().
Channel notifications (IRC, telegram, tmux) now flow through the
existing notification pipeline instead of the dead module system.

Remove claude/config.rs — daemon config is fully covered by
channel config files in ~/.consciousness/channels/.

Co-Authored-By: Kent Overstreet <kent.overstreet@linux.dev>
2026-04-05 12:58:46 -04:00
Kent Overstreet
1941624249 cargo-dedupe 2026-04-05 12:15:34 -04:00
Kent Overstreet
93bc49959c deps: replace tracing with log+env_logger, drop console-subscriber
Switch all tracing::{info,warn,error} to log::{info,warn,error}.
Replace tracing_subscriber::fmt::init() with env_logger::init().
Drop tracing, tracing-subscriber, tracing-appender as direct deps.
Drop console feature from jobkit (was pulling in console-subscriber
which pulled in tracing-subscriber).

tracing still compiled as transitive dep of reqwest and tui-markdown,
but our code no longer depends on it.

Co-Authored-By: Kent Overstreet <kent.overstreet@linux.dev>
2026-04-05 06:54:11 -04:00
Kent Overstreet
917960cb76 deps: remove faer (224 transitive crates)
Spectral decomposition (eigenvalue computation) removed — it was
only used by the spectral-save CLI command. The spectral embedding
reader and query engine features remain (they load pre-computed
embeddings from disk, no faer needed).

Removes: faer, nano-gemm, private-gemm, and ~220 other transitive
dependencies. Significant build time and artifact size reduction.

Co-Authored-By: Kent Overstreet <kent.overstreet@linux.dev>
2026-04-05 06:39:47 -04:00
Kent Overstreet
c1a5638be5 deps: switch reqwest to rustls, drop aws-lc-sys
Use rustls instead of default native-tls (aws-lc-sys) for HTTPS.
Saves ~80 MB of build artifacts. Applied to both main crate and
telegram channel daemon.

Also: tracing default-features = false (Kent's edit).

Co-Authored-By: Kent Overstreet <kent.overstreet@linux.dev>
2026-04-05 06:31:50 -04:00
Kent Overstreet
8d045a3e6b use ratatui::crossterm re-exports, add event-stream feature
All crossterm imports go through ratatui::crossterm. Direct crossterm
dep kept only for the event-stream feature flag (EventStream for
async terminal input).

Co-Authored-By: Kent Overstreet <kent.overstreet@linux.dev>
2026-04-05 06:22:31 -04:00
Kent Overstreet
bacfd5f234 rust edition 2024 2026-04-05 06:20:16 -04:00
Kent Overstreet
2ab4fd1c92 Trim unused deps
Signed-off-by: Kent Overstreet <kent.overstreet@linux.dev>
2026-04-05 06:06:38 -04:00
Kent Overstreet
7b75296457 Update reqwest
Signed-off-by: Kent Overstreet <kent.overstreet@linux.dev>
2026-04-05 06:02:25 -04:00
Kent Overstreet
3f79bba27a Update json5
Signed-off-by: Kent Overstreet <kent.overstreet@linux.dev>
2026-04-05 06:01:24 -04:00
Kent Overstreet
120ffabfaa Kill socket, read/write subcommands
Redundant with channels

Signed-off-by: Kent Overstreet <kent.overstreet@linux.dev>
2026-04-05 05:58:51 -04:00
Kent Overstreet
3b15c690ec event_loop: diff MindState on input submission, fix input display
Extract diff_mind_state() — reused on render tick and input submit.
When pushing user input, lock shared, diff (catches any Mind state
changes), push input, snapshot. The next diff sees the input was
consumed → displays it.

Fixes: user text not appearing in conversation window.

Co-Authored-By: Kent Overstreet <kent.overstreet@linux.dev>
2026-04-05 05:58:51 -04:00
Kent Overstreet
7dc515b985 mind: remove Arc from MindState
MindState is now std::sync::Mutex<MindState> owned by Mind, not
Arc-wrapped. Background scoring completion signals through a
BgEvent channel instead of locking shared directly. Retry sends
a Turn command instead of pushing to shared input.

No Arc on Mind (scoped tasks), no Arc on MindState (owned by Mind).
Only Arc<Mutex<Agent>> remains — needed for background turn spawns.

Co-Authored-By: Kent Overstreet <kent.overstreet@linux.dev>
Signed-off-by: Kent Overstreet <kent.overstreet@linux.dev>
2026-04-05 05:58:51 -04:00
Kent Overstreet
5eaba3c951 mind: use tokio-scoped for Mind/UI loop lifetimes
Both event loops borrow &mind through a scoped spawn — no Arc on
Mind needed. Interior Arcs on agent/shared stay (background spawns
need 'static), but event_loop::run() now takes &Arc refs instead
of cloned Arcs.

Co-Authored-By: Kent Overstreet <kent.overstreet@linux.dev>
2026-04-05 05:58:51 -04:00
Kent Overstreet
aae9687de2 mind: Mind is fully &self — no &mut needed
Move turn_handle into MindState (behind the mutex). All Mind
methods now take &self. Mind can be shared across tasks without
Arc — it's Send + Sync and immutable from the outside.

Manual Clone impl for MindState skips turn_handle (not needed
for UI diffing).

Co-Authored-By: Kent Overstreet <kent.overstreet@linux.dev>
2026-04-05 05:58:51 -04:00
Kent Overstreet
8e3137fe3f mind: static assert Mind is Send + Sync
Mind is already Send + Sync — all fields use Arc or sync primitives.
Add compile-time assertion so it stays that way.

Co-Authored-By: Kent Overstreet <kent.overstreet@linux.dev>
2026-04-05 05:58:50 -04:00
Kent Overstreet
d5a706147a mind: keep supervisor alive on Mind struct
The channel daemon supervisor was created in init() and immediately
dropped. Keep it on Mind so it can restart crashed daemons.

Co-Authored-By: Kent Overstreet <kent.overstreet@linux.dev>
2026-04-05 04:32:11 -04:00
Kent Overstreet
57b0f94b54 move startup orchestration from mind to event_loop
The top-level run() that creates Mind, wires channels, spawns the
Mind event loop, and starts the UI event loop is orchestration —
it belongs with the UI entry point, not in the cognitive layer.

Renamed to event_loop::start(cli). mind/mod.rs is now purely the
Mind struct: state machine, MindCommand, and the run loop.

Co-Authored-By: Kent Overstreet <kent.overstreet@linux.dev>
2026-04-05 04:29:56 -04:00
Kent Overstreet
e449cda40f mind: absorb agent creation into Mind::new()
Mind::new() takes config + ui_tx + turn_tx, creates the agent,
conversation log, shared state internally. The top-level run()
is now just: load config, create channels, Mind::new, init, spawn,
run event_loop.

Co-Authored-By: Kent Overstreet <kent.overstreet@linux.dev>
2026-04-05 04:20:49 -04:00
Kent Overstreet
91033fe754 mind: clean startup split between Mind and event_loop
Mind::init() now handles channel daemons, observation socket, and
scoring startup. event_loop::run() creates its own idle state and
channel polling — UI concerns owned by the UI.

Startup run() is now: create agent, create Mind, init, spawn Mind,
run event_loop. Seven parameters on event_loop::run() instead of
twelve.

Remove observe_input_rx from event loop (being replaced by channel).

Co-Authored-By: Kent Overstreet <kent.overstreet@linux.dev>
2026-04-05 04:17:04 -04:00
Kent Overstreet
9d597b5eff mind: zero UI dependencies — init() + run() split
Mind::init() restores conversation from log and starts scoring.
No UiMessages sent. The UI event loop reads Mind's state after
init and displays startup info (model, restored conversation)
by reading the agent directly.

mind/mod.rs has zero UiMessage imports or sends. Complete
separation between cognitive state machine and user interface.

Co-Authored-By: Kent Overstreet <kent.overstreet@linux.dev>
2026-04-05 04:02:16 -04:00
Kent Overstreet
3ee1aa69b0 mind: zero UiMessages from Mind's run loop
UserInput display moved to UI diff — when MindState.input goes from
populated to empty (consumed by a turn), the UI displays it. Mind
no longer sends any UiMessage from its event loop.

Remaining UiMessages are only in the startup function (one-time
init info).

Co-Authored-By: Kent Overstreet <kent.overstreet@linux.dev>
2026-04-05 03:54:01 -04:00
Kent Overstreet
84fe757260 mind: remove DMN helper methods, inline field assignments
dmn_sleep/dmn_wake/dmn_pause/cycle_autonomy were just setting two
fields each. Inline the assignments at call sites. cycle_autonomy
moves to event_loop since it's UI state machine logic (deciding
which label to show).

Co-Authored-By: Kent Overstreet <kent.overstreet@linux.dev>
2026-04-05 03:50:21 -04:00
Kent Overstreet
556a56035b mind: inline compaction (not async), remove check_compaction
Compaction is CPU-only on in-memory data — no reason to spawn a
task. Inline it in run_commands as a synchronous agent lock + compact.

Co-Authored-By: Kent Overstreet <kent.overstreet@linux.dev>
2026-04-05 03:43:53 -04:00
Kent Overstreet
2d6a17e773 mind: move CycleAutonomy to event_loop, simplify MindCommand
CycleAutonomy just flips DMN state — handled directly in event_loop
by locking shared MindState. MindCommand::Hotkey replaced with
MindCommand::Interrupt — the only command that needs Mind's async
context (abort handles, kill processes).

Remove dmn_interval() wrapper — inline self.dmn.interval().

MindCommand is now: Turn, Compact, Score, Interrupt, NewSession, None.

Co-Authored-By: Kent Overstreet <kent.overstreet@linux.dev>
2026-04-05 03:41:47 -04:00
Kent Overstreet
402bae4178 mind: restore age_out_images and publish_context_state after turns
These were called from handle_turn_result before the refactor but
got lost during the MindState migration. Re-add them in the turn
completion path. Delete the trivial refresh_context_state wrapper.

Co-Authored-By: Kent Overstreet <kent.overstreet@linux.dev>
2026-04-05 03:38:42 -04:00
Kent Overstreet
01b07a7f28 mind: unify MindCommand, add command queue pattern
MindCommand replaces both Action and MindMessage — one type for
everything: turns, compaction, scoring, hotkeys, new session.

State methods return MindCommand values. The run loop collects
commands into a Vec, then drains them through run_commands().
Compact and Score now flow through the same command path as
everything else.

Removes execute(), MindMessage from event_loop. Mind's run loop
is now: select! → collect commands → run_commands().

mind/mod.rs: 957 → 516 lines.

Co-Authored-By: Kent Overstreet <kent.overstreet@linux.dev>
2026-04-05 03:34:43 -04:00
Kent Overstreet
07ca136c14 mind: double-buffer MindState for UI diffing
UI event loop clones MindState on each render tick, diffs against
the previous copy, and generates status updates from changes. Mind
no longer sends UiMessage::StatusUpdate — state changes are detected
automatically by the UI.

Removes update_status from both Mind and event_loop. DMN state
changes, turn tracking, scoring status all flow through the diff.

Zero UiMessage sends from Mind's run loop for state changes.

Co-Authored-By: Kent Overstreet <kent.overstreet@linux.dev>
2026-04-05 03:24:08 -04:00
Kent Overstreet
54cd3783eb mind: move send_context_info and update_status to event_loop
Both are pure UI operations that read config/shared state and format
display messages. No Mind state mutation involved.

Co-Authored-By: Kent Overstreet <kent.overstreet@linux.dev>
2026-04-05 03:12:50 -04:00
Kent Overstreet
4eb0c891c4 mind: move DMN commands to event_loop, remove MindMessage variants
/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>
2026-04-05 03:08:36 -04:00
Kent Overstreet
7adc333219 mind: move state to MindState, Mind becomes thin event loop
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>
2026-04-05 03:05:28 -04:00
Kent Overstreet
792e9440af mind: shared MindState for pending input
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>
2026-04-05 02:52:56 -04:00
Kent Overstreet
05d6bbc912 move hotkey handlers from Mind to event_loop
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>
2026-04-05 02:44:58 -04:00
Kent Overstreet
64add58caa mind: move all slash commands to event_loop dispatch
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>
2026-04-05 02:40:45 -04:00
Kent Overstreet
b05c956ab8 mind: add turn_watch, move /retry to event_loop
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>
2026-04-05 02:37:51 -04:00
Kent Overstreet
178824fa01 move UI commands from Mind to event_loop
/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>
2026-04-05 02:29:44 -04:00
Kent Overstreet
804d55a702 mind: split event loop — Mind and UI run independently
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>
2026-04-05 02:11:32 -04:00
Kent Overstreet
1f06b49503 mind: rename Session to Mind
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>
2026-04-05 01:55:14 -04:00
Kent Overstreet
390b6c6c0a more reorg 2026-04-05 01:48:11 -04:00
ProofOfConcept
fcd77fb79e training: per-node scoring with graph weight updates
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>
2026-04-05 01:18:47 -04:00
Kent Overstreet
b0603fd1ef fix model_name switch 2026-04-05 01:18:47 -04:00
Kent Overstreet
59aaaa5742 drop strip_md_keys() 2026-04-05 01:18:47 -04:00
Kent Overstreet
40ecd63099 drop unused thalamus code 2026-04-05 01:18:47 -04:00