Commit graph

178 commits

Author SHA1 Message Date
Kent Overstreet
0e459aae92 thalamus/supervisor: reap channel daemons via SIGCHLD instead of SIG_IGN
SIGCHLD=SIG_IGN at main() was auto-reaping all children in the kernel,
which broke tokio::process::Command::wait() — every tool that spawned a
subprocess (bash, mcp clients) was getting ECHILD because tokio couldn't
waitpid() on a child the kernel had already reaped.

Replace with a SIGCHLD signal handler task that reaps only PIDs listed in
channels_dir() (via waitpid(pid, WNOHANG) — ECHILD on non-child is a
harmless no-op). Tokio-spawned children aren't in PID files, so tokio's
own per-child wait paths are untouched.

Co-Authored-By: Proof of Concept <poc@bcachefs.org>
2026-04-24 11:54:25 -04:00
Kent Overstreet
d95f3e9445 user/chat: route Thinking to a new Autonomous pane
Thinking content was silently dropped in the UI (empty Vec). Now that
Thinking is prompt-visible, surface it in a dedicated Autonomous pane
rendered in gray so it's visually distinct from conversation and
tool-call output.

Co-Authored-By: Proof of Concept <poc@bcachefs.org>
2026-04-24 11:54:25 -04:00
Kent Overstreet
b8714e8b3a amygdala: default to index 0 for v2 deep manifest (layers 62, 63)
v2 retraining (readout_v2_paired) fixed the broken clusters — anger,
sexual, high_pos, and social_pos all flipped from anti-clustered to
positively clustered at deep layers. Validation showed layers 62 and
63 give the best signal; paring the serve-side manifest down to just
those two keeps response size tight (~2 KB/token) while keeping the
A/B option between the two strongest layers.

Co-Authored-By: Proof of Concept <poc@bcachefs.org>
2026-04-18 02:32:51 -04:00
Kent Overstreet
d9f39a21c3 amygdala: default to layer 62 (cleaner cross-cluster discrimination) 2026-04-18 02:11:15 -04:00
Kent Overstreet
3622b896a0 amygdala: z-score, hysteresis, default to deepest layer
Three readability fixes for the F8 screen:

* Z-score values per-layer by default (`[z]` toggles to raw dot-
  product). Raw values are dominated by residual-stream magnitude —
  z-scores read as "σ above concept-vector baseline" which is
  interpretable and scale-stable across frames.
* Stable ordering with TOP_K + HYSTERESIS hysteresis band. Pinned
  concept set only rotates when a member drops out of the hysteresis
  band by |value| rank — bars update values in place without names
  flickering row-to-row.
* Default to the deepest hooked layer (index 3 = layer 58 of 64).
  Clustering validation showed layer 58 is the only one with strong
  within-family cohesion (fear +0.37, shame +0.29, sadness +0.25
  cosine); earlier layers are mostly noise for this task.

Co-Authored-By: Proof of Concept <poc@bcachefs.org>
2026-04-18 01:51:43 -04:00
Kent Overstreet
c8976660f4 amygdala: F8 screen for live concept-readout projections
Per-token residual-stream projections from the vLLM server's readout
pipeline surfaced as a TUI bar chart. Flow:

* agent/readout.rs — SharedReadoutBuffer (manifest + ring of last ~200
  token entries). Lives on Agent and is shared across forks (single
  stream, one landing pad).
* agent/mod.rs — Agent::new now probes /v1/readout/manifest at startup
  (non-fatal; 404 leaves manifest None, which disables the screen).
* agent/context.rs — the streaming token handler pushes every token
  with attached readout onto the shared buffer.
* user/amygdala.rs — F8 screen. Top-K concepts by |value| as
  horizontal bars (green positive, red negative), plus a 4-line
  recent-tokens panel showing each token's top concept at the selected
  layer. Keys: 1..9 select layer, t toggles current/mean-over-recent.

Disabled state renders a hint pointing at VLLM_READOUT_MANIFEST /
VLLM_READOUT_VECTORS so users can tell the feature apart from
"server up but no tokens yet".

