feat(awareness): route ISE writes to HTTP Engram, configurable tick and heartbeat interval, http_serve_async for concurrent awareness loop
This commit is contained in:
+189
-8
@@ -1,5 +1,49 @@
|
||||
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 ts: Int = time_now()
|
||||
let payload: String = "{\"event\":\"heartbeat\",\"pulse\":" + pulse + ",\"boot\":" + boot + ",\"ts\":" + int_to_str(ts) + "}"
|
||||
ise_post(payload)
|
||||
}
|
||||
|
||||
fn pulse_count() -> Int {
|
||||
let s: String = state_get("soul.pulse")
|
||||
if str_eq(s, "") {
|
||||
@@ -169,14 +213,9 @@ fn one_cycle() -> Bool {
|
||||
if is_interesting {
|
||||
let trigger_content: String = json_get(node, "content")
|
||||
let safe_trigger: String = str_replace(trigger_content, "\"", "'")
|
||||
let tags: String = "[\"internal-state\",\"awareness-decision\"]"
|
||||
let ts: Int = time_now()
|
||||
let event_content: String = "{\"trigger\":\"" + safe_trigger + "\",\"kind\":\"" + kind + "\",\"ts\":" + int_to_str(ts) + "}"
|
||||
let discard_ev: String = engram_node_full(
|
||||
event_content, "InternalStateEvent", "state-event:" + kind,
|
||||
el_from_float(0.85), el_from_float(0.8), el_from_float(0.9),
|
||||
"Episodic", tags
|
||||
)
|
||||
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") {
|
||||
@@ -200,7 +239,149 @@ fn awareness_run() -> Void {
|
||||
println("[awareness] exiting")
|
||||
return ""
|
||||
}
|
||||
one_cycle()
|
||||
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)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user