- 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>
Memory nodes in the conversation section are now counted separately:
sys X% id Y% jnl Z% mem W% conv V% = NK/MK
Co-Authored-By: Proof of Concept <poc@bcachefs.org>
Parser skips Thinking nodes that are just whitespace. Conscious screen
now shows assistant children (Content, Thinking, ToolCall) as nested
tree items via recursive node_to_view. Nodes get timestamped in
push_node and on assistant branch creation.
Co-Authored-By: Proof of Concept <poc@bcachefs.org>
sync_from_agent now detects changed entries by comparing token counts
(cheap proxy for content changes during streaming). Changed entries
get popped and re-pushed. Extracted push_routed/pop_routed helpers.
Co-Authored-By: Proof of Concept <poc@bcachefs.org>
New ActiveTools struct with proper methods: push, remove, abort_all,
take_finished, take_foreground, iter, len. Lives directly on AgentState,
no separate Arc<Mutex> needed.
TUI reads active tools through agent.state.try_lock(). Turn loop uses
helpers instead of manual index iteration.
Co-Authored-By: Proof of Concept <poc@bcachefs.org>
New ActiveTools struct with proper methods: push, remove,
take_finished, take_foreground, iter, len. Turn loop uses
helpers instead of manual index iteration.
Removing SharedActiveTools (Arc<Mutex<Vec>>) — active tools
live directly in AgentState. A few UI callers still need
updating.
Co-Authored-By: Proof of Concept <poc@bcachefs.org>
Agent is now Arc<Agent> (immutable config). ContextState and AgentState
have separate tokio::sync::Mutex locks. The parser locks only context,
tool dispatch locks only state. No contention between the two.
All callers migrated: mind/, user/, tools/, oneshot, dmn, learn.
28 tests pass, zero errors.
Co-Authored-By: Proof of Concept <poc@bcachefs.org>
Bulk replaced Arc<Mutex<Agent>> with Arc<Agent> across all files.
Fixed control.rs, memory.rs tool handlers. Fixed oneshot Backend.
Remaining errors are all agent.lock() → agent.state.lock() or
agent.context.lock() in mind/, user/, and a few in mod.rs.
Co-Authored-By: Proof of Concept <poc@bcachefs.org>
Entries with empty token_ids (Thinking, Log) are not part of the
prompt and don't have messages. Skip them in streaming_index(),
route_entry(), and sync_from_agent() instead of calling .message()
which panics.
Using token_ids.is_empty() as the guard in streaming_index means
the check is tied to the data, not the type — any entry that
doesn't produce tokens is safely skipped.
Co-Authored-By: Proof of Concept <poc@bcachefs.org>
The consciousness binary has its own main() separate from poc-memory.
Agent::new() creates ContextEntries which need the tokenizer, so it
must be initialized before Mind::new().
Co-Authored-By: Proof of Concept <poc@bcachefs.org>
Add agent/tokenizer.rs with global Qwen 3.5 tokenizer that generates
actual token IDs including chat template wrapping. ContextEntry now
stores token_ids: Vec<u32> instead of tokens: usize — the count is
derived from the length.
ContextEntry::new() tokenizes automatically via the global tokenizer.
ContextSection::push_entry() takes a raw ConversationEntry and
tokenizes it. set_message() re-tokenizes without needing an external
tokenizer parameter.
Token IDs include the full chat template: <|im_start|>role\ncontent
<|im_end|>\n — so concatenating token_ids across entries produces a
ready-to-send prompt for vLLM's /v1/completions endpoint.
The old tiktoken CoreBPE is now unused on Agent (will be removed in
a followup). Token counts are now exact for Qwen 3.5 instead of the
~85-90% approximation from cl100k_base.
Co-Authored-By: Proof of Concept <poc@bcachefs.org>
Thinking/reasoning content is now a first-class entry type:
- Serialized as {"thinking": "..."} in conversation log
- 0 tokens for budgeting (doesn't count against context window)
- Filtered from assemble_api_messages (not sent back to model)
- Displayed in UI with "thinking: ..." label and expandable content
Co-Authored-By: Proof of Concept <poc@bcachefs.org>
Individual memory nodes now show their score in the status column
after the token count, not embedded in the name. Unscored memories
have an empty status column.
Co-Authored-By: Proof of Concept <poc@bcachefs.org>
SectionView gains a status field for extra info displayed after the
token count column. Memory nodes section shows "N scored, M unscored"
in the status column instead of burying it in the title.
Renderer uses fixed-width columns (40 name, 16 tokens, status) for
consistent alignment across sections.
Co-Authored-By: Proof of Concept <poc@bcachefs.org>
Introduced SectionView {name, tokens, content, children} as a
UI-only tree node, separate from the data ContextSection. The widget
SectionTree renders SectionView with the old recursive expand/collapse
behavior — children for sub-sections, content for text expansion.
section_to_view() converts data sections to UI views, using
ConversationEntry::label() for names and content_text() for
expandable content.
read_context_views() builds the same tree the old context_state_summary
did: System, Identity, Journal, Memory nodes (scored/unscored counts,
expandable to show content), Conversation entries.
Co-Authored-By: Proof of Concept <poc@bcachefs.org>
Build a synthetic "Memory nodes (N scored, M unscored)" section in
the context screen by extracting Memory entries from the conversation
section. Each node shows its key and score. Inserted before the
conversation section so scores are visible at a glance.
This makes it easy to see whether scoring is keeping up — if unscored
count is high relative to scored, scoring needs to run more often.
Co-Authored-By: Proof of Concept <poc@bcachefs.org>
trim_entries is now a simple loop:
1. Drop duplicate memories and DMN entries
2. While over budget: if memories > 50% of entry tokens, drop
lowest-scored memory; otherwise drop oldest conversation entry
3. Snap to user message boundary
ContextBudget is gone — sections already have cached token totals:
- total_tokens() on ContextState replaces budget.total()
- format_budget() on ContextState replaces budget.format()
- trim() takes fixed_tokens: usize (system + identity + journal)
Co-Authored-By: Proof of Concept <poc@bcachefs.org>
Signed-off-by: Kent Overstreet <kent.overstreet@linux.dev>
New types — not yet wired to callers:
- ContextEntry: wraps ConversationEntry with cached token count and
timestamp
- ContextSection: named group of entries with cached token total.
Private entries/tokens, read via entries()/tokens().
Mutation via push(entry), set(index, entry), del(index).
- ContextState: system/identity/journal/conversation sections + working_stack
- ConversationEntry::System variant for system prompt entries
Token counting happens once at push time. Sections maintain their
totals incrementally via push/set/del. No more recomputing from
scratch on every budget check.
Does not compile — callers need updating.
Co-Authored-By: Proof of Concept <poc@bcachefs.org>
Moved persistent_state from per-agent to a single shared BTreeMap on
Subconscious. All agents read/write the same state — surface's walked
keys are visible to observe and reflect, etc.
- Subconscious.state: shared BTreeMap<String, String>
- walked() derives from state["walked"] instead of separate Vec
- subconscious-state.json is now a flat key-value map
- All agent outputs merge into the shared state on completion
- Loaded on startup, saved after any agent completes
Co-Authored-By: Proof of Concept <poc@bcachefs.org>
SectionTree:
- 'e': expand all nodes
- 'c': collapse all nodes
- Home/End already wired from previous commit
Key legend shown at bottom border of each focused pane:
- Tree panes: nav, expand/collapse, expand/collapse all, paging
- Agent list: select, tab
- History: scroll, paging
Legend only appears on the focused pane to avoid clutter.
Co-Authored-By: Proof of Concept <poc@bcachefs.org>