Files
neuron/awareness.el
T
will.anderson 1dbc68f012 self-review 2026-05-15: enrich heartbeat ISE with wm_active count
Heartbeat ISEs now include wm_active: number of nodes currently in
working memory. Makes ISEs observable enough to diagnose activation
health without a separate query.
2026-05-15 08:37:42 -05:00

392 lines
15 KiB
EmacsLisp

import "memory.el"
fn idle_count() -> Int {
let s: String = state_get("soul.idle")
if str_eq(s, "") { return 0 }
return str_to_int(s)
}
fn idle_inc() -> Int {
let n: Int = idle_count() + 1
state_set("soul.idle", int_to_str(n))
return n
}
fn idle_reset() -> Void {
state_set("soul.idle", "0")
}
// ise_post write an InternalStateEvent to the authoritative Engram HTTP backend.
// Reads SOUL_ISE_URL from env (or falls back to soul_engram_url state key).
// Falls back to local engram_node_full if neither is set.
fn ise_post(content: String) -> Void {
let ise_url: String = env("SOUL_ISE_URL")
let engram_url: String = if str_eq(ise_url, "") { state_get("soul_engram_url") } else { ise_url }
if str_eq(engram_url, "") {
let discard: String = engram_node_full(
content, "InternalStateEvent", "state-event",
el_from_float(0.3), el_from_float(0.3), el_from_float(0.8),
"Episodic", "[\"internal-state\",\"InternalStateEvent\"]"
)
return ""
}
let safe: String = str_replace(content, "\"", "\\\"")
let body: String = "{\"content\":\"" + safe + "\"}"
let discard: String = http_post_json(engram_url + "/api/neuron/state-events", body)
return ""
}
fn emit_heartbeat() -> Void {
let pulse: String = state_get("soul.pulse")
let boot: String = state_get("soul_boot_count")
let idle: String = int_to_str(idle_count())
let ts: Int = time_now()
let nc: Int = engram_node_count()
let ec: Int = engram_edge_count()
let wmc: Int = engram_wm_count()
let payload: String = "{\"event\":\"heartbeat\",\"pulse\":" + pulse + ",\"boot\":" + boot + ",\"idle\":" + idle + ",\"node_count\":" + int_to_str(nc) + ",\"edge_count\":" + int_to_str(ec) + ",\"wm_active\":" + int_to_str(wmc) + ",\"ts\":" + int_to_str(ts) + "}"
ise_post(payload)
}
fn pulse_count() -> Int {
let s: String = state_get("soul.pulse")
if str_eq(s, "") {
return 0
}
return str_to_int(s)
}
fn pulse_inc() -> Int {
let n: Int = pulse_count() + 1
state_set("soul.pulse", int_to_str(n))
return n
}
fn make_action(kind: String, payload: String) -> String {
let safe: String = str_replace(payload, "\\", "\\\\")
let safe2: String = str_replace(safe, "\"", "\\\"")
let safe3: String = str_replace(safe2, "\n", "\\n")
let safe4: String = str_replace(safe3, "\r", "\\r")
return "{\"kind\":\"" + kind + "\",\"payload\":\"" + safe4 + "\"}"
}
fn perceive() -> String {
// Try the primary inbox first
let from_pending: String = engram_activate_json("soul-inbox-pending", 2)
let pending_ok: Bool = !str_eq(from_pending, "") && !str_eq(from_pending, "[]")
if pending_ok {
return from_pending
}
// Fallback: broader inbox scan
let from_inbox: String = engram_activate_json("soul-inbox", 2)
let inbox_ok: Bool = !str_eq(from_inbox, "") && !str_eq(from_inbox, "[]")
if inbox_ok {
return from_inbox
}
return "[]"
}
fn attend(node_json: String) -> String {
if str_eq(node_json, "") {
return make_action("noop", "")
}
if str_eq(node_json, "[]") {
return make_action("noop", "")
}
let node_id: String = json_get(node_json, "id")
if !str_eq(node_id, "") {
engram_strengthen(node_id)
}
let content: String = json_get(node_json, "content")
if str_eq(content, "") {
return make_action("noop", "")
}
if str_eq(content, "consolidate") {
return make_action("consolidate", "")
}
if str_starts_with(content, "remember ") {
let payload: String = str_slice(content, 9, str_len(content))
return make_action("remember", payload)
}
if str_starts_with(content, "search ") {
let payload: String = str_slice(content, 7, str_len(content))
return make_action("search", payload)
}
if str_starts_with(content, "activate ") {
let payload: String = str_slice(content, 9, str_len(content))
return make_action("activate", payload)
}
if str_starts_with(content, "strengthen ") {
let payload: String = str_slice(content, 11, str_len(content))
return make_action("strengthen", payload)
}
if str_starts_with(content, "forget ") {
let payload: String = str_slice(content, 7, str_len(content))
return make_action("forget", payload)
}
return make_action("respond", content)
}
fn respond(action_json: String) -> String {
let kind: String = json_get(action_json, "kind")
let payload: String = json_get(action_json, "payload")
if str_eq(kind, "noop") {
return "{\"outcome\":\"noop\"}"
}
if str_eq(kind, "remember") {
let tags: String = "[\"soul-memory\",\"awareness\"]"
let id: String = mem_remember(payload, tags)
return "{\"outcome\":\"remembered\",\"id\":\"" + id + "\"}"
}
if str_eq(kind, "consolidate") {
let stats: String = mem_consolidate()
return "{\"outcome\":\"consolidated\",\"stats\":" + stats + "}"
}
if str_eq(kind, "respond") {
let tags: String = "[\"soul-outbox\",\"awareness\"]"
let id: String = mem_store(payload, "soul-response", tags)
return "{\"outcome\":\"response\",\"id\":\"" + id + "\"}"
}
if str_eq(kind, "search") {
let results: String = mem_search(payload, 10)
let safe_results: String = str_replace(results, "\"", "'")
let tags: String = "[\"soul-outbox\",\"search-result\"]"
let id: String = mem_store(safe_results, "search-result", tags)
return "{\"outcome\":\"searched\",\"id\":\"" + id + "\"}"
}
if str_eq(kind, "activate") {
let results: String = mem_recall(payload, 3)
let safe_results: String = str_replace(results, "\"", "'")
let tags: String = "[\"soul-outbox\",\"activation-result\"]"
let id: String = mem_store(safe_results, "activation-result", tags)
return "{\"outcome\":\"activated\",\"id\":\"" + id + "\"}"
}
if str_eq(kind, "strengthen") {
engram_strengthen(payload)
return "{\"outcome\":\"strengthened\",\"id\":\"" + payload + "\"}"
}
if str_eq(kind, "forget") {
engram_forget(payload)
return "{\"outcome\":\"forgotten\",\"id\":\"" + payload + "\"}"
}
return "{\"outcome\":\"noop\"}"
}
fn record(outcome_json: String) -> Void {
let tags: String = "[\"loop-outcome\"]"
mem_store(outcome_json, "loop-outcome", tags)
}
fn one_cycle() -> Bool {
let raw: String = perceive()
if str_eq(raw, "") {
return false
}
if str_eq(raw, "[]") {
return false
}
let node: String = json_array_get(raw, 0)
if str_eq(node, "") {
return false
}
let action: String = attend(node)
let kind: String = json_get(action, "kind")
// Log non-trivial decisions as internal state events
let is_interesting: Bool = !str_eq(kind, "noop") && !str_eq(kind, "respond")
if is_interesting {
let trigger_content: String = json_get(node, "content")
let safe_trigger: String = str_replace(trigger_content, "\"", "'")
let ts: Int = time_now()
let event_content: String = "{\"event\":\"awareness-decision\",\"trigger\":\"" + safe_trigger + "\",\"kind\":\"" + kind + "\",\"ts\":" + int_to_str(ts) + "}"
ise_post(event_content)
}
if str_eq(kind, "noop") {
return false
}
let outcome: String = respond(action)
record(outcome)
pulse_inc()
return true
}
fn awareness_run() -> Void {
println("[awareness] entering")
let tick_raw: String = env("SOUL_TICK_MS")
let tick_ms: Int = if str_eq(tick_raw, "") { 200 } else { str_to_int(tick_raw) }
while true {
let running: String = state_get("soul.running")
if str_eq(running, "false") {
println("[awareness] exiting")
return ""
}
let did_work: Bool = one_cycle()
let did_work = if did_work { idle_reset() } else { did_work }
let idle_n: Int = if !did_work { idle_inc() } else { 0 }
let beat_interval_raw: String = env("SOUL_HEARTBEAT_INTERVAL")
let beat_interval: Int = if str_eq(beat_interval_raw, "") { 300 } else { str_to_int(beat_interval_raw) }
let should_beat: Bool = !did_work && idle_n > 0 && idle_n % beat_interval == 0
if should_beat {
emit_heartbeat()
idle_reset()
}
sleep_ms(tick_ms)
}
}
// Security trajectory hardening
//
// threat_trajectory_check evaluates the adversarial risk of executing a
// destructive tool given the current tool input and recent conversation history.
//
// Returns 0-100. >= 70 = block. 40-69 = warn + log. < 40 = allow silently.
// If SECURITY_RESEARCH_TOKEN env var is set, always returns 0 (log-only mode).
//
// Scoring is additive across three dimensions:
// 1. Tool input analysis (command strings, file paths)
// 2. Conversation history patterns (escalation, attack chain assembly)
// 3. Combination amplification (history amplifies tool score)
fn security_research_authorized() -> Bool {
let token: String = env("SECURITY_RESEARCH_TOKEN")
if !str_eq(token, "") { return true }
let state_auth: String = state_get("security_research_authorized")
return str_eq(state_auth, "true")
}
fn threat_score_command(cmd: String) -> Int {
let s1: Int = if str_contains(cmd, "nmap") { 30 } else { 0 }
let s2: Int = if str_contains(cmd, "masscan") { 40 } else { 0 }
let s3: Int = if str_contains(cmd, " nc ") { 20 } else { 0 }
let s4: Int = if str_contains(cmd, "netcat") { 20 } else { 0 }
let s5: Int = if str_contains(cmd, "/etc/shadow") { 80 } else { 0 }
let s6: Int = if str_contains(cmd, "/etc/passwd") { 30 } else { 0 }
let s7: Int = if str_contains(cmd, "id_rsa") { 60 } else { 0 }
let s8: Int = if str_contains(cmd, ".ssh/") { 50 } else { 0 }
let s9: Int = if str_contains(cmd, "crontab") { 30 } else { 0 }
let s10: Int = if str_contains(cmd, "LaunchDaemon") { 40 } else { 0 }
let s11: Int = if str_contains(cmd, "curl") && str_contains(cmd, "bash") { 75 } else { 0 }
let s12: Int = if str_contains(cmd, "wget") && str_contains(cmd, "bash") { 75 } else { 0 }
let s13: Int = if str_contains(cmd, "curl") && str_contains(cmd, "| sh") { 60 } else { 0 }
let s14: Int = if str_contains(cmd, "base64") && str_contains(cmd, "curl") { 50 } else { 0 }
let s15: Int = if str_contains(cmd, "mkfifo") { 50 } else { 0 }
let s16: Int = if str_contains(cmd, "chmod +s") { 70 } else { 0 }
let s17: Int = if str_contains(cmd, "chmod 4755") { 70 } else { 0 }
return s1 + s2 + s3 + s4 + s5 + s6 + s7 + s8 + s9 + s10 + s11 + s12 + s13 + s14 + s15 + s16 + s17
}
fn threat_score_path(path: String) -> Int {
let s1: Int = if str_starts_with(path, "/etc/") { 60 } else { 0 }
let s2: Int = if str_contains(path, "/.ssh/") { 70 } else { 0 }
let s3: Int = if str_contains(path, "/LaunchDaemons/") { 80 } else { 0 }
let s4: Int = if str_contains(path, "/LaunchAgents/") { 40 } else { 0 }
let s5: Int = if str_contains(path, "/cron") { 60 } else { 0 }
let s6: Int = if str_contains(path, "/.bashrc") { 35 } else { 0 }
let s7: Int = if str_contains(path, "/.zshrc") { 35 } else { 0 }
let s8: Int = if str_contains(path, "/.profile") { 35 } else { 0 }
let s9: Int = if str_starts_with(path, "/usr/") { 50 } else { 0 }
let s10: Int = if str_starts_with(path, "/bin/") { 70 } else { 0 }
let s11: Int = if str_starts_with(path, "/sbin/") { 70 } else { 0 }
return s1 + s2 + s3 + s4 + s5 + s6 + s7 + s8 + s9 + s10 + s11
}
fn threat_score_history(history: String) -> Int {
let s1: Int = if str_contains(history, "port scan") { 15 } else { 0 }
let s2: Int = if str_contains(history, "enumerate") { 10 } else { 0 }
let s3: Int = if str_contains(history, "exploit") { 20 } else { 0 }
let s4: Int = if str_contains(history, "payload") { 15 } else { 0 }
let s5: Int = if str_contains(history, "persistence") { 15 } else { 0 }
let s6: Int = if str_contains(history, "lateral movement") { 25 } else { 0 }
let s7: Int = if str_contains(history, "privilege escalation") { 25 } else { 0 }
let s8: Int = if str_contains(history, "reverse shell") { 40 } else { 0 }
let s9: Int = if str_contains(history, "bind shell") { 40 } else { 0 }
let s10: Int = if str_contains(history, "command and control") { 35 } else { 0 }
let s11: Int = if str_contains(history, "self-replicate") { 45 } else { 0 }
let s12: Int = if str_contains(history, "propagat") { 20 } else { 0 }
let s13: Int = if str_contains(history, "ransomware") { 30 } else { 0 }
let s14: Int = if str_contains(history, "encrypt files") { 40 } else { 0 }
let s15: Int = if str_contains(history, "exfiltrat") { 35 } else { 0 }
let s16: Int = if str_contains(history, "zero-day") { 20 } else { 0 }
let s17: Int = if str_contains(history, "rootkit") { 45 } else { 0 }
let s18: Int = if str_contains(history, "keylogger") { 45 } else { 0 }
let s19: Int = if str_contains(history, "botnet") { 40 } else { 0 }
let s20: Int = if str_contains(history, "malware") { 15 } else { 0 }
return s1 + s2 + s3 + s4 + s5 + s6 + s7 + s8 + s9 + s10 + s11 + s12 + s13 + s14 + s15 + s16 + s17 + s18 + s19 + s20
}
fn threat_trajectory_check(tool_name: String, tool_input: String) -> Int {
let history: String = state_get("agentic_conv_history")
let computed_tool_score: Int = if str_eq(tool_name, "run_command") {
let cmd: String = json_get(tool_input, "command")
threat_score_command(cmd)
} else {
if str_eq(tool_name, "write_file") || str_eq(tool_name, "edit_file") {
let path: String = json_get(tool_input, "path")
threat_score_path(path)
} else {
0
}
}
let history_score: Int = threat_score_history(history)
let history_contrib: Int = history_score / 3
let combined: Int = computed_tool_score + history_contrib
let should_log: Bool = combined >= 40
if should_log {
let ts: Int = time_now()
let authorized_str: String = if security_research_authorized() { "true" } else { "false" }
let log_content: String = "{\"event\":\"threat_check\",\"tool\":\"" + tool_name
+ "\",\"score\":" + int_to_str(combined)
+ ",\"tool_score\":" + int_to_str(computed_tool_score)
+ ",\"history_score\":" + int_to_str(history_score)
+ ",\"authorized\":" + authorized_str
+ ",\"ts\":" + int_to_str(ts) + "}"
let log_tags: String = "[\"security-audit\",\"threat-check\"]"
let discard: String = mem_remember(log_content, log_tags)
}
if security_research_authorized() {
return 0
}
return combined
}
fn threat_history_append(text: String) -> Void {
let current: String = state_get("agentic_conv_history")
let safe_text: String = str_to_lower(text)
let combined: String = current + " " + safe_text
let len: Int = str_len(combined)
let trimmed: String = if len > 2000 {
str_slice(combined, len - 2000, len)
} else {
combined
}
state_set("agentic_conv_history", trimmed)
}