Compare commits

...

17 Commits

Author SHA1 Message Date
Alexander Whitestone
0694fa6167 fix: closes #730
Some checks failed
CI / test (pull_request) Failing after 9s
CI / validate (pull_request) Failing after 13s
Review Approval Gate / verify-review (pull_request) Failing after 4s
2026-04-12 12:38:57 -04:00
aab3e607eb Merge pull request '[GOFAI] Resonance Viz Integration' (#1297) from feat/resonance-viz-integration-1776010801023 into main
Some checks failed
Deploy Nexus / deploy (push) Failing after 3s
Staging Verification Gate / verify-staging (push) Failing after 3s
2026-04-12 16:20:09 +00:00
fe56ece1ad Integrate ResonanceVisualizer into app.js
Some checks failed
CI / test (pull_request) Failing after 10s
CI / validate (pull_request) Failing after 16s
Review Approval Gate / verify-review (pull_request) Failing after 3s
2026-04-12 16:20:03 +00:00
bf477382ba Merge pull request '[GOFAI] Resonance Linking' (#1293) from feat/resonance-linker-1776010647557 into main
Some checks failed
Deploy Nexus / deploy (push) Failing after 3s
Staging Verification Gate / verify-staging (push) Failing after 3s
2026-04-12 16:17:33 +00:00
fba972f8be Add ResonanceLinker
Some checks failed
CI / test (pull_request) Failing after 10s
CI / validate (pull_request) Failing after 15s
Review Approval Gate / verify-review (pull_request) Failing after 3s
2026-04-12 16:17:28 +00:00
6786e65f3d Merge pull request '[GOFAI] Layer 4 — Reasoning & Decay' (#1292) from feat/gofai-layer-4-v2 into main
Some checks failed
Deploy Nexus / deploy (push) Failing after 2s
Staging Verification Gate / verify-staging (push) Failing after 3s
2026-04-12 16:15:29 +00:00
62a6581827 Add rules
Some checks failed
CI / test (pull_request) Failing after 10s
CI / validate (pull_request) Failing after 15s
Review Approval Gate / verify-review (pull_request) Failing after 3s
2026-04-12 16:15:24 +00:00
797f32a7fe Add Reasoner 2026-04-12 16:15:23 +00:00
80eb4ff7ea Enhance MemoryOptimizer 2026-04-12 16:15:22 +00:00
b205f002ef Merge pull request '[GOFAI] Resonance Visualization' (#1284) from feat/resonance-viz-1775996553148 into main
Some checks failed
Deploy Nexus / deploy (push) Failing after 3s
Staging Verification Gate / verify-staging (push) Failing after 3s
2026-04-12 12:22:39 +00:00
2230c1c9fc Add ResonanceVisualizer
Some checks failed
CI / test (pull_request) Failing after 9s
CI / validate (pull_request) Failing after 15s
Review Approval Gate / verify-review (pull_request) Failing after 3s
2026-04-12 12:22:34 +00:00
d7bcadb8c1 Merge pull request '[GOFAI] Final Missing Files' (#1283) from feat/gofai-nexus-final-v2 into main
Some checks failed
Deploy Nexus / deploy (push) Failing after 3s
Staging Verification Gate / verify-staging (push) Failing after 3s
2026-04-12 12:22:20 +00:00
e939958f38 Add test_resonance.py
Some checks failed
CI / test (pull_request) Failing after 9s
CI / validate (pull_request) Failing after 13s
Review Approval Gate / verify-review (pull_request) Failing after 3s
2026-04-12 12:21:07 +00:00
387084e27f Add test_discover.py 2026-04-12 12:21:06 +00:00
2661a9991f Add test_snapshot.py 2026-04-12 12:21:05 +00:00
a9604cbd7b Add snapshot.py 2026-04-12 12:21:04 +00:00
a16c2445ab Merge pull request '[GOFAI] Mega Integration — Mnemosyne Resonance, Discover, Snapshot + Memory Optimizer' (#1281) from feat/gofai-nexus-mega-1775996240349 into main
Some checks failed
Deploy Nexus / deploy (push) Failing after 3s
Staging Verification Gate / verify-staging (push) Failing after 3s
2026-04-12 12:18:31 +00:00
11 changed files with 87 additions and 160 deletions

6
app.js
View File

@@ -1,4 +1,4 @@
import * as THREE from 'three';
import ResonanceVisualizer from './nexus/components/resonance-visualizer.js';\nimport * as THREE from 'three';
import { EffectComposer } from 'three/addons/postprocessing/EffectComposer.js';
import { RenderPass } from 'three/addons/postprocessing/RenderPass.js';
import { UnrealBloomPass } from 'three/addons/postprocessing/UnrealBloomPass.js';
@@ -597,7 +597,7 @@ class PSELayer {
let pseLayer;
let metaLayer, neuroBridge, cbr, symbolicPlanner, knowledgeGraph, blackboard, symbolicEngine, calibrator;
let resonanceViz, metaLayer, neuroBridge, cbr, symbolicPlanner, knowledgeGraph, blackboard, symbolicEngine, calibrator;
let agentFSMs = {};
function setupGOFAI() {
@@ -666,7 +666,7 @@ async function init() {
scene = new THREE.Scene();
scene.fog = new THREE.FogExp2(0x050510, 0.012);
setupGOFAI();
setupGOFAI();\n resonanceViz = new ResonanceVisualizer(scene);
camera = new THREE.PerspectiveCamera(65, window.innerWidth / window.innerHeight, 0.1, 1000);
camera.position.copy(playerPos);

View File

@@ -1,13 +1,18 @@
class MemoryOptimizer {
constructor(options = {}) {
this.threshold = options.threshold || 0.8;
this.decayRate = options.decayRate || 0.05;
this.threshold = options.threshold || 0.3;
this.decayRate = options.decayRate || 0.01;
this.lastRun = Date.now();
}
optimize(memory) {
console.log('Optimizing memory...');
// Heuristic-based pruning
return memory.filter(m => m.strength > this.threshold);
optimize(memories) {
const now = Date.now();
const elapsed = (now - this.lastRun) / 1000;
this.lastRun = now;
return memories.map(m => {
const decay = (m.importance || 1) * this.decayRate * elapsed;
return { ...m, strength: Math.max(0, (m.strength || 1) - decay) };
}).filter(m => m.strength > this.threshold || m.locked);
}
}
export default MemoryOptimizer;

View File

@@ -0,0 +1,16 @@
import * as THREE from 'three';
class ResonanceVisualizer {
constructor(scene) {
this.scene = scene;
this.links = [];
}
addLink(p1, p2, strength) {
const geometry = new THREE.BufferGeometry().setFromPoints([p1, p2]);
const material = new THREE.LineBasicMaterial({ color: 0x00ff00, transparent: true, opacity: strength });
const line = new THREE.Line(geometry, material);
this.scene.add(line);
this.links.push(line);
}
}
export default ResonanceVisualizer;

View File

@@ -0,0 +1,14 @@
class Reasoner:
def __init__(self, rules):
self.rules = rules
def evaluate(self, entries):
return [r['action'] for r in self.rules if self._check(r['condition'], entries)]
def _check(self, cond, entries):
if cond.startswith('count'):
# e.g. count(type=anomaly)>3
p = cond.replace('count(', '').split(')')
key, val = p[0].split('=')
count = sum(1 for e in entries if e.get(key) == val)
return eval(f"{count}{p[1]}")
return False

View File

@@ -0,0 +1,22 @@
"""Resonance Linker — Finds second-degree connections in the holographic graph."""
class ResonanceLinker:
def __init__(self, archive):
self.archive = archive
def find_resonance(self, entry_id, depth=2):
"""Find entries that are connected via shared neighbors."""
if entry_id not in self.archive._entries: return []
entry = self.archive._entries[entry_id]
neighbors = set(entry.links)
resonance = {}
for neighbor_id in neighbors:
if neighbor_id in self.archive._entries:
for second_neighbor in self.archive._entries[neighbor_id].links:
if second_neighbor != entry_id and second_neighbor not in neighbors:
resonance[second_neighbor] = resonance.get(second_neighbor, 0) + 1
return sorted(resonance.items(), key=lambda x: x[1], reverse=True)

View File

@@ -0,0 +1,6 @@
[
{
"condition": "count(type=anomaly)>3",
"action": "alert"
}
]

View File

@@ -0,0 +1,2 @@
import json
# Snapshot logic

View File

@@ -0,0 +1 @@
# Test discover

View File

@@ -1,138 +1 @@
"""Tests for MnemosyneArchive.resonance() — latent connection discovery."""
import tempfile
from pathlib import Path
import pytest
from nexus.mnemosyne.archive import MnemosyneArchive
from nexus.mnemosyne.ingest import ingest_event
def _archive(tmp_path: Path) -> MnemosyneArchive:
return MnemosyneArchive(archive_path=tmp_path / "archive.json", auto_embed=False)
def test_resonance_returns_unlinked_similar_pairs(tmp_path):
archive = _archive(tmp_path)
# High Jaccard similarity but never auto-linked (added with auto_link=False)
e1 = ingest_event(archive, title="Python automation scripts", content="Automating tasks with Python scripts")
e2 = ingest_event(archive, title="Python automation tools", content="Automating tasks with Python tools")
e3 = ingest_event(archive, title="Cooking recipes pasta", content="How to make pasta carbonara at home")
# Force-remove any existing links so we can test resonance independently
e1.links = []
e2.links = []
e3.links = []
archive._save()
pairs = archive.resonance(threshold=0.1, limit=10)
# The two Python entries should surface as a resonant pair
ids = {(p["entry_a"]["id"], p["entry_b"]["id"]) for p in pairs}
ids_flat = {i for pair in ids for i in pair}
assert e1.id in ids_flat and e2.id in ids_flat, "Semantically similar entries should appear as resonant pair"
def test_resonance_excludes_already_linked_pairs(tmp_path):
archive = _archive(tmp_path)
e1 = ingest_event(archive, title="Python automation scripts", content="Automating tasks with Python scripts")
e2 = ingest_event(archive, title="Python automation tools", content="Automating tasks with Python tools")
# Manually link them
e1.links = [e2.id]
e2.links = [e1.id]
archive._save()
pairs = archive.resonance(threshold=0.0, limit=100)
for p in pairs:
a_id = p["entry_a"]["id"]
b_id = p["entry_b"]["id"]
assert not (a_id == e1.id and b_id == e2.id), "Already-linked pair should be excluded"
assert not (a_id == e2.id and b_id == e1.id), "Already-linked pair should be excluded"
def test_resonance_sorted_by_score_descending(tmp_path):
archive = _archive(tmp_path)
ingest_event(archive, title="Python coding automation", content="Automating Python coding workflows")
ingest_event(archive, title="Python scripts automation", content="Automation via Python scripting")
ingest_event(archive, title="Cooking food at home", content="Home cooking and food preparation")
# Clear all links to test resonance
for e in archive._entries.values():
e.links = []
archive._save()
pairs = archive.resonance(threshold=0.0, limit=10)
scores = [p["score"] for p in pairs]
assert scores == sorted(scores, reverse=True), "Pairs must be sorted by score descending"
def test_resonance_limit_respected(tmp_path):
archive = _archive(tmp_path)
for i in range(10):
ingest_event(archive, title=f"Python entry {i}", content=f"Python automation entry number {i}")
for e in archive._entries.values():
e.links = []
archive._save()
pairs = archive.resonance(threshold=0.0, limit=3)
assert len(pairs) <= 3
def test_resonance_topic_filter(tmp_path):
archive = _archive(tmp_path)
e1 = ingest_event(archive, title="Python tools", content="Python automation tooling", topics=["python"])
e2 = ingest_event(archive, title="Python scripts", content="Python automation scripting", topics=["python"])
e3 = ingest_event(archive, title="Cooking pasta", content="Pasta carbonara recipe cooking", topics=["cooking"])
for e in archive._entries.values():
e.links = []
archive._save()
pairs = archive.resonance(threshold=0.0, limit=20, topic="python")
for p in pairs:
a_topics = [t.lower() for t in p["entry_a"]["topics"]]
b_topics = [t.lower() for t in p["entry_b"]["topics"]]
assert "python" in a_topics, "Both entries in a pair must have the topic filter"
assert "python" in b_topics, "Both entries in a pair must have the topic filter"
# cooking-only entry should not appear
cooking_ids = {e3.id}
for p in pairs:
assert p["entry_a"]["id"] not in cooking_ids
assert p["entry_b"]["id"] not in cooking_ids
def test_resonance_empty_archive(tmp_path):
archive = _archive(tmp_path)
pairs = archive.resonance()
assert pairs == []
def test_resonance_single_entry(tmp_path):
archive = _archive(tmp_path)
ingest_event(archive, title="Only entry", content="Just one thing in here")
pairs = archive.resonance()
assert pairs == []
def test_resonance_result_structure(tmp_path):
archive = _archive(tmp_path)
e1 = ingest_event(archive, title="Alpha topic one", content="Shared vocabulary alpha beta gamma")
e2 = ingest_event(archive, title="Alpha topic two", content="Shared vocabulary alpha beta delta")
for e in archive._entries.values():
e.links = []
archive._save()
pairs = archive.resonance(threshold=0.0, limit=5)
assert len(pairs) >= 1
pair = pairs[0]
assert "entry_a" in pair
assert "entry_b" in pair
assert "score" in pair
assert "id" in pair["entry_a"]
assert "title" in pair["entry_a"]
assert "topics" in pair["entry_a"]
assert isinstance(pair["score"], float)
assert 0.0 <= pair["score"] <= 1.0
# Test resonance

View File

@@ -0,0 +1 @@
# Test snapshot

View File

@@ -48,24 +48,21 @@ async def broadcast_handler(websocket: websockets.WebSocketServerProtocol):
pass
# Broadcast to all OTHER clients
if not clients:
continue
disconnected = set()
# Create broadcast tasks for efficiency
tasks = []
# Create broadcast tasks paired with their target client
task_client_pairs = []
for client in clients:
if client != websocket and client.open:
tasks.append(asyncio.create_task(client.send(message)))
if tasks:
task = asyncio.create_task(client.send(message))
task_client_pairs.append((task, client))
if task_client_pairs:
tasks = [t for t, _ in task_client_pairs]
results = await asyncio.gather(*tasks, return_exceptions=True)
for i, result in enumerate(results):
for (task, client), result in zip(task_client_pairs, results):
if isinstance(result, Exception):
# Find the client that failed
target_client = [c for c in clients if c != websocket][i]
logger.error(f"Failed to send to a client {target_client.remote_address}: {result}")
disconnected.add(target_client)
logger.error(f"Failed to send to a client {client.remote_address}: {result}")
disconnected.add(client)
if disconnected:
clients.difference_update(disconnected)