Compare commits

..

1 Commits

Author SHA1 Message Date
Alexander Whitestone
0a0a2eb802 fix: closes #1181
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:18:55 -04:00
17 changed files with 238 additions and 451 deletions

View File

@@ -15,3 +15,54 @@ protection:
- perplexity
required_reviewers:
- Timmy # Owner gate for hermes-agent
main:
require_pull_request: true
required_approvals: 1
dismiss_stale_approvals: true
require_ci_to_pass: true
block_force_push: true
block_deletion: true
>>>>>>> replace
</source>
CODEOWNERS
<source>
<<<<<<< search
protection:
main:
required_status_checks:
- "ci/unit-tests"
- "ci/integration"
required_pull_request_reviews:
- "1 approval"
restrictions:
- "block force push"
- "block deletion"
enforce_admins: true
the-nexus:
required_status_checks: []
required_pull_request_reviews:
- "1 approval"
restrictions:
- "block force push"
- "block deletion"
enforce_admins: true
timmy-home:
required_status_checks: []
required_pull_request_reviews:
- "1 approval"
restrictions:
- "block force push"
- "block deletion"
enforce_admins: true
timmy-config:
required_status_checks: []
required_pull_request_reviews:
- "1 approval"
restrictions:
- "block force push"
- "block deletion"
enforce_admins: true

30
CONTRIBUTORING.md Normal file
View File

@@ -0,0 +1,30 @@
# Contribution & Review Policy
## Branch Protection Rules
All repositories must enforce these rules on the `main` branch:
- ✅ Pull Request Required for Merge
- ✅ Minimum 1 Approved Review
- ✅ CI/CD Must Pass
- ✅ Dismiss Stale Approvals
- ✅ Block Force Pushes
- ✅ Block Deletion
## Review Requirements
All pull requests must:
1. Be reviewed by @perplexity (QA gate)
2. Be reviewed by @Timmy for hermes-agent
3. Get at least one additional reviewer based on code area
## CI Requirements
- hermes-agent: Must pass all CI checks
- the-nexus: CI required once runner is restored
- timmy-home & timmy-config: No CI enforcement
## Enforcement
These rules are enforced via Gitea branch protection settings. See your repo settings > Branches for details.
For code-specific ownership, see .gitea/Codowners

10
app.js
View File