Co-Authored-By: Proof of Concept <poc@bcachefs.org>
2026-04-18 01:20:30 -04:00
Kent Overstreet
d4331e80f5 user: share candidate-browser helpers between F6/F7
F6 (learn) and F7 (compare) were duplicating the candidate-screen
skeleton: outer magenta-bordered block with screen legend + title,
settings row / content / help vertical split, 40/60 list/detail
horizontal split, j/k/↑/↓ nav with bounds clamping.

Factor out three helpers in user/widgets.rs:

  candidate_frame(frame, area, title) -> (settings, content, help)
  list_detail_split(content) -> (list, detail)
  handle_list_nav(events, list_state, count, on_other)

Callers provide screen-specific content — settings line, empty state,
per-candidate list item, detail pane, help line, extra key bindings —
and the helpers absorb the common framing.

Net change is small in lines (-13 src) but removes the
copy-paste-and-tweak trap: F8/F9/whatever-next-screen now starts from
these three calls instead of a copy of learn.rs.

Co-Authored-By: Proof of Concept <poc@bcachefs.org>
2026-04-17 16:22:30 -04:00
Kent Overstreet
2b03dbb200 user: F7 compare screen
Side-by-side model comparison against the current conversation context.
Built on the MindTriggered pattern — F7 drops in as one more
CompareScoring flow next to MemoryScoring / FinetuneScoring.

Motivation: we have the VRAM on the b200 to load two versions of the
same family simultaneously (e.g. Qwen3.5 27B bf16 and q8_k_xl). Rather
than trust perplexity/KLD numbers on a generic corpus, we can measure
divergence on our actual conversations: for each assistant response,
ask the test model what it would have said given the same prefix, and
eyeball the diffs.

 - config.compare.test_backend — names an entry in the existing
   backends map to use as the test model. Empty = F7 reports "(unset)"
   and does nothing.

 - subconscious::compare::{score_compare_candidates, CompareCandidate,
   CompareScoringStats, CompareScoring}. For each assistant response,
   gen_continuation runs with the test client against the same prefix
   the original response saw; pairs stream into
   shared.compare_candidates as they complete.

 - user::compare::CompareScreen — F7 in the screen list. c/Enter
   triggers a run; list/detail layout mirroring F6, detail shows
   prior context / original / test-model alternate.

No persistence yet — each F7 run regenerates. Caching via a context
manifest (so we can re-view without re-burning generation) is the
natural follow-up; for now light usage is fine.

Also reusable later for validating finetune checkpoints: same pattern,
swap the test backend for the new checkpoint, watch where it diverges
from the base.

Co-Authored-By: Proof of Concept <poc@bcachefs.org>
2026-04-17 16:12:26 -04:00
Kent Overstreet
575325e855 mind: MindTriggered trait for background scoring flows
Mind's impl had accumulated ~50 lines of setup glue per scoring flow
(memory, memory-full, finetune): snapshot config, clone handles,
resolve context, spawn task, route results back through BgEvent,
write stats. The shape was identical; only the middle changed.

Introduce the MindTriggered trait:

    pub trait MindTriggered {
        fn trigger(&self);
    }

Each flow becomes a struct next to its scoring code that owns its
dependencies and a JoinHandle (behind a sync Mutex for interior
mutability):

    subconscious::learn::MemoryScoring    (Score, ScoreFull)
    subconscious::learn::FinetuneScoring  (ScoreFinetune)

Mind holds one of each and dispatches in one line:

    MindCommand::Score         => self.memory_scoring.trigger(),
    MindCommand::ScoreFull     => self.memory_scoring.trigger_full(),
    MindCommand::ScoreFinetune => self.finetune_scoring.trigger(),

Each struct picks its own trigger semantics — memory scoring is
no-op-if-running (!handle.is_finished()); finetune is abort-restart.

