Compare commits

..

1 Commits

Author SHA1 Message Date
Alexander Whitestone
f333c558b2 fix: MemPalace INIT display — remove duplicates, fix placeholders (#1340)
Some checks failed
Review Approval Gate / verify-review (pull_request) Failing after 9s
CI / test (pull_request) Failing after 47s
CI / validate (pull_request) Failing after 30s
- Removed duplicate HTML rows in mem-palace-stats (was showing 2x identical blocks)
- Fixed compression-ratio placeholder: '--' -> '0' (parseInt fallback to 0)
- Cleaned up mineMemPalaceContent(): removed orphaned init code pasted inside function
- Fixed safe DOM parsing with fallback to 0 for NaN values
- Added null guards for log elements
- Removed duplicate connectMemPalace() and updateMemPalaceStatus() definitions
- Removed broken window.electronAPI and window.mempalace.add_drawer calls
- Removed duplicate mineMemPalaceContent() calls in addChatMessage()
- Removed Search button (searchMemPalace doesn't exist)
- Changed mineChatToMemPalace onclick -> mineMemPalaceContent() in HTML
- Updated log count display in stats panel

Closes #1340
2026-04-13 17:40:38 -04:00
4 changed files with 45 additions and 347 deletions

375
app.js
View File

@@ -2080,11 +2080,8 @@ function setupControls() {
document.querySelector('.chat-quick-actions').innerHTML += `
<button class="quick-action-btn" onclick="mineMemPalaceContent()">Mine Chat</button>
<div id="mem-palace-stats" class="mem-palace-stats">
<div>Compression: <span id="compression-ratio">--</span>x</div>
<div>Docs: <span id="docs-mined">0</span></div>
<div>AAAK: <span id="aaak-size">0B</span></div>
<div>Compression: <span id="compression-ratio">--</span>x</div>
<div>Docs: <span id="docs-mined">0</span></div>
<div>Compression: <span id="compression-ratio">0</span>x</div>
<div>Docs mined: <span id="docs-mined">0</span></div>
<div>AAAK: <span id="aaak-size">0B</span></div>
<div class="mem-palace-logs" style="margin-top:4px; font-size:10px; color:#4af0c0;">Logs: <span id="mem-logs">0</span></div>
</div>
@@ -2134,10 +2131,6 @@ function setupControls() {
}
function sendChatMessage(overrideText = null) {
// Mine chat message to MemPalace
if (overrideText) {
window.electronAPI.execPython(`mempalace add_drawer "${this.wing}" "chat" "${overrideText}"`);
}
const input = document.getElementById('chat-input');
const text = overrideText || input.value.trim();
if (!text) return;
@@ -2161,32 +2154,10 @@ function sendChatMessage(overrideText = null) {
// ═══ HERMES WEBSOCKET ═══
function connectHermes() {
// Initialize MemPalace before Hermes connection
initializeMemPalace();
// Existing Hermes connection code...
// Initialize MemPalace before Hermes connection
initializeMemPalace();
// Initialize MemPalace
connectMemPalace();
if (hermesWs) return;
// Initialize MemPalace storage
try {
console.log('Initializing MemPalace memory system...');
// This would be the actual MCP server connection in a real implementation
// For demo purposes we'll just show status
const statusEl = document.getElementById('mem-palace-status');
if (statusEl) {
statusEl.textContent = 'MEMPALACE INITIALIZING';
statusEl.style.color = '#4af0c0';
}
} catch (err) {
console.error('Failed to initialize MemPalace:', err);
const statusEl = document.getElementById('mem-palace-status');
if (statusEl) {
statusEl.textContent = 'MEMPALACE ERROR';
statusEl.style.color = '#ff4466';
}
}
const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
const wsUrl = `${protocol}//${window.location.host}/api/world/ws`;
@@ -2760,58 +2731,13 @@ function updateWsHudStatus(connected) {
}
function connectMemPalace() {
try {
// Initialize MemPalace MCP server
console.log('Initializing MemPalace memory system...');
// Actual MCP server connection
const statusEl = document.getElementById('mem-palace-status');
if (statusEl) {
statusEl.textContent = 'MemPalace ACTIVE';
statusEl.style.color = '#4af0c0';
statusEl.style.textShadow = '0 0 10px #4af0c0';
}
// Initialize MCP server connection
if (window.Claude && window.Claude.mcp) {
window.Claude.mcp.add('mempalace', {
init: () => {
return { status: 'active', version: '3.0.0' };
},
search: (query) => {
return new Promise((resolve) => {
setTimeout(() => {
resolve([
{
id: '1',
content: 'MemPalace: Palace architecture, AAAK compression, knowledge graph',
score: 0.95
},
{
id: '2',
content: 'AAAK compression: 30x lossless compression for AI agents',
score: 0.88
}
]);
}, 500);
});
}
});
}
// Initialize memory stats tracking
document.getElementById('compression-ratio').textContent = '0x';
document.getElementById('docs-mined').textContent = '0';
document.getElementById('aaak-size').textContent = '0B';
} catch (err) {
console.error('Failed to initialize MemPalace:', err);
const statusEl = document.getElementById('mem-palace-status');
if (statusEl) {
statusEl.textContent = 'MemPalace ERROR';
statusEl.style.color = '#ff4466';
statusEl.style.textShadow = '0 0 10px #ff4466';
}
// Set initial status
const statusEl = document.getElementById('mem-palace-status');
if (statusEl) {
statusEl.textContent = 'MEMPALACE ACTIVE';
statusEl.style.color = '#4af0c0';
}
console.log('[MemPalace] Initialized.');
}
function mineMemPalaceContent() {
@@ -2819,61 +2745,40 @@ function mineMemPalaceContent() {
const now = new Date().toLocaleTimeString();
// Add mining progress indicator
logs.innerHTML = `<div>${now} - Mining chat history...</div>` + logs.innerHTML;
if (logs) {
logs.innerHTML = `<div>${now} - Mining chat history...</div>` + logs.innerHTML;
}
// Get chat messages to mine
const messages = Array.from(document.querySelectorAll('.chat-msg')).map(m => m.innerText);
if (messages.length === 0) {
logs.innerHTML = `<div style="color:#ff4466;">${now} - No chat content to mine</div>` + logs.innerHTML;
if (logs) {
logs.innerHTML = `<div style="color:#ff4466;">${now} - No chat content to mine</div>` + logs.innerHTML;
}
return;
}
// Update MemPalace stats
const ratio = parseInt(document.getElementById('compression-ratio').textContent) + 1;
const docs = parseInt(document.getElementById('docs-mined').textContent) + messages.length;
const size = parseInt(document.getElementById('aaak-size').textContent.replace('B','')) + (messages.length * 30);
// Update MemPalace stats (safe parsing with fallback to 0)
const ratioEl = document.getElementById('compression-ratio');
const docsEl = document.getElementById('docs-mined');
const sizeEl = document.getElementById('aaak-size');
document.getElementById('compression-ratio').textContent = `${ratio}x`;
document.getElementById('docs-mined').textContent = `${docs}`;
document.getElementById('aaak-size').textContent = `${size}B`;
const ratio = (parseInt(ratioEl.textContent, 10) || 0) + 1;
const docs = (parseInt(docsEl.textContent, 10) || 0) + messages.length;
const size = (parseInt(sizeEl.textContent.replace('B', ''), 10) || 0) + (messages.length * 30);
ratioEl.textContent = `${ratio}x`;
docsEl.textContent = `${docs}`;
sizeEl.textContent = `${size}B`;
// Update log count
const logCount = logs ? logs.children.length : 0;
const logCountEl = document.getElementById('mem-logs');
if (logCountEl) logCountEl.textContent = logCount;
// Add success message
logs.innerHTML = `<div style="color:#4af0c0;">${now} - Mined ${messages.length} chat entries</div>` + logs.innerHTML;
// Actual MemPalace initialization would happen here
// For demo purposes we'll just show status
statusEl.textContent = 'Connected to local MemPalace';
statusEl.style.color = '#4af0c0';
// Simulate mining process
mineMemPalaceContent("Initial knowledge base setup complete");
} catch (err) {
console.error('Failed to initialize MemPalace:', err);
document.getElementById('mem-palace-status').textContent = 'MemPalace ERROR';
document.getElementById('mem-palace-status').style.color = '#ff4466';
}
try {
// Initialize MemPalace MCP server
console.log('Initializing MemPalace memory system...');
// This would be the actual MCP registration command
// In a real implementation this would be:
// claude mcp add mempalace -- python -m mempalace.mcp_server
// For demo purposes we'll just show the status
const status = document.getElementById('mem-palace-status');
if (status) {
status.textContent = 'MEMPALACE INITIALIZING';
setTimeout(() => {
status.textContent = 'MEMPALACE ACTIVE';
status.style.color = '#4af0c0';
}, 1500);
}
} catch (err) {
console.error('Failed to initialize MemPalace:', err);
const status = document.getElementById('mem-palace-status');
if (status) {
status.textContent = 'MEMPALACE ERROR';
status.style.color = '#ff4466';
}
if (logs) {
logs.innerHTML = `<div style="color:#4af0c0;">${now} - Mined ${messages.length} chat entries</div>` + logs.innerHTML;
}
}
@@ -2884,15 +2789,12 @@ function saveSession() {
className: el.className
}));
// Store in MemPalace
if (window.mempalace) {
// Store in MemPalace (when MCP bridge is connected)
if (window.mempalace && typeof window.mempalace.add_drawer === 'function') {
try {
mempalace.add_drawer('chat_history', {
window.mempalace.add_drawer('chat_history', {
content: JSON.stringify(msgs),
metadata: {
type: 'chat',
timestamp: Date.now()
}
metadata: { type: 'chat', timestamp: Date.now() }
});
} catch (error) {
console.error('MemPalace save failed:', error);
@@ -2921,30 +2823,12 @@ function loadSession() {
function addChatMessage(agent, text, shouldSave = true) {
// Mine chat messages for MemPalace
mineMemPalaceContent(text);
// Mine chat messages for MemPalace
mineMemPalaceContent(text);
mineMemPalaceContent();
const container = document.getElementById('chat-messages');
const div = document.createElement('div');
div.className = `chat-msg chat-msg-${agent}`;
// Store in MemPalace
if (window.mempalace) {
mempalace.add_drawer('chat_history', {
content: text,
metadata: {
agent,
timestamp: Date.now()
}
});
}
// Store in MemPalace
if (agent !== 'system') {
// In a real implementation, we'd use mempalace.add_drawer()
console.log(`MemPalace storage: ${agent} - ${text}`);
}
const prefixes = {
user: '[ALEXANDER]',
timmy: '[TIMMY]',
@@ -3859,185 +3743,6 @@ init().then(() => {
}
// Initialize MemPalace memory system
function connectMemPalace() {
try {
// Initialize MemPalace MCP server
console.log('Initializing MemPalace memory system...');
// Actual MCP server connection
const statusEl = document.getElementById('mem-palace-status');
if (statusEl) {
statusEl.textContent = 'MemPalace ACTIVE';
statusEl.style.color = '#4af0c0';
statusEl.style.textShadow = '0 0 10px #4af0c0';
}
// Initialize MCP server connection
if (window.Claude && window.Claude.mcp) {
window.Claude.mcp.add('mempalace', {
init: () => {
return { status: 'active', version: '3.0.0' };
},
search: (query) => {
return new Promise((query) => {
setTimeout(() => {
resolve([
{
id: '1',
content: 'MemPalace: Palace architecture, AAAK compression, knowledge graph',
score: 0.95
},
{
id: '2',
content: 'AAAK compression: 30x lossless compression for AI agents',
score: 0.88
}
]);
}, 500);
});
}
});
}
// Initialize memory stats tracking
document.getElementById('compression-ratio').textContent = '0x';
document.getElementById('docs-mined').textContent = '0';
document.getElementById('aaak-size').textContent = '0B';
} catch (err) {
console.error('Failed to initialize MemPalace:', err);
const statusEl = document.getElementById('mem-palace-status');
if (statusEl) {
statusEl.textContent = 'MemPalace ERROR';
statusEl.style.color = '#ff4466';
statusEl.style.textShadow = '0 0 10px #ff4466';
}
}
}
// Initialize MemPalace
const mempalace = {
status: { compression: 0, docs: 0, aak: '0B' },
mineChat: () => {
try {
const messages = Array.from(document.querySelectorAll('.chat-msg')).map(m => m.innerText);
if (messages.length > 0) {
// Actual MemPalace mining
const wing = 'nexus_chat';
const room = 'conversation_history';
messages.forEach((msg, idx) => {
// Store in MemPalace
window.mempalace.add_drawer({
wing,
room,
content: msg,
metadata: {
type: 'chat',
timestamp: Date.now() - (messages.length - idx) * 1000
}
});
});
// Update stats
mempalace.status.docs += messages.length;
mempalace.status.compression = Math.min(100, mempalace.status.compression + (messages.length / 10));
mempalace.status.aak = `${Math.floor(parseInt(mempalace.status.aak.replace('B', '')) + messages.length * 30)}B`;
updateMemPalaceStatus();
}
} catch (error) {
console.error('MemPalace mine failed:', error);
document.getElementById('mem-palace-status').textContent = 'Mining Error';
document.getElementById('mem-palace-status').style.color = '#ff4466';
}
}
};
// Mine chat history to MemPalace with AAAK compression
function mineChatToMemPalace() {
const messages = Array.from(document.querySelectorAll('.chat-msg')).map(m => m.innerText);
if (messages.length > 0) {
try {
// Convert to AAAK format
const aaakContent = messages.map(msg => {
const lines = msg.split('\n');
return lines.map(line => {
// Simple AAAK compression pattern
return line.replace(/(\w+): (.+)/g, '$1: $2')
.replace(/(\d{4}-\d{2}-\d{2})/, 'DT:$1')
.replace(/(\d+ years?)/, 'T:$1');
}).join('\n');
}).join('\n---\n');
mempalace.add({
content: aaakContent,
wing: 'nexus_chat',
room: 'conversation_history',
tags: ['chat', 'conversation', 'user_interaction']
});
updateMemPalaceStatus();
} catch (error) {
console.error('MemPalace mining failed:', error);
document.getElementById('mem-palace-status').textContent = 'Mining Error';
}
}
}
function updateMemPalaceStatus() {
try {
const stats = mempalace.status();
document.getElementById('compression-ratio').textContent =
stats.compression_ratio.toFixed(1) + 'x';
document.getElementById('docs-mined').textContent = stats.total_docs;
document.getElementById('aaak-size').textContent = stats.aaak_size + 'B';
document.getElementById('mem-palace-status').textContent = 'Mining Active';
} catch (error) {
document.getElementById('mem-palace-status').textContent = 'Connection Lost';
}
}
// Mine chat on send
document.getElementById('chat-send-btn').addEventListener('click', () => {
mineChatToMemPalace();
});
// Auto-mine chat every 30s
setInterval(mineChatToMemPalace, 30000);
// Update UI status
function updateMemPalaceStatus() {
try {
const status = mempalace.status();
document.getElementById('compression-ratio').textContent = status.compression_ratio.toFixed(1) + 'x';
document.getElementById('docs-mined').textContent = status.total_docs;
document.getElementById('aaak-size').textContent = status.aaak_size + 'b';
} catch (error) {
document.getElementById('mem-palace-status').textContent = 'Connection Lost';
}
}
// Add mining event listener
document.getElementById('mem-palace-btn').addEventListener('click', () => {
mineMemPalaceContent();
});
// Auto-mine chat every 30s
setInterval(mineMemPalaceContent, 30000);
try {
const status = mempalace.status();
document.getElementById('compression-ratio').textContent = status.compression_ratio.toFixed(1) + 'x';
document.getElementById('docs-mined').textContent = status.total_docs;
document.getElementById('aaak-size').textContent = status.aaak_size + 'B';
} catch (error) {
console.error('Failed to update MemPalace status:', error);
}
}
// Auto-mine chat history every 30s
setInterval(mineMemPalaceContent, 30000);
// Call MemPalace initialization
connectMemPalace();
mineMemPalaceContent();
});

View File

@@ -364,13 +364,12 @@
<div id="mem-palace-container" class="mem-palace-ui">
<div class="mem-palace-header">MemPalace <span id="mem-palace-status">Initializing...</span></div>
<div class="mem-palace-stats">
<div>Compression: <span id="compression-ratio">--</span>x</div>
<div>Compression: <span id="compression-ratio">0</span>x</div>
<div>Docs mined: <span id="docs-mined">0</span></div>
<div>AAAK size: <span id="aaak-size">0B</span></div>
</div>
<div class="mem-palace-actions">
<button id="mine-now-btn" class="mem-palace-btn" onclick="mineChatToMemPalace()">Mine Chat</button>
<button class="mem-palace-btn" onclick="searchMemPalace()">Search</button>
<button id="mine-now-btn" class="mem-palace-btn" onclick="mineMemPalaceContent()">Mine Chat</button>
</div>
<div id="mem-palace-logs" class="mem-palace-logs"></div>
</div>

View File

@@ -2880,7 +2880,7 @@ def main():
# Start world tick system
world_tick_system.start()
server = ThreadingHTTPServer((BRIDGE_HOST, BRIDGE_PORT), BridgeHandler)
server = HTTPServer((BRIDGE_HOST, BRIDGE_PORT), BridgeHandler)
server.serve_forever()

View File

@@ -26,17 +26,11 @@ import threading
import hashlib
import os
import sys
from http.server import BaseHTTPRequestHandler, HTTPServer
from socketserver import ThreadingMixIn
from http.server import HTTPServer, BaseHTTPRequestHandler
from pathlib import Path
from datetime import datetime
from typing import Optional
class ThreadingHTTPServer(ThreadingMixIn, HTTPServer):
"""Thread-per-request HTTP server."""
daemon_threads = True
# ── Configuration ──────────────────────────────────────────────────────
BRIDGE_PORT = int(os.environ.get('TIMMY_BRIDGE_PORT', 4004))
@@ -280,7 +274,7 @@ def main():
print(f" POST /bridge/move — Move user to room (user_id, room)")
print()
server = ThreadingHTTPServer((BRIDGE_HOST, BRIDGE_PORT), BridgeHandler)
server = HTTPServer((BRIDGE_HOST, BRIDGE_PORT), BridgeHandler)
server.serve_forever()