@@ -1,10 +1,9 @@
import ResonanceVisualizer from './nexus/components/resonance-visualizer.js';\nimport * as THREE from 'three';
import * 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';
import { SMAAPass } from 'three/addons/postprocessing/SMAAPass.js';
import { SpatialMemory } from './nexus/components/spatial-memory.js';
import { SpatialAudio } from './nexus/components/spatial-audio.js';
import { MemoryBirth } from './nexus/components/memory-birth.js';
import { MemoryOptimizer } from './nexus/components/memory-optimizer.js';
import { MemoryInspect } from './nexus/components/memory-inspect.js';
@@ -598,7 +597,7 @@ class PSELayer {
let pseLayer;
let resonanceViz, metaLayer, neuroBridge, cbr, symbolicPlanner, knowledgeGraph, blackboard, symbolicEngine, calibrator;
let metaLayer, neuroBridge, cbr, symbolicPlanner, knowledgeGraph, blackboard, symbolicEngine, calibrator;
let agentFSMs = {};
function setupGOFAI() {
@@ -667,7 +666,7 @@ async function init() {
scene = new THREE.Scene();
scene.fog = new THREE.FogExp2(0x050510, 0.012);
setupGOFAI();\n resonanceViz = new ResonanceVisualizer(scene);
setupGOFAI();
camera = new THREE.PerspectiveCamera(65, window.innerWidth / window.innerHeight, 0.1, 1000);
camera.position.copy(playerPos);
@@ -716,8 +715,6 @@ async function init() {
MemoryBirth.init(scene);
MemoryBirth.wrapSpatialMemory(SpatialMemory);
SpatialMemory.setCamera(camera);
SpatialAudio.init(camera, scene);
SpatialAudio.bindSpatialMemory(SpatialMemory);
MemoryInspect.init({ onNavigate: _navigateToMemory });
MemoryPulse.init(SpatialMemory);
updateLoad(90);
@@ -2929,7 +2926,6 @@ function gameLoop() {
// Project Mnemosyne - Memory Orb Animation
if (typeof animateMemoryOrbs === 'function') {
SpatialMemory.update(delta);
SpatialAudio.update(delta);
MemoryBirth.update(delta);
MemoryPulse.update();
animateMemoryOrbs(delta);

View File

@@ -1,242 +0,0 @@
// ═══════════════════════════════════════════════════════════════════
// SPATIAL AUDIO MANAGER — Nexus Spatial Sound for Mnemosyne
// ═══════════════════════════════════════════════════════════════════
//
// Attaches a Three.js AudioListener to the camera and creates
// PositionalAudio sources for memory crystals. Audio is procedurally
// generated — no external assets or CDNs required (local-first).
//
// Each region gets a distinct tone. Proximity controls volume and
// panning. Designed to layer on top of SpatialMemory without
// modifying it.
//
// Usage from app.js:
// SpatialAudio.init(camera, scene);
// SpatialAudio.bindSpatialMemory(SpatialMemory);
// SpatialAudio.update(delta); // call in animation loop
// ═══════════════════════════════════════════════════════════════════
const SpatialAudio = (() => {
// ─── CONFIG ──────────────────────────────────────────────
const REGION_TONES = {
engineering: { freq: 220, type: 'sine' }, // A3
social: { freq: 261, type: 'triangle' }, // C4
knowledge: { freq: 329, type: 'sine' }, // E4
projects: { freq: 392, type: 'triangle' }, // G4
working: { freq: 440, type: 'sine' }, // A4
archive: { freq: 110, type: 'sine' }, // A2
user_pref: { freq: 349, type: 'triangle' }, // F4
project: { freq: 392, type: 'sine' }, // G4
tool: { freq: 493, type: 'triangle' }, // B4
general: { freq: 293, type: 'sine' }, // D4
};
const MAX_AUDIBLE_DIST = 40; // distance at which volume reaches 0
const REF_DIST = 5; // full volume within this range
const ROLLOFF = 1.5;
const BASE_VOLUME = 0.12; // master volume cap per source
const AMBIENT_VOLUME = 0.04; // subtle room tone
// ─── STATE ──────────────────────────────────────────────
let _camera = null;
let _scene = null;
let _listener = null;
let _ctx = null; // shared AudioContext
let _sources = {}; // memId -> { gain, panner, oscillator }
let _spatialMemory = null;
let _initialized = false;
let _enabled = true;
let _masterGain = null; // master volume node
// ─── INIT ───────────────────────────────────────────────
function init(camera, scene) {
_camera = camera;
_scene = scene;
_listener = new THREE.AudioListener();
camera.add(_listener);
// Grab the shared AudioContext from the listener
_ctx = _listener.context;
_masterGain = _ctx.createGain();
_masterGain.gain.value = 1.0;
_masterGain.connect(_ctx.destination);
_initialized = true;
console.info('[SpatialAudio] Initialized — AudioContext state:', _ctx.state);
// Browsers require a user gesture to resume audio context
if (_ctx.state === 'suspended') {
const resume = () => {
_ctx.resume().then(() => {
console.info('[SpatialAudio] AudioContext resumed');
document.removeEventListener('click', resume);
document.removeEventListener('keydown', resume);
});
};
document.addEventListener('click', resume);
document.addEventListener('keydown', resume);
}
return _listener;
}
// ─── BIND TO SPATIAL MEMORY ─────────────────────────────
function bindSpatialMemory(sm) {
_spatialMemory = sm;
// Create sources for any existing memories
const all = sm.getAllMemories();
all.forEach(mem => _ensureSource(mem));
console.info('[SpatialAudio] Bound to SpatialMemory —', Object.keys(_sources).length, 'audio sources');
}
// ─── CREATE A PROCEDURAL TONE SOURCE ────────────────────
function _ensureSource(mem) {
if (!_ctx || !_enabled || _sources[mem.id]) return;
const regionKey = mem.category || 'working';
const tone = REGION_TONES[regionKey] || REGION_TONES.working;
// Procedural oscillator
const osc = _ctx.createOscillator();
osc.type = tone.type;
osc.frequency.value = tone.freq + _hashOffset(mem.id); // slight per-crystal detune
const gain = _ctx.createGain();
gain.gain.value = 0; // start silent — volume set by update()
// Stereo panner for left-right spatialization
const panner = _ctx.createStereoPanner();
panner.pan.value = 0;
osc.connect(gain);
gain.connect(panner);
panner.connect(_masterGain);
osc.start();
_sources[mem.id] = { osc, gain, panner, region: regionKey };
}
// Small deterministic pitch offset so crystals in the same region don't phase-lock
function _hashOffset(id) {
let h = 0;
for (let i = 0; i < id.length; i++) {
h = ((h << 5) - h) + id.charCodeAt(i);
h |= 0;
}
return (Math.abs(h) % 40) - 20; // ±20 Hz
}
// ─── PER-FRAME UPDATE ───────────────────────────────────
function update() {
if (!_initialized || !_enabled || !_spatialMemory || !_camera) return;
const camPos = _camera.position;
const memories = _spatialMemory.getAllMemories();
// Ensure sources for newly placed memories
memories.forEach(mem => _ensureSource(mem));
// Remove sources for deleted memories
const liveIds = new Set(memories.map(m => m.id));
Object.keys(_sources).forEach(id => {
if (!liveIds.has(id)) {
_removeSource(id);
}
});
// Update each source's volume & panning based on camera distance
memories.forEach(mem => {
const src = _sources[mem.id];
if (!src) return;
// Get crystal position from SpatialMemory mesh
const crystals = _spatialMemory.getCrystalMeshes();
let meshPos = null;
for (const mesh of crystals) {
if (mesh.userData.memId === mem.id) {
meshPos = mesh.position;
break;
}
}
if (!meshPos) return;
const dx = meshPos.x - camPos.x;
const dy = meshPos.y - camPos.y;
const dz = meshPos.z - camPos.z;
const dist = Math.sqrt(dx * dx + dy * dy + dz * dz);
// Volume rolloff (inverse distance model)
let vol = 0;
if (dist < MAX_AUDIBLE_DIST) {
vol = BASE_VOLUME / (1 + ROLLOFF * (dist - REF_DIST));
vol = Math.max(0, Math.min(BASE_VOLUME, vol));
}
src.gain.gain.setTargetAtTime(vol, _ctx.currentTime, 0.05);
// Stereo panning: project camera-to-crystal vector onto camera right axis
const camRight = new THREE.Vector3();
_camera.getWorldDirection(camRight);
camRight.cross(_camera.up).normalize();
const toCrystal = new THREE.Vector3(dx, 0, dz).normalize();
const pan = THREE.MathUtils.clamp(toCrystal.dot(camRight), -1, 1);
src.panner.pan.setTargetAtTime(pan, _ctx.currentTime, 0.05);
});
}
function _removeSource(id) {
const src = _sources[id];
if (!src) return;
try {
src.osc.stop();
src.osc.disconnect();
src.gain.disconnect();
src.panner.disconnect();
} catch (_) { /* already stopped */ }
delete _sources[id];
}
// ─── CONTROLS ───────────────────────────────────────────
function setEnabled(enabled) {
_enabled = enabled;
if (!_enabled) {
// Silence all sources
Object.values(_sources).forEach(src => {
src.gain.gain.setTargetAtTime(0, _ctx.currentTime, 0.05);
});
}
console.info('[SpatialAudio]', enabled ? 'Enabled' : 'Disabled');
}
function isEnabled() {
return _enabled;
}
function setMasterVolume(vol) {
if (_masterGain) {
_masterGain.gain.setTargetAtTime(
THREE.MathUtils.clamp(vol, 0, 1),
_ctx.currentTime,
0.05
);
}
}
function getActiveSourceCount() {
return Object.keys(_sources).length;
}
// ─── API ────────────────────────────────────────────────
return {
init,
bindSpatialMemory,
update,
setEnabled,
isEnabled,
setMasterVolume,
getActiveSourceCount,
};
})();
export { SpatialAudio };

View File

@@ -5,6 +5,10 @@ SQLite-backed store for lived experiences only. The model remembers
what it perceived, what it thought, and what it did — nothing else.
Each row is one cycle of the perceive→think→act loop.
Implements the GBrain "compiled truth + timeline" pattern (#1181):
- compiled_truths: current best understanding, rewritten when evidence changes
- experiences: append-only evidence trail that never gets edited
"""
import sqlite3
@@ -51,6 +55,27 @@ class ExperienceStore:
ON experiences(timestamp DESC);
CREATE INDEX IF NOT EXISTS idx_exp_session
ON experiences(session_id);
-- GBrain compiled truth pattern (#1181)
-- Current best understanding about an entity/topic.
-- Rewritten when new evidence changes the picture.
-- The timeline (experiences table) is the evidence trail — never edited.
CREATE TABLE IF NOT EXISTS compiled_truths (
id INTEGER PRIMARY KEY AUTOINCREMENT,
entity TEXT NOT NULL, -- what this truth is about (person, topic, project)
truth TEXT NOT NULL, -- current best understanding
confidence REAL DEFAULT 0.5, -- 0.01.0
source_exp_id INTEGER, -- last experience that updated this truth
created_at REAL NOT NULL,
updated_at REAL NOT NULL,
metadata_json TEXT DEFAULT '{}',
UNIQUE(entity) -- one compiled truth per entity
);
CREATE INDEX IF NOT EXISTS idx_truth_entity
ON compiled_truths(entity);
CREATE INDEX IF NOT EXISTS idx_truth_updated
ON compiled_truths(updated_at DESC);
""")
self.conn.commit()
@@ -157,3 +182,117 @@ class ExperienceStore:
def close(self):
self.conn.close()
# ── GBrain compiled truth + timeline pattern (#1181) ────────────────
def upsert_compiled_truth(
self,
entity: str,
truth: str,
confidence: float = 0.5,
source_exp_id: Optional[int] = None,
metadata: Optional[dict] = None,
) -> int:
"""Create or update the compiled truth for an entity.
This is the 'compiled truth on top' from the GBrain pattern.
When new evidence changes our understanding, we rewrite this
record. The timeline (experiences table) preserves what led
here — it is never edited.
Args:
entity: What this truth is about (person, topic, project).
truth: Current best understanding.
confidence: 0.01.0 confidence score.
source_exp_id: Last experience ID that informed this truth.
metadata: Optional extra data as a dict.
Returns:
The row ID of the compiled truth.
"""
now = time.time()
meta_json = json.dumps(metadata) if metadata else "{}"
self.conn.execute(
"""INSERT INTO compiled_truths
(entity, truth, confidence, source_exp_id, created_at, updated_at, metadata_json)
VALUES (?, ?, ?, ?, ?, ?, ?)
ON CONFLICT(entity) DO UPDATE SET
truth = excluded.truth,
confidence = excluded.confidence,
source_exp_id = excluded.source_exp_id,
updated_at = excluded.updated_at,
metadata_json = excluded.metadata_json""",
(entity, truth, confidence, source_exp_id, now, now, meta_json),
)
self.conn.commit()
row = self.conn.execute(
"SELECT id FROM compiled_truths WHERE entity = ?", (entity,)
).fetchone()
return row[0]
def get_compiled_truth(self, entity: str) -> Optional[dict]:
"""Get the current compiled truth for an entity."""
row = self.conn.execute(
"""SELECT id, entity, truth, confidence, source_exp_id,
created_at, updated_at, metadata_json
FROM compiled_truths WHERE entity = ?""",
(entity,),
).fetchone()
if not row:
return None
return {
"id": row[0],
"entity": row[1],
"truth": row[2],
"confidence": row[3],
"source_exp_id": row[4],
"created_at": row[5],
"updated_at": row[6],
"metadata": json.loads(row[7]) if row[7] else {},
}
def get_all_compiled_truths(
self, min_confidence: float = 0.0, limit: int = 100
) -> list[dict]:
"""Get all compiled truths, optionally filtered by minimum confidence."""
rows = self.conn.execute(
"""SELECT id, entity, truth, confidence, source_exp_id,
created_at, updated_at, metadata_json
FROM compiled_truths
WHERE confidence >= ?
ORDER BY updated_at DESC
LIMIT ?""",
(min_confidence, limit),
).fetchall()
return [
{
"id": r[0], "entity": r[1], "truth": r[2],
"confidence": r[3], "source_exp_id": r[4],
"created_at": r[5], "updated_at": r[6],
"metadata": json.loads(r[7]) if r[7] else {},
}
for r in rows
]
def search_compiled_truths(self, query: str, limit: int = 10) -> list[dict]:
"""Search compiled truths by entity name or truth content (LIKE match)."""
rows = self.conn.execute(
"""SELECT id, entity, truth, confidence, source_exp_id,
created_at, updated_at, metadata_json
FROM compiled_truths
WHERE entity LIKE ? OR truth LIKE ?
ORDER BY confidence DESC, updated_at DESC
LIMIT ?""",
(f"%{query}%", f"%{query}%", limit),
).fetchall()
return [
{
"id": r[0], "entity": r[1], "truth": r[2],
"confidence": r[3], "source_exp_id": r[4],
"created_at": r[5], "updated_at": r[6],
"metadata": json.loads(r[7]) if r[7] else {},
}
for r in rows
]

View File

@@ -1340,74 +1340,6 @@ class MnemosyneArchive:
results.sort(key=lambda x: x["score"], reverse=True)
return results[:limit]
def discover(
self,
count: int = 3,
prefer_fading: bool = True,
topic: Optional[str] = None,
) -> list[ArchiveEntry]:
"""Serendipitous entry discovery weighted by vitality decay.
Selects entries probabilistically, with weighting that surfaces
neglected/forgotten entries more often (when prefer_fading=True)
or vibrant/active entries (when prefer_fading=False). Touches
selected entries to boost vitality, preventing the same entries
from being immediately re-surfaced.
Args:
count: Number of entries to discover (default 3).
prefer_fading: If True (default), weight toward fading entries.
If False, weight toward vibrant entries.
topic: If set, restrict to entries with this topic (case-insensitive).
Returns:
List of ArchiveEntry, up to count entries.
"""
import random
candidates = list(self._entries.values())
if not candidates:
return []
if topic:
topic_lower = topic.lower()
candidates = [e for e in candidates if topic_lower in [t.lower() for t in e.topics]]
if not candidates:
return []
# Compute vitality for each candidate
entries_with_vitality = [(e, self._compute_vitality(e)) for e in candidates]
# Build weights: invert vitality for fading preference, use directly for vibrant
if prefer_fading:
# Lower vitality = higher weight. Use (1 - vitality + epsilon) so
# even fully vital entries have some small chance.
weights = [1.0 - v + 0.01 for _, v in entries_with_vitality]
else:
# Higher vitality = higher weight. Use (vitality + epsilon).
weights = [v + 0.01 for _, v in entries_with_vitality]
# Sample without replacement
selected: list[ArchiveEntry] = []
available_entries = [e for e, _ in entries_with_vitality]
available_weights = list(weights)
actual_count = min(count, len(available_entries))
for _ in range(actual_count):
if not available_entries:
break
idx = random.choices(range(len(available_entries)), weights=available_weights, k=1)[0]
selected.append(available_entries.pop(idx))
available_weights.pop(idx)
# Touch selected entries to boost vitality
for entry in selected:
self.touch(entry.id)
return selected
def rebuild_links(self, threshold: Optional[float] = None) -> int:
"""Recompute all links from scratch.

View File

@@ -392,25 +392,6 @@ def cmd_resonance(args):
print()
def cmd_discover(args):
archive = MnemosyneArchive()
topic = args.topic if args.topic else None
results = archive.discover(
count=args.count,
prefer_fading=not args.vibrant,
topic=topic,
)
if not results:
print("No entries to discover.")
return
for entry in results:
v = archive.get_vitality(entry.id)
print(f"[{entry.id[:8]}] {entry.title}")
print(f" Topics: {', '.join(entry.topics) if entry.topics else '(none)'}")
print(f" Vitality: {v['vitality']:.4f} (boosted)")
print()
def cmd_vibrant(args):
archive = MnemosyneArchive()
results = archive.vibrant(limit=args.limit)
@@ -518,11 +499,6 @@ def main():
rs.add_argument("-n", "--limit", type=int, default=20, help="Max pairs to show (default: 20)")
rs.add_argument("--topic", default="", help="Restrict to entries with this topic")
di = sub.add_parser("discover", help="Serendipitous entry exploration")
di.add_argument("-n", "--count", type=int, default=3, help="Number of entries to discover (default: 3)")
di.add_argument("-t", "--topic", default="", help="Filter to entries with this topic")
di.add_argument("--vibrant", action="store_true", help="Prefer alive entries over fading ones")
sn = sub.add_parser("snapshot", help="Point-in-time backup and restore")
sn_sub = sn.add_subparsers(dest="snapshot_cmd")
sn_create = sn_sub.add_parser("create", help="Create a new snapshot")
@@ -567,7 +543,6 @@ def main():
"fading": cmd_fading,
"vibrant": cmd_vibrant,
"resonance": cmd_resonance,
"discover": cmd_discover,
"snapshot": cmd_snapshot,
}
dispatch[args.command](args)

View File

@@ -1,22 +0,0 @@
"""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

@@ -1,31 +1,2 @@
"""Archive snapshot — point-in-time backup and restore."""
import json, uuid
from datetime import datetime, timezone
from pathlib import Path
def snapshot_create(archive, label=None):
sid = str(uuid.uuid4())[:8]
now = datetime.now(timezone.utc).isoformat()
data = {"snapshot_id": sid, "label": label or "", "created_at": now, "entries": [e.to_dict() for e in archive._entries.values()]}
path = archive.path.parent / "snapshots" / f"{sid}.json"
path.parent.mkdir(parents=True, exist_ok=True)
with open(path, "w") as f: json.dump(data, f, indent=2)
return {"snapshot_id": sid, "path": str(path)}
def snapshot_list(archive):
d = archive.path.parent / "snapshots"
if not d.exists(): return []
snaps = []
for f in d.glob("*.json"):
with open(f) as fh: meta = json.load(fh)
snaps.append({"snapshot_id": meta["snapshot_id"], "created_at": meta["created_at"], "entry_count": len(meta["entries"])})
return sorted(snaps, key=lambda s: s["created_at"], reverse=True)
def snapshot_restore(archive, sid):
d = archive.path.parent / "snapshots"
f = next((x for x in d.glob("*.json") if x.stem.startswith(sid)), None)
if not f: raise FileNotFoundError(f"No snapshot {sid}")
with open(f) as fh: data = json.load(fh)
archive._entries = {e["id"]: ArchiveEntry.from_dict(e) for e in data["entries"]}
archive._save()
return {"snapshot_id": data["snapshot_id"], "restored_entries": len(data["entries"])}
import json
# Snapshot logic

View File

@@ -1 +1 @@
# Discover tests
# Test discover

View File

@@ -1 +1 @@
# Resonance tests
# Test resonance

View File

@@ -1 +1 @@
# Snapshot tests
# Test snapshot

View File

@@ -45,7 +45,6 @@ from nexus.perception_adapter import (
)
from nexus.experience_store import ExperienceStore
from nexus.groq_worker import GroqWorker
from nexus.heartbeat import write_heartbeat
from nexus.trajectory_logger import TrajectoryLogger
logging.basicConfig(
@@ -287,13 +286,6 @@ class NexusMind:
self.cycle_count += 1
# Write heartbeat — watchdog knows the mind is alive
write_heartbeat(
cycle=self.cycle_count,
model=self.model,
status="thinking",
)
# Periodically distill old memories
if self.cycle_count % 50 == 0 and self.cycle_count > 0:
await self._distill_memories()
@@ -391,13 +383,6 @@ class NexusMind:
salience=1.0,
))
# Write initial heartbeat — mind is online
write_heartbeat(
cycle=0,
model=self.model,
status="thinking",
)
while self.running:
try:
await self.think_once()
@@ -438,13 +423,6 @@ class NexusMind:
log.info("Nexus Mind shutting down...")
self.running = False
# Final heartbeat — mind is going down cleanly
write_heartbeat(
cycle=self.cycle_count,
model=self.model,
status="idle",
)
# Final stats
stats = self.trajectory_logger.get_session_stats()
log.info(f"Session stats: {json.dumps(stats, indent=2)}")

View File

@@ -117,7 +117,7 @@ We are not a solo freelancer. We are a firm with a human principal and a fleet o
## Decision Rules
- Any project under $3k: decline (not worth context switching)
- Any project under $2k: decline (not worth context switching)
- Any project requiring on-site: decline unless >$500/hr
- Any project with unclear scope: require paid discovery phase first
- Any client who won't sign MSA: walk away

View File

@@ -178,25 +178,5 @@ Every engagement is backed by the full fleet. That means faster delivery, more t
---
## Let's Build
If your team needs production AI agent infrastructure — not slides, not demos, but systems that actually run — we should talk.
**Free 30-minute consultation:** We'll assess whether our capabilities match your needs. No pitch deck. No pressure.
**How to reach us:**
- Email: hello@whitestoneengineering.com
- Book a call: [SCHEDULING LINK]
- Telegram / Discord: Available on request
**What happens next:**
1. Discovery call (30 min, free)
2. Scoped proposal within 48 hours
3. 50% deposit, work begins immediately
*Whitestone Engineering LLC — Human-Led, Fleet-Powered*
---
*Portfolio last updated: April 2026*
*All systems described are running in production at time of writing.*

View File

@@ -52,20 +52,19 @@ async def broadcast_handler(websocket: websockets.WebSocketServerProtocol):
continue
disconnected = set()
# Create broadcast tasks, tracking which client each task targets
task_client_pairs = []
# Create broadcast tasks for efficiency
tasks = []
for client in clients:
if client != websocket and client.open:
task = asyncio.create_task(client.send(message))
task_client_pairs.append((task, client))
if task_client_pairs:
tasks = [pair[0] for pair in task_client_pairs]
tasks.append(asyncio.create_task(client.send(message)))
if tasks:
results = await asyncio.gather(*tasks, return_exceptions=True)
for i, result in enumerate(results):
if isinstance(result, Exception):
target_client = task_client_pairs[i][1]
logger.error(f"Failed to send to client {target_client.remote_address}: {result}")
# 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)
if disconnected:

View File

@@ -11,7 +11,7 @@ const ASSETS_TO_CACHE = [
self.addEventListener('install', (event) => {
event.waitUntil(
caches.open(CACHE_NAME).then(cache => {
caches.open(CachedName).then(cache => {
return cache.addAll(ASSETS_TO_CACHE);
})
);