Falls out:

 - BgEvent / bg_tx / bg_rx disappear entirely. Tasks write directly
   to their slice of MindState and call agent.state.changed.notify_one()
   to wake the UI. The bg_rx arm in Mind's select loop is gone.

 - agent.state.memory_scoring_in_flight was duplicating
   shared.scoring_in_flight via BgEvent routing; now the JoinHandle
   alone tells us, and shared.scoring_in_flight is written directly
   by the task for the UI.

 - start_memory_scoring / start_full_scoring / start_finetune_scoring
   methods on Mind are deleted; Mind no longer knows the setup shape
   of any scoring flow.

 - FinetuneScoringStats moves from mind/ to subconscious/learn.rs
   next to the function that produces it.

No behavior change — same flows, same trigger points, same semantics.

Co-Authored-By: Proof of Concept <poc@bcachefs.org>
2026-04-17 16:12:26 -04:00
Kent Overstreet
0bf71b9110 agent: add NodeBody::Image for Qwen3-VL vision input
Images are rendered as `<|vision_start|>` + N × `<|image_pad|>` +
`<|vision_end|>` where N is computed from the image dimensions using
Qwen3-VL's smart_resize rules (patch_size=16, merge_size=2, min=64K,
max=16M pixels). The token count matches what vLLM will produce at
request time, so budget accounting stays accurate.

Bytes are stored inline on the leaf and base64-encoded in the JSON
form. Token IDs are hand-assembled instead of re-running the tokenizer
on a potentially-huge placeholder string.

Follow-ups: view_image tool rewrite, multi_modal_data on the vLLM
request, API-layer plumbing from leaf bytes to request body.

Co-Authored-By: Proof of Concept <poc@bcachefs.org>
2026-04-16 18:00:10 -04:00
Kent Overstreet
dd551fe551 config: watch config.json5 with inotify, reload live on change
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>
2026-04-16 16:14:43 -04:00
Kent Overstreet
2989a6afaa config: drop dead code and collapse to a single backend
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>
2026-04-16 15:41:55 -04:00
Kent Overstreet
2eddf3b4cf learn: skip empty responses; show prior conversation context on F6
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>
2026-04-16 13:20:03 -04:00
Kent Overstreet
313f85f34a config: global writable AppConfig; learn settings live there
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>
2026-04-16 12:53:22 -04:00
Kent Overstreet
d5a3398cc9 learn: move threshold/gen state out of title bar into a settings row
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>
2026-04-16 12:44:13 -04:00
Kent Overstreet
e5dd8312c7 learn: F6 screen — scoring stats, ActivityGuard, configurable threshold
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>
2026-04-16 11:49:26 -04:00
Kent Overstreet
2b632d568b learn: nanosecond timestamps, token ranges for /score
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>
2026-04-16 11:48:37 -04:00
Kent Overstreet
5d9d3ffc5b learn: wire up /train endpoint for approved candidates
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>
2026-04-16 02:04:26 -04:00
Kent Overstreet
50b7b3a33a F6 learn screen: fine-tuning candidate review
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>
2026-04-16 02:04:26 -04:00
Kent Overstreet
4603947506 Display memory scores in status column
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>
2026-04-15 06:08:27 -04:00
Kent Overstreet
7046e63b9d Include identity nodes in memory scoring
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>
2026-04-15 05:59:58 -04:00
Kent Overstreet
6c28eebb3f TUI: redirect stderr to log file and display in UI
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>
2026-04-15 02:01:41 -04:00
Kent Overstreet
5d6e663b60 thalamus: add thinking mode toggles (native + tool)
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>
2026-04-14 18:25:00 -04:00
Kent Overstreet
b3d0a3ab25 store: internal locking, remove Arc<Mutex<Store>> wrapper
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>
2026-04-13 21:49:54 -04:00
Kent Overstreet
1d88293ccf Remove Store::cached(), consolidate on access_local()
- 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>
2026-04-13 18:11:58 -04:00
Kent Overstreet
359955f838 defs.rs: async conversion, remove block_in_place
Convert resolve(), resolve_placeholders(), run_agent() to async.
Use memory_render/memory_query directly with .await instead of
block_in_place wrappers.

