diff --git a/app.js b/app.js
index 90ec33c0..ac5e752a 100644
--- a/app.js
+++ b/app.js
@@ -9,6 +9,7 @@ import { MemoryBirth } from './nexus/components/memory-birth.js';
import { MemoryOptimizer } from './nexus/components/memory-optimizer.js';
import { MemoryInspect } from './nexus/components/memory-inspect.js';
import { MemoryPulse } from './nexus/components/memory-pulse.js';
+import { ReasoningTrace } from './nexus/components/reasoning-trace.js';
// ═══════════════════════════════════════════
// NEXUS v1.1 — Portal System Update
@@ -758,6 +759,7 @@ async function init() {
SpatialAudio.bindSpatialMemory(SpatialMemory);
MemoryInspect.init({ onNavigate: _navigateToMemory });
MemoryPulse.init(SpatialMemory);
+ ReasoningTrace.init();
updateLoad(90);
loadSession();
diff --git a/index.html b/index.html
index 60999bad..0f487554 100644
--- a/index.html
+++ b/index.html
@@ -101,6 +101,19 @@
+
+
+
No active task
+
0 steps
+
+
diff --git a/nexus/components/reasoning-trace.js b/nexus/components/reasoning-trace.js
new file mode 100644
index 00000000..9a6881d3
--- /dev/null
+++ b/nexus/components/reasoning-trace.js
@@ -0,0 +1,451 @@
+// ═══════════════════════════════════════════════════
+// REASONING TRACE HUD COMPONENT
+// ═══════════════════════════════════════════════════
+//
+// Displays a real-time trace of the agent's reasoning
+// steps during complex task execution. Shows the chain
+// of thought, decision points, and confidence levels.
+//
+// Usage:
+// ReasoningTrace.init();
+// ReasoningTrace.addStep(step);
+// ReasoningTrace.clear();
+// ReasoningTrace.toggle();
+// ═══════════════════════════════════════════════════
+
+const ReasoningTrace = (() => {
+ // ── State ─────────────────────────────────────────
+ let _container = null;
+ let _content = null;
+ let _header = null;
+ let _steps = [];
+ let _maxSteps = 20;
+ let _isVisible = true;
+ let _currentTask = null;
+ let _stepCounter = 0;
+
+ // ── Config ────────────────────────────────────────
+ const STEP_TYPES = {
+ THINK: { icon: '💭', color: '#4af0c0', label: 'THINK' },
+ DECIDE: { icon: '⚖️', color: '#ffd700', label: 'DECIDE' },
+ RECALL: { icon: '🔍', color: '#7b5cff', label: 'RECALL' },
+ PLAN: { icon: '📋', color: '#ff8c42', label: 'PLAN' },
+ EXECUTE: { icon: '⚡', color: '#ff4466', label: 'EXECUTE' },
+ VERIFY: { icon: '✅', color: '#4af0c0', label: 'VERIFY' },
+ DOUBT: { icon: '❓', color: '#ff8c42', label: 'DOUBT' },
+ MEMORY: { icon: '💾', color: '#7b5cff', label: 'MEMORY' }
+ };
+
+ // ── Helpers ───────────────────────────────────────
+
+ function _escapeHtml(s) {
+ return String(s)
+ .replace(/&/g, '&')
+ .replace(//g, '>')
+ .replace(/"/g, '"')
+ .replace(/'/g, ''');
+ }
+
+ function _formatTimestamp(timestamp) {
+ const date = new Date(timestamp);
+ return date.toLocaleTimeString('en-US', {
+ hour12: false,
+ hour: '2-digit',
+ minute: '2-digit',
+ second: '2-digit'
+ });
+ }
+
+ function _getConfidenceBar(confidence) {
+ if (confidence === undefined || confidence === null) return '';
+ const percent = Math.max(0, Math.min(100, Math.round(confidence * 100)));
+ const bars = Math.round(percent / 10);
+ const filled = '█'.repeat(bars);
+ const empty = '░'.repeat(10 - bars);
+ return `${filled}${empty}`;
+ }
+
+ // ── DOM Setup ─────────────────────────────────────
+
+ function _createDOM() {
+ // Create container if it doesn't exist
+ if (_container) return;
+
+ _container = document.createElement('div');
+ _container.id = 'reasoning-trace';
+ _container.className = 'hud-panel reasoning-trace';
+
+ _header = document.createElement('div');
+ _header.className = 'panel-header';
+ _header.innerHTML = `🧠 REASONING TRACE`;
+
+ // Task indicator
+ const taskIndicator = document.createElement('div');
+ taskIndicator.className = 'trace-task';
+ taskIndicator.id = 'trace-task';
+ taskIndicator.textContent = 'No active task';
+
+ // Step counter
+ const stepCounter = document.createElement('div');
+ stepCounter.className = 'trace-counter';
+ stepCounter.id = 'trace-counter';
+ stepCounter.textContent = '0 steps';
+
+ // Controls
+ const controls = document.createElement('div');
+ controls.className = 'trace-controls';
+ controls.innerHTML = `
+
+
+
+ `;
+
+ // Header container
+ const headerContainer = document.createElement('div');
+ headerContainer.className = 'trace-header-container';
+ headerContainer.appendChild(_header);
+ headerContainer.appendChild(controls);
+
+ // Content area
+ _content = document.createElement('div');
+ _content.className = 'panel-content trace-content';
+ _content.id = 'reasoning-trace-content';
+
+ // Assemble
+ _container.appendChild(headerContainer);
+ _container.appendChild(taskIndicator);
+ _container.appendChild(stepCounter);
+ _container.appendChild(_content);
+
+ // Add to HUD
+ const hud = document.getElementById('hud');
+ if (hud) {
+ const gofaiHud = hud.querySelector('.gofai-hud');
+ if (gofaiHud) {
+ gofaiHud.appendChild(_container);
+ } else {
+ hud.appendChild(_container);
+ }
+ }
+
+ // Add event listeners
+ document.getElementById('trace-clear')?.addEventListener('click', clear);
+ document.getElementById('trace-toggle')?.addEventListener('click', toggle);
+ document.getElementById('trace-export')?.addEventListener('click', exportTrace);
+ }
+
+ // ── Rendering ─────────────────────────────────────
+
+ function _renderStep(step, index) {
+ const typeConfig = STEP_TYPES[step.type] || STEP_TYPES.THINK;
+ const timestamp = _formatTimestamp(step.timestamp);
+ const confidence = _getConfidenceBar(step.confidence);
+
+ const stepEl = document.createElement('div');
+ stepEl.className = `trace-step trace-step-${step.type.toLowerCase()}`;
+ stepEl.dataset.stepId = step.id;
+
+ // Step header
+ const header = document.createElement('div');
+ header.className = 'trace-step-header';
+ header.innerHTML = `
+ ${typeConfig.icon}
+ ${typeConfig.label}
+ ${timestamp}
+ ${confidence}
+ `;
+
+ // Step content
+ const content = document.createElement('div');
+ content.className = 'trace-step-content';
+
+ if (step.thought) {
+ const thought = document.createElement('div');
+ thought.className = 'step-thought';
+ thought.textContent = step.thought;
+ content.appendChild(thought);
+ }
+
+ if (step.reasoning) {
+ const reasoning = document.createElement('div');
+ reasoning.className = 'step-reasoning';
+ reasoning.textContent = step.reasoning;
+ content.appendChild(reasoning);
+ }
+
+ if (step.decision) {
+ const decision = document.createElement('div');
+ decision.className = 'step-decision';
+ decision.innerHTML = `Decision: ${_escapeHtml(step.decision)}`;
+ content.appendChild(decision);
+ }
+
+ if (step.alternatives && step.alternatives.length > 0) {
+ const alternatives = document.createElement('div');
+ alternatives.className = 'step-alternatives';
+ alternatives.innerHTML = `Alternatives: ${step.alternatives.map(a => _escapeHtml(a)).join(', ')}`;
+ content.appendChild(alternatives);
+ }
+
+ if (step.source) {
+ const source = document.createElement('div');
+ source.className = 'step-source';
+ source.innerHTML = `Source: ${_escapeHtml(step.source)}`;
+ content.appendChild(source);
+ }
+
+ stepEl.appendChild(header);
+ stepEl.appendChild(content);
+
+ return stepEl;
+ }
+
+ function _render() {
+ if (!_content) return;
+
+ // Clear content
+ _content.innerHTML = '';
+
+ // Update task indicator
+ const taskEl = document.getElementById('trace-task');
+ if (taskEl) {
+ taskEl.textContent = _currentTask || 'No active task';
+ taskEl.className = _currentTask ? 'trace-task active' : 'trace-task';
+ }
+
+ // Update step counter
+ const counterEl = document.getElementById('trace-counter');
+ if (counterEl) {
+ counterEl.textContent = `${_steps.length} step${_steps.length !== 1 ? 's' : ''}`;
+ }
+
+ // Render steps (newest first)
+ const sortedSteps = [..._steps].sort((a, b) => b.timestamp - a.timestamp);
+
+ for (let i = 0; i < sortedSteps.length; i++) {
+ const stepEl = _renderStep(sortedSteps[i], i);
+ _content.appendChild(stepEl);
+
+ // Add separator between steps
+ if (i < sortedSteps.length - 1) {
+ const separator = document.createElement('div');
+ separator.className = 'trace-separator';
+ _content.appendChild(separator);
+ }
+ }
+
+ // Show empty state if no steps
+ if (_steps.length === 0) {
+ const empty = document.createElement('div');
+ empty.className = 'trace-empty';
+ empty.innerHTML = `
+ 💭
+ No reasoning steps yet
+ Start a task to see the trace
+ `;
+ _content.appendChild(empty);
+ }
+ }
+
+ // ── Public API ────────────────────────────────────
+
+ function init() {
+ _createDOM();
+ _render();
+ console.info('[ReasoningTrace] Initialized');
+ }
+
+ /**
+ * Add a reasoning step to the trace.
+ * @param {Object} step - The reasoning step
+ * @param {string} step.type - Step type (THINK, DECIDE, RECALL, PLAN, EXECUTE, VERIFY, DOUBT, MEMORY)
+ * @param {string} step.thought - The main thought/content
+ * @param {string} [step.reasoning] - Detailed reasoning
+ * @param {string} [step.decision] - Decision made
+ * @param {string[]} [step.alternatives] - Alternative options considered
+ * @param {string} [step.source] - Source of information
+ * @param {number} [step.confidence] - Confidence level (0-1)
+ * @param {string} [step.taskId] - Associated task ID
+ */
+ function addStep(step) {
+ if (!step || !step.thought) return;
+
+ // Generate unique ID
+ const id = `step-${++_stepCounter}-${Date.now()}`;
+
+ // Create step object
+ const newStep = {
+ id,
+ timestamp: Date.now(),
+ type: step.type || 'THINK',
+ thought: step.thought,
+ reasoning: step.reasoning || null,
+ decision: step.decision || null,
+ alternatives: step.alternatives || null,
+ source: step.source || null,
+ confidence: step.confidence !== undefined ? Math.max(0, Math.min(1, step.confidence)) : null,
+ taskId: step.taskId || _currentTask
+ };
+
+ // Add to steps array
+ _steps.unshift(newStep);
+
+ // Limit number of steps
+ if (_steps.length > _maxSteps) {
+ _steps = _steps.slice(0, _maxSteps);
+ }
+
+ // Update task if provided
+ if (step.taskId && step.taskId !== _currentTask) {
+ setTask(step.taskId);
+ }
+
+ // Re-render
+ _render();
+
+ // Log to console for debugging
+ console.debug(`[ReasoningTrace] ${newStep.type}: ${newStep.thought}`);
+
+ return newStep.id;
+ }
+
+ /**
+ * Set the current task being traced.
+ * @param {string} taskId - Task identifier
+ */
+ function setTask(taskId) {
+ _currentTask = taskId;
+ _render();
+ console.info(`[ReasoningTrace] Task set: ${taskId}`);
+ }
+
+ /**
+ * Clear all steps from the trace.
+ */
+ function clear() {
+ _steps = [];
+ _stepCounter = 0;
+ _render();
+ console.info('[ReasoningTrace] Cleared');
+ }
+
+ /**
+ * Toggle the visibility of the trace panel.
+ */
+ function toggle() {
+ _isVisible = !_isVisible;
+ if (_container) {
+ _container.style.display = _isVisible ? 'block' : 'none';
+ }
+ console.info(`[ReasoningTrace] Visibility: ${_isVisible ? 'shown' : 'hidden'}`);
+ }
+
+ /**
+ * Export the trace as JSON.
+ * @returns {string} JSON string of the trace
+ */
+ function exportTrace() {
+ const exportData = {
+ task: _currentTask,
+ exportedAt: new Date().toISOString(),
+ steps: _steps.map(step => ({
+ type: step.type,
+ thought: step.thought,
+ reasoning: step.reasoning,
+ decision: step.decision,
+ alternatives: step.alternatives,
+ source: step.source,
+ confidence: step.confidence,
+ timestamp: new Date(step.timestamp).toISOString()
+ }))
+ };
+
+ const json = JSON.stringify(exportData, null, 2);
+
+ // Copy to clipboard
+ navigator.clipboard.writeText(json).then(() => {
+ console.info('[ReasoningTrace] Copied to clipboard');
+ // Show feedback
+ const btn = document.getElementById('trace-export');
+ if (btn) {
+ const original = btn.innerHTML;
+ btn.innerHTML = '✅';
+ setTimeout(() => { btn.innerHTML = original; }, 1000);
+ }
+ }).catch(err => {
+ console.error('[ReasoningTrace] Failed to copy:', err);
+ });
+
+ return json;
+ }
+
+ /**
+ * Get the current trace data.
+ * @returns {Object} Current trace state
+ */
+ function getTrace() {
+ return {
+ task: _currentTask,
+ steps: [..._steps],
+ stepCount: _steps.length,
+ isVisible: _isVisible
+ };
+ }
+
+ /**
+ * Get steps filtered by type.
+ * @param {string} type - Step type to filter by
+ * @returns {Array} Filtered steps
+ */
+ function getStepsByType(type) {
+ return _steps.filter(step => step.type === type);
+ }
+
+ /**
+ * Get steps for a specific task.
+ * @param {string} taskId - Task ID to filter by
+ * @returns {Array} Filtered steps
+ */
+ function getStepsByTask(taskId) {
+ return _steps.filter(step => step.taskId === taskId);
+ }
+
+ /**
+ * Mark the current task as complete.
+ * @param {string} [result] - Optional result description
+ */
+ function completeTask(result) {
+ if (_currentTask) {
+ addStep({
+ type: 'VERIFY',
+ thought: `Task completed: ${result || 'Success'}`,
+ taskId: _currentTask
+ });
+
+ // Clear current task after a delay
+ setTimeout(() => {
+ _currentTask = null;
+ _render();
+ }, 2000);
+ }
+ }
+
+ // ── Return Public API ─────────────────────────────
+
+ return {
+ init,
+ addStep,
+ setTask,
+ clear,
+ toggle,
+ exportTrace,
+ getTrace,
+ getStepsByType,
+ getStepsByTask,
+ completeTask,
+ STEP_TYPES
+ };
+})();
+
+export { ReasoningTrace };
\ No newline at end of file
diff --git a/style.css b/style.css
index ff8f34fb..961e1a9a 100644
--- a/style.css
+++ b/style.css
@@ -2685,3 +2685,252 @@ body.operator-mode #mode-label {
color: #ffd700;
}
+/* ═══ REASONING TRACE COMPONENT ═══ */
+
+.reasoning-trace {
+ width: 320px;
+ max-height: 400px;
+ display: flex;
+ flex-direction: column;
+}
+
+.trace-header-container {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ margin-bottom: 4px;
+}
+
+.trace-header-container .panel-header {
+ margin-bottom: 0;
+ border-bottom: none;
+ padding-bottom: 0;
+}
+
+.trace-icon {
+ margin-right: 4px;
+}
+
+.trace-controls {
+ display: flex;
+ gap: 4px;
+}
+
+.trace-btn {
+ background: rgba(74, 240, 192, 0.1);
+ border: 1px solid rgba(74, 240, 192, 0.2);
+ color: #4af0c0;
+ padding: 2px 6px;
+ font-size: 10px;
+ cursor: pointer;
+ border-radius: 2px;
+ transition: all 0.2s ease;
+}
+
+.trace-btn:hover {
+ background: rgba(74, 240, 192, 0.2);
+ border-color: #4af0c0;
+}
+
+.trace-task {
+ font-size: 9px;
+ color: #8899aa;
+ margin-bottom: 4px;
+ padding: 2px 6px;
+ background: rgba(0, 0, 0, 0.2);
+ border-radius: 2px;
+ font-family: 'JetBrains Mono', monospace;
+}
+
+.trace-task.active {
+ color: #4af0c0;
+ background: rgba(74, 240, 192, 0.1);
+ border-left: 2px solid #4af0c0;
+}
+
+.trace-counter {
+ font-size: 9px;
+ color: #667788;
+ margin-bottom: 6px;
+ font-family: 'JetBrains Mono', monospace;
+}
+
+.trace-content {
+ flex: 1;
+ overflow-y: auto;
+ max-height: 300px;
+}
+
+.trace-step {
+ margin-bottom: 8px;
+ padding: 6px;
+ background: rgba(0, 0, 0, 0.2);
+ border-radius: 3px;
+ border-left: 3px solid #4af0c0;
+ transition: all 0.2s ease;
+}
+
+.trace-step-think {
+ border-left-color: #4af0c0;
+}
+
+.trace-step-decide {
+ border-left-color: #ffd700;
+}
+
+.trace-step-recall {
+ border-left-color: #7b5cff;
+}
+
+.trace-step-plan {
+ border-left-color: #ff8c42;
+}
+
+.trace-step-execute {
+ border-left-color: #ff4466;
+}
+
+.trace-step-verify {
+ border-left-color: #4af0c0;
+}
+
+.trace-step-doubt {
+ border-left-color: #ff8c42;
+}
+
+.trace-step-memory {
+ border-left-color: #7b5cff;
+}
+
+.trace-step-header {
+ display: flex;
+ align-items: center;
+ gap: 6px;
+ margin-bottom: 4px;
+ font-size: 10px;
+}
+
+.step-icon {
+ font-size: 12px;
+}
+
+.step-type {
+ font-weight: 700;
+ letter-spacing: 0.5px;
+ font-family: 'JetBrains Mono', monospace;
+}
+
+.step-time {
+ color: #667788;
+ font-size: 9px;
+ margin-left: auto;
+ font-family: 'JetBrains Mono', monospace;
+}
+
+.confidence-bar {
+ font-family: 'JetBrains Mono', monospace;
+ font-size: 9px;
+ color: #4af0c0;
+ letter-spacing: -1px;
+ margin-left: 4px;
+}
+
+.trace-step-content {
+ font-size: 11px;
+ line-height: 1.4;
+ color: #d9f7ff;
+}
+
+.step-thought {
+ margin-bottom: 4px;
+ font-style: italic;
+ color: #e0f0ff;
+}
+
+.step-reasoning {
+ margin-bottom: 4px;
+ color: #aabbcc;
+ font-size: 10px;
+ padding-left: 8px;
+ border-left: 1px solid rgba(74, 240, 192, 0.2);
+}
+
+.step-decision {
+ margin-bottom: 4px;
+ color: #ffd700;
+ font-size: 10px;
+}
+
+.step-alternatives {
+ margin-bottom: 4px;
+ color: #8899aa;
+ font-size: 10px;
+}
+
+.step-source {
+ margin-bottom: 4px;
+ color: #7b5cff;
+ font-size: 10px;
+}
+
+.trace-separator {
+ height: 1px;
+ background: linear-gradient(90deg, transparent, rgba(74, 240, 192, 0.2), transparent);
+ margin: 6px 0;
+}
+
+.trace-empty {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+ padding: 20px;
+ color: #667788;
+ text-align: center;
+}
+
+.empty-icon {
+ font-size: 24px;
+ margin-bottom: 8px;
+ opacity: 0.5;
+}
+
+.empty-text {
+ font-size: 11px;
+ margin-bottom: 4px;
+ font-family: 'JetBrains Mono', monospace;
+}
+
+.empty-hint {
+ font-size: 9px;
+ color: #445566;
+ font-family: 'JetBrains Mono', monospace;
+}
+
+/* Animation for new steps */
+@keyframes trace-step-in {
+ from {
+ opacity: 0;
+ transform: translateY(-10px);
+ }
+ to {
+ opacity: 1;
+ transform: translateY(0);
+ }
+}
+
+.trace-step {
+ animation: trace-step-in 0.3s ease-out;
+}
+
+/* Responsive adjustments */
+@media (max-width: 768px) {
+ .reasoning-trace {
+ width: 280px;
+ }
+
+ .trace-content {
+ max-height: 200px;
+ }
+}
+