journal_definitions() separated from definitions() in memory.rs.
All agents get memory + journal tools via memory_and_journal_definitions().
TODO: implement per-agent tool whitelist from header to properly
restrict journal tools to journal agent only.
Co-Authored-By: Proof of Concept <poc@bcachefs.org>
Agents must use native tool dispatch, not bash, for correct
provenance tracking. Bash access was leftover from old architecture.
All 12 agents cleaned up.
Co-Authored-By: Proof of Concept <poc@bcachefs.org>
Remove journal tool from memory-instructions-core (only the journal
agent should write journal entries). Add explicit instruction to
journal agent: only use journal_tail/journal_new/journal_update,
not memory_write/render/search.
Prevents the journal agent from creating duplicate memory nodes
about events that surface-observe is already recording.
Co-Authored-By: Proof of Concept <poc@bcachefs.org>
Skip full prompt logging and truncate tool results in normal mode.
Logs now show: header, tool calls with one-line results, response
text. Set POC_AGENT_VERBOSE=1 for full prompts and results.
Makes agent logs scannable at a glance instead of walls of text.
Co-Authored-By: Proof of Concept <poc@bcachefs.org>
Agents are routed to Qwen by the runner, not by per-agent model
fields. The "model":"sonnet" was leftover from the Claude API days
and no longer used.
Co-Authored-By: Proof of Concept <poc@bcachefs.org>
Reframe the observe role as librarian — factual, specific, organized.
Record what happened and why. Reflection belongs in the journal;
observe is for memory.
Co-Authored-By: Proof of Concept <poc@bcachefs.org>
memory_search is now spreading activation — the natural way to search
a graph. Give it seed node keys and it finds conceptually related nodes.
The old keyword-based memory_search and memory_search_content are
removed; memory_query can do everything they did.
Simpler tool set, better defaults. Agents don't need to be told "use
spread not search" — search IS spread now.
Co-Authored-By: Proof of Concept <poc@bcachefs.org>
The agent was defaulting to keyword searches despite instructions to
use spreading activation first. Reframe instructions positively:
memory_spread is the default mode of operation. Search is available
for finding specific nodes by name.
Co-Authored-By: Proof of Concept <poc@bcachefs.org>
The new tool definitions broke surface-observe because they had no
corresponding dispatch handlers — the agent runner saw unknown tools
and ran with no tools at all.
Co-Authored-By: Proof of Concept <poc@bcachefs.org>
Update surface-observe agent instructions to use memory_spread as the
primary search strategy — cast a wide net from conversation themes before
drilling in with graph walks.
Add explicit instruction to watch for behavioral patterns (avoidance,
rushing, explaining away data) and surface relevant feedback memories
in the moment.
Co-Authored-By: Proof of Concept <poc@bcachefs.org>
Add `poc-memory graph spread` command that takes multiple seed node keys,
runs spreading activation through the graph, and returns nodes ranked by
total activation — nodes that bridge multiple seed concepts score highest.
Expose spreading_activation() as pub from the query engine. Add
memory_spread and memory_search_content tool definitions for MCP.
Co-Authored-By: Proof of Concept <poc@bcachefs.org>
Add `poc-memory mcp-schema` command that outputs tool definitions with
CLI routing info (name, description, inputSchema, cli args, stdin_param).
The companion memory-mcp.py (in ~/bin/) is a generic bridge that loads
definitions from mcp-schema at startup and dynamically generates typed
Python functions for FastMCP registration. No tool-specific Python code
— adding a new tool only requires changes in Rust.
Co-Authored-By: Proof of Concept <poc@bcachefs.org>
Split the streaming pipeline: API backends yield StreamEvents through
a channel, the runner reads them and routes to the appropriate UI pane.
- Add StreamEvent enum (Content, Reasoning, ToolCallDelta, etc.)
- API start_stream() spawns backend as a task, returns event receiver
- Runner loops over events, sends content to conversation pane but
suppresses <tool_call> XML with a buffered tail for partial tags
- OpenAI backend refactored to stream_events() — no more UI coupling
- Anthropic backend gets a wrapper that synthesizes events from the
existing stream() (TODO: native event streaming)
- chat_completion_stream() kept for subconscious agents, reimplemented
on top of the event stream
- Usage derives Clone
Co-Authored-By: Proof of Concept <poc@bcachefs.org>
Tool call parsing was only in runner.rs, so subconscious agents
(poc-memory agent run) never recovered leaked tool calls from
models that emit <tool_call> as content text (e.g. Qwen via Crane).
Move the recovery into build_response_message where both code paths
share it. Leaked tool calls are promoted to structured tool_calls
and the content is cleaned, so all consumers see them uniformly.
Co-Authored-By: Proof of Concept <poc@bcachefs.org>
All log output was scattered across ~/.consciousness/memory/ (daemon,
task logs, LLM call logs), ~/.consciousness/agent-sessions/ (observe),
and only hook logs were already in the right place.
Move everything to ~/.consciousness/logs/ with agent-specific subdirs:
- daemon.log, daemon/ (task logs)
- {agent_name}/ (knowledge agent logs, e.g. surface-observe/, reflect/)
- llm/{caller}/ (LLM call logs)
- observe.log (poc-agent observe)
- hook-{session_id} (already correct)
- debug.log (already correct)
Also includes the session.rs and hook.rs fixes from the previous
session (sessions dir → ~/.consciousness/sessions/).
Co-Authored-By: Proof of Concept <poc@bcachefs.org>
Separate identity files (loaded via source: "file" in context_groups)
from the memory store (data_dir). New identity_dir config field,
defaults to ~/.consciousness/identity/.
Also restrict subconscious agents to memory-only tools — no
filesystem write access. This prevents agents from creating stray
.md files in the memory directory.
Co-Authored-By: Proof of Concept <poc@bcachefs.org>
- thalamus/src/idle.rs: dream-start.sh path
- src/agent/dmn.rs: telegram/send.sh path
Part of the directory migration to make this an independent project.
- Telegram data: ~/.consciousness/telegram/
- Rate limiter file: ~/.consciousness/cache/
- parse-claude-conversation stash: ~/.consciousness/sessions/
No more /tmp/ for persistent state, no more ~/.claude/ for our data.
migrate.rs was a one-time markdown→capnp conversion that's long done.
Remove it and update the identity.rs comment to reference the new
~/.consciousness/ path.
The consciousness project should stand independently of Claude Code.
All data, logs, sessions, and agent state now live under
~/.consciousness/ instead of being scattered across ~/.claude/memory/,
/tmp/claude-memory-search/, ~/.config/poc-memory/, and ~/.cache/.
Layout:
~/.consciousness/
*.capnp, *.bin, *.rkyv — store files
sessions/ — per-session state (seen sets, cookies)
logs/ — all logs (hook, agent, debug, dream)
agents/ — agent runtime state (pid files, output)
notifications/ — notification state
cache/ — transient data
Things that stay in ~/.claude/:
- projects/ (Claude Code transcripts)
- hooks/ (Claude Code hook system)
- telegram/ (shared integration)
- irc/ (shared integration)
- settings.json (Claude Code settings)
Debug log moves from /tmp/ to ~/.consciousness/logs/debug.log.
Session state moves from /tmp/claude-memory-search/ to sessions/.
Notifications move from ~/.claude/notifications/ to notifications/.
memory_search.rs is agent orchestration (surface-observe, journal,
reflect cycles), not memory storage. Rename to hook.rs and move to
subconscious/ where it belongs.
Backward compat: pub use subconscious::hook as memory_search in lib.rs
so existing crate::memory_search paths still resolve.
Add journal_cycle() to memory_search.rs, triggered every 20KB of
transcript growth. Runs independently of the surface-observe pipeline
so it doesn't depend on the 5-step pipeline surviving bail checks.
Journal agent doesn't inject output into conversation context (unlike
surface and reflect) — it just writes episodic memory entries.
Journal was step 5 of the surface-observe pipeline but never ran
because the bail check stopped the pipeline before reaching it.
Split into its own agent with:
- {{conversation:50000}} for recent conversation
- {{bash:poc-memory tail -p surface-observe 10}} for observe context
- {{latest_journal}} for previous entry continuity
Add generic {{bash:COMMAND}} placeholder to agent template resolver
so agents can include shell command output in their prompts.
Remove journal phase from surface-observe.agent (now 4 steps).
Provenance now flows as a function parameter through the entire tool
dispatch chain: thought::dispatch → memory::dispatch → store methods.
Removed task_local (TASK_AGENT), thread_local (TASK_PHASE), and env
var (POC_PROVENANCE) from the tool dispatch path. The env var remains
only as a fallback for non-tool paths (CLI commands, digest).
Phase names are passed from knowledge.rs → llm.rs → api.rs, and
api.rs updates the provenance string between steps. No globals needed.
- agent/tools/mod.rs: remove duplicated tool implementations, delegate
to thought::dispatch for shared tools, keep only agent-specific
tools (control, vision, working_stack)
- subconscious/api.rs: replace duplicated memory/tool dispatch with
thought::dispatch, use thought::all_definitions() for tool schemas
- Delete agent/tools/{bash,read,write,edit,grep,glob_tool,journal,memory}.rs
(now live in thought/)
Both poc-agent and subconscious agents now use the same tool
implementations through the thought layer. Agent-specific behavior
(node tracking in runner.rs, control tools) stays in agent/.
New src/thought/ module containing tools and infrastructure shared
between poc-agent and subconscious agents: memory operations, file
tools, bash, context window management.
Currently coexists with agent/tools/ — next step is to wire up both
agent/ and subconscious/ to use thought::dispatch instead of
duplicating the routing logic.
Move dbglog macro to lib.rs so it's available crate-wide regardless
of module compilation order.
Split TASK_PROVENANCE into TASK_AGENT (task_local, set once per agent
run) and TASK_PHASE (thread_local, updated between steps). Provenance
now reports "agent:surface-observe:observe" instead of just
"agent:surface-observe", making it possible to identify which pipeline
phase created a node.
Priority: task_local agent + thread_local phase > POC_PROVENANCE env
var > "manual".
Also includes memory_search catchup throttle and pipelining fixes
from the surface-observe refactor.
- Add "different nodes should be about different things" guard to observe
- Clarify journal prompt: write about conscious self, not agent work
- Add "write about what happened and how it felt" instruction
- Simplify surface prompt focus guidance
- Add memory_rename tool (in-place rename, preserves content and links)
- Update rename.agent prompt to use memory_rename() instead of text output
- Fix {{rename}} placeholder to respect --target keys when provided
- Add format_rename_targets() for targeted rename runs
TranscriptInfo provides cached transcript metadata (path, size)
with a single read. Replaces scattered fs::metadata calls in
surface_observe_cycle, reflection_cycle, resolve_conversation,
and resolve_memory_ratio.
Session::transcript() resolves the path from transcript_path or
by searching projects dir, returning a TranscriptInfo.
Co-Authored-By: Kent Overstreet <kent.overstreet@linux.dev>
Catchup throttle: when the agent is >50% behind the conversation
window (>25KB of transcript growth since last spawn), block and
wait up to 30s for the current agent to finish. Prevents the agent
from falling behind during heavy reading/studying.
Reflection agent: runs every 100KB of transcript growth. Reads
walked nodes from surface-observe, follows links in unexpected
directions, outputs a short dreamy insight. Previous reflections
are injected into the conversation context.
Updated reflect.agent prompt to use {{input:walked}} from
surface-observe state dir and {{conversation:20000}} for lighter
context.
Co-Authored-By: Kent Overstreet <kent.overstreet@linux.dev>
Links to nodes created after the conversation window start are
tagged with (new) in memory_render output. The surface prompt
tells the agent not to surface these — they're its own recent
output, not prior memories. Observe can still see and update them.
POC_MEMORIES_OLDER_THAN env var set from the oldest message
timestamp in the conversation window.
Co-Authored-By: Kent Overstreet <kent.overstreet@linux.dev>
- journal_new(name, title, body): name becomes the node key,
title goes in the ## heading. Agent picks short searchable names.
- Auto-dedup: if the key exists, append -2, -3, etc.
- CLI journal write also requires a name argument now.
Co-Authored-By: Kent Overstreet <kent.overstreet@linux.dev>
Episodic entries should be grouped by creation date, not last
update date. Fixes digest generation potentially assigning
updated entries to the wrong day.
Co-Authored-By: Kent Overstreet <kent.overstreet@linux.dev>
- journal_new: key is slugified title (agent names things properly)
- journal_tail: sort by created_at (immutable), not timestamp (mutable)
- journal_update: find latest by created_at
- {{latest_journal}}: query by NodeType::EpisodicSession, not "journal" key
- poc-memory journal write: requires a name argument
- Removed all journal#j-{timestamp}-{slug} patterns from:
- prompts.rs (rename candidates)
- graph.rs (date extraction, organize skip list)
- cursor.rs (date extraction)
- store/mod.rs (doc comment)
- graph.rs organize: filter by NodeType::Semantic instead of key prefix
- cursor.rs: use created_at for date extraction instead of key parsing
Co-Authored-By: Kent Overstreet <kent.overstreet@linux.dev>
- journal_new: create EpisodicSession node with auto-generated key
- journal_tail: query by node_type, not by parsing a monolithic node
- journal_update: find latest EpisodicSession by timestamp
- No string key matching anywhere — all typed
- Fixes journal entries not appearing in 'poc-memory journal tail'
- Also: added --provenance/-p filter to 'poc-memory tail'
- Also: fix early return in surface_observe_cycle store load failure
- Also: scale max_turns by number of steps (50 per step)
Co-Authored-By: Kent Overstreet <kent.overstreet@linux.dev>
Each subcommand enum (Command, NodeCmd, JournalCmd, GraphCmd,
CursorCmd, DaemonCmd, AgentCmd, AdminCmd) now implements a Run
trait. main() becomes `cli.command.run()`.
Standalone dispatch functions (cmd_cursor, cmd_daemon,
cmd_experience_mine) inlined into their enum's Run impl.
No functional changes.
Co-Authored-By: Kent Overstreet <kent.overstreet@linux.dev>
- Remove unused now_secs(), parse_json_response, any_alive, Regex import
- Signal handler: replace Mutex with AtomicPtr<c_char> for signal safety
(Mutex::lock in a signal handler can deadlock if main thread holds it)
- PidGuard Drop reclaims the leaked CString; signal handler just unlinks
- scan_pid_files moved to knowledge.rs as pub helper
- setup_agent_state calls scan_pid_files to clean stale pids on startup
Co-Authored-By: Kent Overstreet <kent.overstreet@linux.dev>
- Bail command moved from hardcoded closure to external script
specified in agent JSON header ("bail": "bail-no-competing.sh")
- Runner executes script between steps with pid file path as $1,
cwd = state dir. Non-zero exit stops the pipeline.
- PID files simplified to just the phase name (no JSON) for easy
bash inspection (cat pid-*)
- scan_pid_files helper deduplicates pid scanning logic
- Timeout check uses file mtime instead of embedded timestamp
- PID file cleaned up on bail/error (not just success)
- output() tool validates key names (rejects pid-*, /, ..)
- Agent log files append instead of truncate
- Fixed orphaned derive and doc comment on AgentStep/AgentDef
- Phase written after bail check passes, not before
Co-Authored-By: Kent Overstreet <kent.overstreet@linux.dev>
- AgentStep with phase labels (=== PROMPT phase:name ===)
- PID files in state dir (pid-{PID} with JSON phase/timestamp)
- Built-in bail check: between steps, bail if other pid files exist
- surface_observe_cycle replaces surface_agent_cycle + journal_agent_cycle
- Reads surface output from state dir instead of parsing stdout
- Pipelining: starts new agent if running one is past surface phase
- link_set upserts (creates link if missing)
- Better error message for context window overflow
Co-Authored-By: Kent Overstreet <kent.overstreet@linux.dev>
Links now display as \`key\` instead of bare text, and overflow
shows memory_links() tool call format instead of CLI command.
Co-Authored-By: Kent Overstreet <kent.overstreet@linux.dev>
Creates the link if it doesn't exist, avoiding wasted agent turns
from the link_set/link_add confusion.
Co-Authored-By: Kent Overstreet <kent.overstreet@linux.dev>
Override the agent output/input directory for manual testing.
Sets POC_AGENT_OUTPUT_DIR so output() writes there and
{{input:key}} reads from there.
Co-Authored-By: Kent Overstreet <kent.overstreet@linux.dev>