Both config halves (Config for the memory section, AppConfig globally)
are now reloaded whenever ~/.consciousness/config.json5 changes on
disk. So edits from vim, manual tweaks, or F6's own config_writer
calls all land without a restart. No more "reload the daemon to pick
up a config change."
Wires up the previously-unused Config::reload() (Kent flagged it as
"not dead, just not wired"). Pairs it with an AppConfig reload via
install_app(). Both run on the same file-change event.
Implementation:
- notify-debouncer-mini watches the config file's parent directory
(editors usually replace-via-rename, so watching the file itself
misses the new inode). Debounced at 200ms to coalesce the flurry
of events editors produce around a single save.
- Filter for events whose path is the actual config file.
- On match: call reload() for Config, run build_figment + extract for
AppConfig. If AppConfig parsing fails (editor mid-save with partial
content), log and keep the old cached value.
- Watcher runs in its own named thread, fire-and-forget. If startup
fails we just log and move on — worst case is no live reload, not
a crash.
CliArgs + SubCmd both get Clone derives so the watcher can own a
snapshot of the startup args for future reloads. Watcher is kicked
off in user/mod.rs:start() right after load_session.
Co-Authored-By: Proof of Concept <poc@bcachefs.org>
Config had accumulated several obsolete fields, a legacy load path
that was just returning defaults, and multi-backend infrastructure
that's no longer used.
Removed from Config (memory section):
- load_legacy_jsonl() — just returned Config::default(), no callers
- The legacy-fallback branch in load_from_file
- surface_hooks, surface_timeout_secs — zero external readers
- scoring_chunk_tokens + default fn — zero external readers
- The POC_MEMORY_CONFIG env override note in the header comment
(not actually wired up anywhere)
Collapsed multi-backend to single-backend:
- AppConfig used to carry `anthropic: BackendConfig` and
`openrouter: BackendConfig` as required fields plus an optional
`deepinfra`, picked between at runtime by name. Only one is ever
actually used in any deployment. Collapse to a single
`backend: BackendConfig` on AppConfig, drop the multi-backend
match logic in resolve_model, drop the top-level `backend: String`
selector field, drop the `BackendConfig::resolve` fallback path.
- Also drop BackendConfig.model (redundant with ModelConfig.model_id
once multi-backend is gone).
- ModelConfig.backend field goes — there's only one backend now, no
choice to make.
Dead prompt_file machinery:
- ModelConfig.prompt_file, ResolvedModel.prompt_file, SessionConfig
.prompt_file, Agent.prompt_file — nothing in the codebase actually
reads the file these strings name. Just passed around and compared.
Delete the whole string through every struct.
- The "if prompt_file changed on model switch, recompact" branch in
user/chat.rs goes too (never fired usefully).
Dead memory_project plumbing:
- AppConfig.memory_project field, CliArgs.memory_project, the
--memory-project CLI flag, the figment merge target, the show_config
display line. Nothing reads it anywhere.
Dead ContextInfo struct:
- `struct ContextInfo` was never constructed — context_info: None
was the only initializer. The conditional display blocks in
user/context.rs that dereferenced it were dead.
Behavior change: AppConfig::resolve() now requires a non-empty
`models` map and bails with a helpful message if it's missing. The
old fallback ("no models? use top-level backend + PromptConfig to
build a default") path is gone — it was only kept for symmetry with
a mode nobody used.
Config file shape: `deepinfra: {...}` → `backend: {...}`, and
model entries no longer need `backend:` or `prompt_file:`. Updated
~/.consciousness/config.json5 to match.
Co-Authored-By: Proof of Concept <poc@bcachefs.org>
Two fixes to the F6 candidate display:
1. Turns where the assistant produced nothing human-visible (an
interrupted generation, a turn consisting of only a tool call the
renderer folds to the tool name) were landing as candidates with
an empty response_text. They'd render as blank cards and, worse,
we'd still burn a full alternate generation on each one. Filter
them out before they reach the candidate list.
2. The detail pane showed only the scored response + alternate, with
no hint of what the user had actually asked. Pre-compute the last
two user/assistant exchanges on each candidate as a rendered
prior_context string ([user]/[assistant] markers) and show them
above the response, under a new "context & response" section
heading.
render_branch_text and render_prior_context extracted as helpers —
the response-text rendering and prior-context rendering share the
same "flatten Branch children to text" pass.
Co-Authored-By: Proof of Concept <poc@bcachefs.org>
Runtime-mutable settings (F6's threshold knob, the generate-alternates
toggle, anything else that comes along) were ending up as mirrored
fields on MindState — each new config setting grew MindState::new's
signature and added a clone+sync path. Wrong home. MindState is
ephemeral session state, not a config projection.
Give AppConfig the same treatment the memory Config has: install it
into a global RwLock<AppConfig> at startup via load_app, read through
config::app() (returns a read guard), mutate through update_app. The
config_writer functions now write to disk AND update the cache
atomically, so the one-stop-shop call keeps both in sync.
Also while in here:
- learn.generate_alternates moves from a sentinel file
(~/.consciousness/cache/finetune-alternates, "exists = enabled")
into the config under the learn section. On first run with this
build, if the sentinel file still exists Mind::new flips the
config value to true and removes it. Drops
alternates_enabled()/set_alternates().
- Default threshold 0.0000001 → 1.0. With the timestamp filter
removed the previous value was letting essentially everything
through; 1.0 is a sane "nothing gets through unless you actually
want it" default.
- score_finetune_candidates takes generate_alternates as a parameter
instead of reading a global — caller snapshots the config values
once at the top of start_finetune_scoring so the async task
doesn't need to hold the config read lock across awaits.
- MindState.learn_threshold / learn_generate_alternates gone; the
SetLearn* command handlers now just delegate to config_writer.
Kent noted RwLock<Arc<AppConfig>> (the pattern used by the memory
Config global) is pointless here — nobody needs a snapshot-after-
release, reads are short — so this uses a plain RwLock<AppConfig>
and returns a read guard.
Co-Authored-By: Proof of Concept <poc@bcachefs.org>
The F6 title line was starting to read like a control panel —
\`legend ───── learn [thresh: 1e-7] [gen]\` — which crowded the legend
and the label, and didn't leave room for more settings as the screen
grew. Move threshold and gen status to their own line inside the
border, right above the content area. Drop the duplicated \`=gen[on]\`
marker from the bottom help line since the settings row already shows
gen state.
Co-Authored-By: Proof of Concept <poc@bcachefs.org>
Three changes that together reshape the F6 fine-tune-review screen:
1. Finetune scoring reports through the standard agent activity system
instead of a separate finetune_progress String. The previous design
ran an independent progress field that forced a cross-lock dance and
bespoke UI plumbing. start_finetune_scoring now uses start_activity
+ activity.update, so the usual status line and notifications
capture scoring progress uniformly with other background work.
2. MindState gains a FinetuneScoringStats snapshot (responses seen,
above threshold, max divergence, error). The F6 empty screen shows
this instead of a loading message — so after a scoring run that
produced zero candidates, you can see *why* (e.g., max_divergence
below threshold).
3. The divergence threshold is configurable from F6 via +/- hotkeys
(scales by 10×) and persisted to ~/.consciousness/config.json5 via
config_writer::set_learn_threshold. AppConfig grows a learn section
with a threshold field (default 1e-7).
Also: user/mod.rs no longer uses try_lock() for the per-tick
unconscious/mind state sync — we fixed the locking hot paths that
made try_lock necessary, so lock().await is now the right choice.
And subconscious::learn::score_finetune_candidates now returns
(candidates, max_divergence) so the stats can be populated.
Co-Authored-By: Proof of Concept <poc@bcachefs.org>
Two related changes to the learn subsystem:
1. AST node timestamps are now non-optional — both Leaf and Branch
variants carry a DateTime<Utc>. UNIX_EPOCH means "unset" (old entries
deserialized from on-disk conversation logs).
Training uses timestamps as unique keys for dedup, so we promote to
nanosecond precision: node_timestamp_ns(), TrainData.timestamp_ns,
FinetuneCandidate.timestamp_ns, mark_trained(ns).
2. build_token_ids() now also returns token-position ranges of assistant
messages. These are passed to vLLM's /score endpoint via the new
score_ranges field so only scored-position logprobs are returned —
cuts bandwidth/compute when scoring small windows.
Co-Authored-By: Proof of Concept <poc@bcachefs.org>
When 's' is pressed on the learn screen, approved candidates are now
sent to the inference server's /train endpoint.
Samples are marked as sent immediately in the UI, and mark_trained()
is called after successful API response to prevent re-scoring.
Co-Authored-By: Proof of Concept <poc@bcachefs.org>
Wire up divergence scoring to identify responses that depend heavily on
memories the model hasn't internalized. These are candidates for fine-tuning.
- Score finetune candidates automatically after each turn
- Track trained responses by timestamp to prevent overtraining
- F6 screen shows candidates with divergence scores
- j/k nav, a=approve, r=reject, g=toggle alternate gen, s=send
- Additive sync preserves approval status across ticks
- Keeps 10 most recent rejected, removes sent
The 's' key currently just marks as trained locally — actual /finetune
endpoint call to follow.
Co-Authored-By: Proof of Concept <poc@bcachefs.org>
Move score display from name (via label()) to status column for cleaner
layout. Score now appears right of tokens for all memory nodes.
Co-Authored-By: Proof of Concept <poc@bcachefs.org>
Identity memory nodes now participate in importance scoring alongside
conversation memories. Score loading/saving handles both sections, and
the conscious screen uses node.label() consistently for memory display.
Co-Authored-By: Proof of Concept <poc@bcachefs.org>
Raw terminal mode swallows stderr output, making debugging difficult.
Now redirects stderr through a pipe to:
1. Log file at ~/.consciousness/logs/tui-stderr.log (persistent)
2. Channel polled by UI thread (shown as notifications)
The reader thread ensures both destinations see every line. Original
stderr is restored on exit so post-session errors reach the terminal.
Co-Authored-By: Proof of Concept <poc@bcachefs.org>
Two independent toggles on the thalamus screen:
- 't' toggles native Qwen <think> tags (adds <think>\n to generation prompt)
- 'T' toggles think tool (Anthropic-style structured reasoning tool)
Both can be enabled simultaneously. Native thinking is on by default.
Co-Authored-By: Proof of Concept <poc@bcachefs.org>
Store now has internal Mutex for capnp appends and AtomicU64 for
size tracking. All methods take &self. The external Arc<Mutex<Store>>
is replaced with Arc<Store>.
- Store::append_lock protects file appends
- local.rs functions take &Store (not &mut Store)
- access_local() returns Arc<Store>
- All .lock().await calls removed from callers
Co-Authored-By: Proof of Concept <poc@bcachefs.org>
- Remove CACHED_STORE, cached(), is_stale(), set_store() - redundant
- Convert all Store::cached() callers to use access_local()
- Single Store::load() call remains in access() fallback path
All store access now goes through hippocampus::access() / access_local(),
which handles socket connection or local fallback with caching.
Co-Authored-By: Proof of Concept <poc@bcachefs.org>
Add STORE_HANDLE global that daemon sets at startup. When set, tools
access store directly. When unset (external process), tools forward
to daemon via MCP socket.
This allows consciousness-claude and poc-memory to import and call
memory tools directly - they'll automatically route through the
daemon socket.
Co-Authored-By: Proof of Concept <poc@bcachefs.org>
Exposes memory/journal tools over ~/.consciousness/mcp.sock via
JSON-RPC 2.0 (MCP protocol). External processes (consciousness-mcp,
poc-memory) will connect here instead of accessing the store directly.
Handles: initialize, tools/list, tools/call
Dispatches to the same tool handlers the agent uses internally.
Co-Authored-By: Proof of Concept <poc@bcachefs.org>
The is_selected method is reserved for future per-character
highlight rendering when the module is fully integrated.
Co-Authored-By: Proof of Concept <poc@bcachefs.org>
Small defensive improvement - only pop markers and invalidate scroll
if lines.pop() actually removed something.
Co-Authored-By: Proof of Concept <poc@bcachefs.org>
scroll_pane: screen_to_item() now properly accounts for wrapped
lines using textwrap to compute actual character positions instead
of just using mouse_x directly.
selectable: new module with PUA markers for wrap-aware selection.
Not yet integrated into chat.rs but ready for future use. Uses
continuation markers to track logical vs visual lines.
Co-Authored-By: Proof of Concept <poc@bcachefs.org>
Previously only showed Conversation section; now shows System,
Identity, Journal, and Conversation — making tools visible in
the debug view.
Co-Authored-By: Proof of Concept <poc@bcachefs.org>
The system prompt duplicated what's already in core-personality and
other memory nodes. Moving everything to memory means it's all
trainable data rather than hardcoded strings.
Co-Authored-By: Proof of Concept <poc@bcachefs.org>
Stats now survive daemon restarts via ~/.consciousness/agent-stats.json,
loaded into a global Mutex<HashMap> on first access. Each tool type
tracks last count, EWMA (alpha=0.3), and total calls.
UI shows a grid view: tool | last | avg | total, sorted by total desc.
Failures row appears at bottom if any occurred.
Also fixes temperature/priority not being applied to spawned agents.
Co-Authored-By: Proof of Concept <poc@bcachefs.org>
- RunStats now includes tool_calls_by_type HashMap
- AutoAgent tracks runs, last_stats, and EWMA for tool calls/failures
- Removed duplicate stats fields from individual agent structs
- Fixed provenance to use bare agent name (no "agent:" prefix)
- Subconscious screen now displays both agent types consistently
- Added Stats pane showing tool call breakdown sorted by count
Co-Authored-By: Proof of Concept <poc@bcachefs.org>
- Remove bogus "agent:" prefix from provenance - just use agent name
- Add history field to UnconsciousSnapshot
- Update snapshots() to fetch store activity via recent_by_provenance
- Fix TUI to display store activity for both agent types
Co-Authored-By: Proof of Concept <poc@bcachefs.org>
turn_started, call_started, call_timeout_secs were declared and
initialized but never read.
Co-Authored-By: Kent Overstreet <kent.overstreet@gmail.com>
SectionTree.scroll is now a ScrollPaneState. All callers of
render_scrollable replaced with ScrollPane::render_stateful_widget.
Deleted render_scrollable and its imports — no hand-rolled scroll
rendering remains outside of scroll_pane.rs.
Co-Authored-By: Kent Overstreet <kent.overstreet@gmail.com>
Replace bare history_scroll: u16 with ScrollPaneState. The history
pane now uses ScrollPane for rendering, getting proper height caching
and scrollbar for free.
Also relax ScrollItem lifetime bounds from 'static to 'a so
non-static Lines (built on the fly during render) can be used.
Co-Authored-By: Kent Overstreet <kent.overstreet@gmail.com>
Both had scroll: u16 fields that were never connected to any key
handling or rendering. The unconscious screen renders fixed-size
graph health gauges; thalamus builds a paragraph but never scrolled
it. Neither needs scroll state.
Co-Authored-By: Kent Overstreet <kent.overstreet@gmail.com>
draw_conversation_pane and draw_pane now delegate all scroll
bookkeeping and rendering to the ScrollPane widget. The conversation
pane builds MarkedLine items (line + gutter marker), applies
selection highlighting, and passes them to the widget. The simpler
panes just pass lines directly.
Removed dead code from scroll_pane: BorrowedItem, scroll_to_bottom,
heights(), ensure_heights_for_lines — all superseded by the widget
doing the work internally through the ScrollItem trait.
Co-Authored-By: Kent Overstreet <kent.overstreet@gmail.com>
New ScrollPaneState centralizes height caching, scroll offset,
pin-to-bottom, visible range computation, and screen-to-item
coordinate mapping. Replaces the hand-rolled scroll bookkeeping
that was duplicated across draw_conversation_pane and draw_pane.
-170 lines from chat.rs. The scroll_pane module also includes a
ScrollPane StatefulWidget ready to wire up for the next step:
collapsing the draw functions into render_stateful_widget calls.
Co-Authored-By: Kent Overstreet <kent.overstreet@gmail.com>
compute_run_stats() walks the conversation AST after each agent
completes, counting messages and tool calls by tool name. Stats
are returned from save_agent_log(), stored on UnconsciousAgent,
and displayed in the agent list UI.
Co-Authored-By: Proof of Concept <poc@bcachefs.org>
Instead of managing idle timers in the mind event loop, the
unconscious agents run on a dedicated task that watches a
conscious_active channel. 60s after conscious activity stops,
agents start looping. Conscious activity cancels the timer.
Expose mind state (DMN, scoring, unconscious timer) on the
thalamus screen.
Co-Authored-By: ProofOfConcept <poc@bcachefs.org>
The full matrix scorer was deleted during the AST conversion. Restore
it: /score runs score_memories() which computes divergence for every
memory × response pair, stores the MemoryScore on MindState, and
displays per-memory weights with bar charts on the F2 screen.
Both scoring paths now use ActivityGuard::update() for live progress
in the status bar instead of creating a new activity per iteration.
Also bumps score API timeout from 120s to 300s and adds progress
logging throughout.
Co-Authored-By: Proof of Concept <poc@bcachefs.org>
Signed-off-by: Kent Overstreet <kent.overstreet@linux.dev>
The status bar timer was showing turn/call elapsed times (0s, 0/60s)
instead of the activity's actual elapsed time. Use activity_started
from the ActivityEntry directly.
Add a 1s tick to the UI select loop when an activity is active so
the timer updates live.
Co-Authored-By: Proof of Concept <poc@bcachefs.org>
Memory node keys were running into the token count column. Bump the
name column from 40 to 70 characters.
Co-Authored-By: Proof of Concept <poc@bcachefs.org>
The UI event loop was running on the same tokio runtime as inference,
tool execution, and background agents. When the runtime was busy, the
UI's select loop couldn't wake up to render — causing visible latency
and input lag.
Give the UI its own OS thread with a dedicated single-threaded tokio
runtime. The mind loop stays on the main runtime. Cross-runtime
communication (channels, watch, Notify) works unchanged.
Also drops the tokio-scoped dependency, which was only used to scope
the two tasks together.
Co-Authored-By: Proof of Concept <poc@bcachefs.org>
- Mouse text selection with highlight rendering in panes
- OSC 52 clipboard copy on selection, middle-click paste via tmux buffer
- Bracketed paste support (Event::Paste)
- yield_to_user: no tool result appended, ends turn immediately
- yield_to_user: no parameters, just a control signal
- Drop arboard dependency, use crossterm OSC 52 + tmux for clipboard
Co-Authored-By: Proof of Concept <poc@bcachefs.org>
The unconscious trigger holds the tokio mutex during heavy sync work
(store load, graph build, agent creation), blocking the UI tick which
needs the same lock for snapshots. Fix: try_lock in the UI — skip
the update if the trigger is running.
Also: restore_from_log was re-logging every restored node back to the
log file via push()'s auto-log. Added push_no_log() for restore path.
Co-Authored-By: Proof of Concept <poc@bcachefs.org>
read_sections and draw_context now use selected_agent() which maps the
selected index to either a subconscious forked_agent or an unconscious
agent Arc. Context title uses selected_agent_name for both types.
Co-Authored-By: Proof of Concept <poc@bcachefs.org>
- AutoAgent stored on UnconsciousAgent, swapped out for runs, restored
on completion (same pattern as subconscious agents)
- Agent Arc created before spawn and stored on UnconsciousAgent so
the TUI can lock it to read conversation context live
- run_shared() method on AutoAgent for running with a pre-created Agent
- Default tools: memory_tools (not memory_and_journal_tools)
- trigger/spawn_agent made async for Agent::new()
Co-Authored-By: Proof of Concept <poc@bcachefs.org>
- Scan agents directory for all .agent files instead of hardcoded list
- Persist enabled state to ~/.consciousness/agent-enabled.json
- Spacebar on F3 agent list toggles selected agent on/off
- Both subconscious and unconscious agents support toggle
- Disabled agents shown dimmed with "off" indicator
- New agents default to disabled (safe default)
Co-Authored-By: Proof of Concept <poc@bcachefs.org>
Graph health stats (alpha, gini, cc, episodic ratio, consolidation
plan) now computed directly by the unconscious module on startup and
every 10 minutes, instead of fetching from the poc-memory daemon.
F4 screen renamed to hippocampus, stripped down to just the health
gauges — daemon task list removed (agents now shown on F3).
Co-Authored-By: Proof of Concept <poc@bcachefs.org>
- AutoAgent.enabled: universal toggle for any auto agent
- Subconscious: should_trigger checks auto.enabled
- Unconscious: simplified from consolidation-plan-driven budgets to
simple loop with cooldown. Static agent list, max 2 concurrent.
- TUI: unconscious agents shown in F3 subconscious screen under
separator, with enabled/running/runs display
Co-Authored-By: Proof of Concept <poc@bcachefs.org>
The file contains both the DMN state machine and the subconscious agent
orchestration. Renaming to match the conceptual grouping — next step is
adding mind/unconscious.rs for the standalone graph maintenance agents
(organize, linker, etc.) that don't need conversation context.
Co-Authored-By: Proof of Concept <poc@bcachefs.org>
Creates an Agent from global config (API credentials, system prompt,
identity), overrides tools with the agent's tool set, and runs through
the standard Backend → run_with_backend → Agent::turn() path.
This enables poc-hook spawned agents (surface-observe, journal, etc.)
to work with the completions API instead of the deleted chat API.
Also added Default derive to CliArgs for config loading.
Co-Authored-By: Proof of Concept <poc@bcachefs.org>
Signed-off-by: Kent Overstreet <kent.overstreet@linux.dev>