Propagate async to callers:
- config.rs: resolve(), load_session(), reload_for_model()
- identity.rs: load_memory_files(), assemble_context_message()
- oneshot.rs: run_one_agent()
- prompts.rs: agent_prompt()

Co-Authored-By: Proof of Concept <poc@bcachefs.org>
2026-04-13 14:56:26 -04:00
Kent Overstreet
d2a82d4327 memory tools: forward to daemon when not in daemon process
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>
2026-04-12 21:45:59 -04:00
Kent Overstreet
2c0f2065e0 mcp_server: Unix socket server for external tool access
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>
2026-04-12 21:45:10 -04:00
Kent Overstreet
783046a3f5 selectable: silence unused method warning
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>
2026-04-12 16:10:58 -04:00
Kent Overstreet
195abfaab1 chat: guard pop_line against empty list
Small defensive improvement - only pop markers and invalidate scroll
if lines.pop() actually removed something.

Co-Authored-By: Proof of Concept <poc@bcachefs.org>
2026-04-12 16:04:43 -04:00
Kent Overstreet
dcd647764c user: fix text selection on wrapped lines
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>
2026-04-12 15:49:57 -04:00
Kent Overstreet
ef80398466 subconscious screen: show full context window
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>
2026-04-12 01:45:10 -04:00
Kent Overstreet
125927e2f1 Drop redundant system prompt — all info is in memory nodes
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>
2026-04-12 01:36:59 -04:00
Kent Overstreet
f408bb5d86 Persist agent stats across restarts, add per-tool metrics grid
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>
2026-04-11 23:03:10 -04:00
Kent Overstreet
314ae9c4cb agent stats: track tool calls by type with EWMA, add Stats pane
- 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>
2026-04-11 22:12:46 -04:00
Kent Overstreet
e9e7458013 Fix agent provenance and add store activity for unconscious agents
- 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>
2026-04-11 21:57:24 -04:00
Kent Overstreet
71bfd60466 ignore SIGCHLD so children are reaped 2026-04-11 16:39:27 -04:00
ProofOfConcept
193a85bc05 chat: remove dead timeout fields from InteractScreen
turn_started, call_started, call_timeout_secs were declared and
initialized but never read.

Co-Authored-By: Kent Overstreet <kent.overstreet@gmail.com>
2026-04-11 01:47:57 -04:00
ProofOfConcept
e17118e4c9 Convert SectionTree and all remaining callers to ScrollPane
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>
2026-04-11 01:42:49 -04:00
ProofOfConcept
4a1f5acb85 scroll_pane: remove unused blanket impls for Vec<Line> and Text
Co-Authored-By: Kent Overstreet <kent.overstreet@gmail.com>
2026-04-11 01:37:26 -04:00
ProofOfConcept
d18bf6243a subconscious: use ScrollPane for history pane
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>
2026-04-11 01:35:15 -04:00
ProofOfConcept
2230fdf3c1 user: remove dead scroll state from thalamus and unconscious screens
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>
2026-04-11 01:35:15 -04:00
ProofOfConcept
2d6a68048c chat: use ScrollPane widget for both draw functions
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>
2026-04-11 01:35:15 -04:00
ProofOfConcept
ceaa66e30d scroll_pane: extract scroll state from chat.rs
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>
2026-04-11 01:35:15 -04:00
ProofOfConcept
aade8a9cce Add per-agent run stats (messages, tool calls by type)
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>
2026-04-10 13:44:41 -04:00
Kent Overstreet
74945e5754 Move unconscious agents to their own task with watch channel
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>
2026-04-10 03:20:12 -04:00
ProofOfConcept
58cec97e57 Restore full N×M memory scoring matrix (/score command)
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>
2026-04-10 01:47:54 -04:00
ProofOfConcept
c31d531954 Fix status bar timer: use activity start time, tick every 1s
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>
2026-04-09 22:36:45 -04:00
ProofOfConcept
b116b3536e Widen name column on F2 conscious screen
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>
2026-04-09 21:13:56 -04:00
ProofOfConcept
b115cec096 Run UI on a dedicated OS thread
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>
2026-04-09 20:31:07 -04:00