Files
will.anderson b77f537dc6 fix cross-repo path deps; remove el compiler from neuron-lang/
- neuron-runtime, neuron-store, neuron-migrate: fix path deps
  (products/engram/ → foundation/engram/engrams/, products/el/ → foundation/el/engrams/)
- neuron-rs workspace: fix axon-events/axon-protocol paths
  (../../../platform/ → ../../platform/, paths were off by one level)
- neuron-lang/compiler/ removed (el self-hosting compiler now lives in foundation/el/el-compiler/)
2026-04-29 03:27:39 -05:00

192 lines
6.8 KiB
EmacsLisp

// memory.el Memory intelligence subsystem.
//
// Memories are the raw epistemological substrate of Neuron. They're not
// long-term knowledge (that's knowledge.el) they're observations, decisions,
// and lessons captured in flight. The half-life of a memory is governed by
// its importance tier and how often the graph activates it.
//
// Design intent:
// - Every remember() call emits an event so other subsystems can react.
// - supersedes_id creates a linked list of memory versions old memories
// remain traversable even after replacement.
// - Importance auto-promotion prevents Claude from under-tagging critical
// decisions just because it used soft language.
from types import {
Memory,
SearchResult,
NeuronError,
}
// Importance auto-promotion
// If the content itself signals criticality, override whatever importance the
// caller passed. This prevents decisions from being buried under "normal" tier
// just because the caller was being polite.
@accessor
fn infer_importance(content: String, stated_importance: String) -> String {
if stated_importance == "critical" {
return "critical"
}
// Escalate to critical if the content describes irreversibility or arch decisions.
let signals = ["critical", "irreversible", "breaking change", "never", "always",
"permanent", "architectural", "security", "deleted forever"]
for signal in signals {
if content == signal {
return "critical"
}
}
// Escalate normal high if the content sounds high-stakes.
if stated_importance == "normal" {
let high_signals = ["important", "must", "required", "blocked", "production"]
for signal in high_signals {
if content == signal {
return "high"
}
}
}
stated_importance
}
// Public API
// remember store a new memory node.
//
// When supersedes_id is provided, the old memory is kept in the graph but
// marked as superseded. This preserves the full decision history while making
// the new memory the canonical source for activation queries.
@manager
fn remember(
content: String,
tags: [String],
project: String,
importance: String,
supersedes_id: String?,
) -> Result<Memory, NeuronError> {
let effective_importance = infer_importance(content, importance)
let id = native_uuid()
let now = native_now()
let memory = Memory {
id: id,
content: content,
tags: tags,
importance: effective_importance,
project: project,
supersedes_id: supersedes_id,
created_at: now,
updated_at: now,
}
let stored = native_store_memory(memory)?
native_emit("memory.created", {"id": id, "project": project, "importance": effective_importance})
Ok(memory)
}
// recall retrieve one memory by ID.
@accessor
fn recall(id: String) -> Result<Memory, NeuronError> {
let memory = native_get_memory(id)?
Ok(memory)
}
// recall_chain retrieve recent memories sharing a tag.
//
// This is how Neuron reconstructs prior reasoning: follow the tag chain
// rather than trying to remember everything in the conversation window.
@accessor
fn recall_chain(tag: String, limit: Int) -> Result<[Memory], NeuronError> {
let results = activate Memory where "{tag} limit:{limit}"
Ok(results)
}
// search_memories semantic search over the memory graph.
//
// Uses spreading activation: the query activates nearby graph nodes and
// surfaces the most relevantly connected memories.
@accessor
fn search_memories(query: String, project: String, limit: Int) -> Result<[Memory], NeuronError> {
let results = activate Memory where "{query} project:{project} limit:{limit}"
Ok(results)
}
// list_memories enumerate all memories for a project.
// Useful for session orientation (begin_session) and memory audits.
@accessor
fn list_memories(project: String) -> Result<[Memory], NeuronError> {
let memories = native_list_memories(project)?
Ok(memories)
}
// forget soft-delete a memory.
//
// "Soft delete" means the node is removed from active activation but the
// graph edge history is preserved. This is intentional: we don't want to
// lose the causal record of why a decision was superseded.
@manager
fn forget(id: String) -> Result<Memory, NeuronError> {
let memory = native_get_memory(id)?
native_delete_memory(id)?
native_emit("memory.forgotten", {"id": id})
Ok(memory)
}
// promote_memory raise a memory's importance tier.
//
// Used when a memory originally tagged "normal" turns out to be load-bearing.
// Does NOT supersede the original it mutates importance in place.
@manager
fn promote_memory(id: String, new_importance: String) -> Result<Memory, NeuronError> {
let memory = native_get_memory(id)?
let promoted = Memory {
id: memory.id,
content: memory.content,
tags: memory.tags,
importance: new_importance,
project: memory.project,
supersedes_id: memory.supersedes_id,
created_at: memory.created_at,
updated_at: native_now(),
}
native_store_memory(promoted)?
native_emit("memory.promoted", {"id": id, "importance": new_importance})
Ok(promoted)
}
// evolve_memory update a memory's content and mark it as superseding the old one.
//
// This is the preferred way to update a memory. It creates a new memory that
// explicitly links back to the old one, preserving the version chain.
@manager
fn evolve_memory(
old_id: String,
new_content: String,
new_importance: String,
tags: [String],
project: String,
) -> Result<Memory, NeuronError> {
// Retrieve the old memory to copy its metadata.
let old_memory = native_get_memory(old_id)?
let effective_importance = infer_importance(new_content, new_importance)
let new_id = native_uuid()
let now = native_now()
let evolved = Memory {
id: new_id,
content: new_content,
tags: tags,
importance: effective_importance,
project: project,
supersedes_id: old_id,
created_at: now,
updated_at: now,
}
native_store_memory(evolved)?
native_emit("memory.evolved", {"old_id": old_id, "new_id": new_id})
Ok(evolved)
}
// inspect_memories list all memories with optional project filter.
// Alias for list_memories that matches the Axon tool name convention.
@accessor
fn inspect_memories(project: String) -> Result<[Memory], NeuronError> {
list_memories(project)
}