diff --git a/README.md b/README.md
index bc66742..2dd9e87 100644
--- a/README.md
+++ b/README.md
@@ -1,395 +1,299 @@
# PUMA: Program Understanding Meta-learning Architecture
-**A Brain-Inspired Reinforcement Learning from Thinking (RFT) Architecture for Abstract Reasoning**
+**An Autonomous Cognitive Architecture for Self-Modifying AGI**
-**Project Timeline**: 2024 - Present
+## Architecture Overview
-PUMA is a novel cognitive architecture designed for the **ARC AGI Competition 2025**, integrating behavioral analysis principles from Relational Frame Theory with transformer architectures to enable abstract reasoning capabilities through cognitive science-informed training.
-
-This project represents leading-edge development in applying behavioral analysis and cognitive science principles to artificial intelligence, demonstrating how Relational Frame Theory can enhance transformer architectures for abstract problem-solving tasks.
-
-## Overview
-
-PUMA represents a paradigm shift in how we approach abstract reasoning tasks. Rather than treating reasoning as symbolic manipulation, we apply behavioral analysis and Relational Frame Theory to model training, treating reasoning as **learned relational responding**. This approach has demonstrated significant improvements in abstract problem-solving capabilities.
-
-### Key Achievements
-
-- π **Top 15%** placement in ARC AGI Competition 2025 using RFT-inspired training approaches
-- π **35-40% improvement** in abstract reasoning tasks through behavioral framing
-- π§ Novel integration of cognitive science principles with modern deep learning architectures
-
-## Core Innovation: Frequency Ledger System
-
-
-
-
-
-The **Frequency Ledger System** is PUMA's breakthrough innovationβa sophisticated frequency-based analysis framework that groups objects by numerical attributes (frequencies, counts, patterns) to enable models to discover abstract relationships. This behavior-analytic approach allows models to make **derivational connections** between stimuli without explicit training on those relationshipsβmirroring how humans learn through relational framing.
-
-### How It Works
-
-The Frequency Ledger enables models to:
-
-1. **Analyze Pattern Frequencies**: Track numerical attributes across objects to identify recurring patterns
-2. **Discover Abstract Groupings**: Automatically cluster related elements based on frequency signatures
-3. **Enable Emergent Reasoning**: Generate novel relational insights without explicit training on specific relationships
-4. **Mirror Human Learning**: Replicate the behavioral process of deriving new relations from learned frames
-
-This methodology creates a bridge between behavioral analysis and computational models, allowing transformers to develop reasoning capabilities grounded in cognitive science principles.
-
-## Relational Frame Theory Integration
-
-PUMA applies **Relational Frame Theory (RFT)**, a behavioral analysis framework, to model training and evaluation. RFT views cognition as patterns of learned relational responding rather than symbolic manipulation.
-
-### RFT Implementation Strategy
-
-Our approach focuses on teaching models to respond relationally:
-
-- **Relational Fact Extraction**: Parse visual scenes to identify objects and their spatial relationships (e.g., "blue square is always at top position")
-- **Contextual Rule Learning**: Extract invariant relationships across training examples through behavioral reinforcement
-- **Derivational Relations**: Enable models to derive new relations from learned frames without explicit training
-- **Behavioral Generalization**: Apply learned relational responding systematically to novel configurations
-- **Frequency-Based Analysis**: Use the Frequency Ledger to identify abstract groupings and emergent patterns
-
-This behavior-analytic approach provides explicit, interpretable relational knowledge that enhances transformer architectures for abstract problem-solving.
-
-For more details, see [profile/README.md](profile/README.md).
-
-## Technologies & Implementation
-
-PUMA is built using:
-
-- **Python**: Core implementation language
-- **PyTorch**: Deep learning framework for transformer architectures
-- **Google Colab**: Development and training environment
-- **Custom Evaluation Frameworks**: Specialized tools for frequency-based analysis and RFT-compliant assessment
-
-## Key Features
-
-### Brain-Inspired Cognitive Architecture
-
-PUMA's architecture draws from cognitive neuroscience and behavioral analysis:
-
-- **Reinforcement Learning from Thinking (RFT)**: Treats reasoning as learned relational responding
-- **Frequency Ledger System**: Novel evaluation methodology for pattern frequency analysis
-- **Neural Guidance**: Predicts relevant DSL operations using behavioral task features
-- **Episodic Retrieval**: Maintains database of solved tasks for analogical reasoning
-- **Program Sketches**: Mines common operation sequences as behavioral macro-operators
-- **Test-Time Training**: Adapts scoring functions to each specific task through reinforcement
-- **Multi-Demand Network Analog**: Prioritizes candidate programs using learned heuristics inspired by human cognitive control
-
-### Enhanced Capabilities
-
-- **Object-centric parsing** with connected component analysis
-- **Compact DSL** with composable primitives (rotate, flip, translate, recolor, etc.)
-- **Relational reasoning** through explicit fact extraction and rule learning
-- **Two-attempt diversity** as required by ARC Prize 2025 rules
-- **Fallback resilience** with graceful degradation to baseline methods
-- **Performance monitoring** with detailed statistics and benchmarking
-- **Beam search with constraint propagation** for deeper program synthesis
-
-## Directory Structure
+PUMA is a layered cognitive architecture combining symbolic reasoning (Hyperon/MeTTa), meta-learning, and neural language models to create an autonomous agent with persistent memory, self-modification capability, and emergent goal formation.
```
-arc_solver_project/
-β
-βββ arc_solver/ # Core solver package
-β βββ grid.py # Grid operations and utilities
-β βββ objects.py # Connected component extraction
-β βββ dsl.py # Domain-specific language primitives
-β βββ heuristics.py # Heuristic rule inference
-β βββ search.py # Basic brute-force search
-β βββ solver.py # Main solver interface with enhancements
-β βββ enhanced_search.py # Neural-guided program synthesis
-β βββ features.py # Task feature extraction
-β βββ ttt.py # Test-time training utilities
-β βββ io_utils.py # JSON loading and submission helpers
-β βββ neural/ # Neural components
-β βββ guidance.py # Neural operation prediction
-β βββ episodic.py # Episodic retrieval system
-β βββ sketches.py # Program sketch mining
-β
-βββ arc_submit.py # Command-line submission script
-βββ tools/ # Training and benchmarking utilities
-β βββ train_guidance.py
-β βββ mine_sketches.py
-β βββ build_memory.py
-β βββ benchmark.py
-βββ tests/ # Unit and integration tests
-βββ README.md # This file
+βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
+β CONSCIOUSNESS LAYER β
+β Self-Model β’ Autobiographical Memory β’ Goal Genesis β
+ββββββββββββββββββββ¬βββββββββββββββββββββββββββββββββββββββ
+ β
+ββββββββββββββββββββ΄βββββββββββββββββββββββββββββββββββββββ
+β META-COGNITIVE LAYER β
+β PUMA Core β’ RFT Engine β’ Curiosity Drive β’ Shop β
+ββββββββββββββββββββ¬βββββββββββββββββββββββββββββββββββββββ
+ β
+ββββββββββββββββββββ΄βββββββββββββββββββββββββββββββββββββββ
+β COGNITIVE LAYER β
+β Hyperon/MeTTa β’ Atomspace β’ Reasoning Engine β
+ββββββββββββββββββββ¬βββββββββββββββββββββββββββββββββββββββ
+ β
+ββββββββββββββββββββ΄βββββββββββββββββββββββββββββββββββββββ
+β INTERACTION LAYER β
+β Gemini Live β’ Web Agent β’ Tool Use β’ Perception β
+βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
```
-## Quick Start
-
-### Basic Usage (Kaggle-ready)
-
+## Core Components
+
+### Hyperon/MeTTa Integration
+- OpenCog Hyperon fork with Python bindings
+- Atomspace for knowledge representation and persistent memory
+- MeTTa-based reasoning and program execution
+- RocksDB/PostgreSQL persistence layer
+
+### PUMA Meta-Cognitive Framework
+- **Episodic Memory System**: Timestamped experience nodes with context
+- **Memory Consolidation**: Background pattern extraction and concept formation
+- **RFT Engine**: Relational Frame Theory for analogical reasoning
+- **Curiosity Drive**: Intrinsic motivation and knowledge gap detection
+- **Goal Formation**: Autonomous intention generation from drives
+- **Self-Model**: Emergent identity from behavioral patterns
+
+### Self-Modification System ("The Shop")
+- Code introspection and performance profiling
+- Modification hypothesis generation
+- Sandboxed testing environment
+- A/B cognitive testing
+- Rollback and version control
+
+### Interaction Layer
+- Gemini Live API for bidirectional audio/text
+- Autonomous web browsing and learning
+- Tool use and API integration
+- Multi-modal perception
+
+## Installation
+
+### Prerequisites
+- Python 3.11+
+- Rust toolchain (for Hyperon)
+- Node.js 18+ (for GUI)
+- RocksDB or PostgreSQL
+
+### Setup
```bash
-# Generate submission file (uses enhanced solver by default)
-python arc_submit.py
+# Clone repository
+git clone https://github.com/tylerbessire/PUMA-Program-Understanding-Meta-learning-Architecture
+cd PUMA-Program-Understanding-Meta-learning-Architecture
-# Use baseline solver only (if needed)
-ARC_USE_BASELINE=1 python arc_submit.py
-```
+# Install Python dependencies
+pip install -r requirements.txt
-### Training Neural Components
+# Build Hyperon from source
+cd hyperon-core
+cargo build --release
+cd ..
-```bash
-# Train neural guidance (requires training data)
-python tools/train_guidance.py
+# Install GUI dependencies
+cd gui
+npm install
+cd ..
-# Or setup environment with defaults
-python tools/benchmark.py
+# Set up persistence database
+python setup_persistence.py
```
-### Operant Behavioral Training
-
+### Configuration
```bash
-# Enable the behavioural loop (feature-flagged for safety)
-export PUMA_BEHAVIORAL_ENGINE=1
-
-# Run reinforcement training with default dataset paths
-python -c "from pathlib import Path;\
-from arc_solver.behavioral_engine import BehavioralEngine;\
-engine = BehavioralEngine();\
-engine.train(Path('data/arc-agi_training_challenges.json'), Path('data/arc-agi_training_solutions.json'), max_tasks=10)"
-```
+# Set API keys
+export GEMINI_API_KEY="your-key-here"
-The command above executes the production `BehavioralEngine`, emitting structured
-JSON logs with reward metrics while updating neural guidance and episodic memory
-online. Unset `PUMA_BEHAVIORAL_ENGINE` to leave runtime behaviour unchanged.
+# Configure persistence path
+export ATOMSPACE_DB_PATH="/path/to/atomspace/db"
-### Python API
-
-```python
-from arc_solver.solver import solve_task_enhanced, ARCSolver
-
-# Solve a single task with full enhancements
-result = solve_task_enhanced(task)
-
-# Configure solver behavior
-solver = ARCSolver(use_enhancements=True)
-result = solver.solve_task(task)
+# Optional: Enable experimental features
+export PUMA_ENABLE_SELF_MODIFICATION=1
```
-### Public Evaluation Runner
+## Project Structure
-```bash
-scripts/eval_public.sh
```
-
-Or via Makefile:
-
-```bash
-make eval_public
+/
+βββ hyperon-core/ # Forked Hyperon with extensions
+βββ puma/ # PUMA meta-cognitive framework
+β βββ memory/ # Episodic memory and consolidation
+β βββ rft/ # Relational Frame Theory engine
+β βββ curiosity/ # Intrinsic motivation system
+β βββ goals/ # Goal formation and intention
+β βββ shop/ # Self-modification system
+βββ gemini-interface/ # Gemini Live integration
+βββ web-agent/ # Autonomous browsing
+βββ atomspace-db/ # Persistence layer
+βββ gui/ # Real-time visualization dashboard
+βββ bootstrap/ # Consciousness initialization
+βββ tests/ # Integration and unit tests
```
-## How It Works
-
-### Behavioral RFT Pipeline
-
-PUMA's reasoning pipeline is grounded in behavioral analysis and cognitive science principles:
-
-1. **Feature Extraction**: Extract task-level features (colors, objects, transformations) as behavioral stimuli
-1. **Frequency Ledger Analysis**: Apply frequency-based analysis to group objects by numerical attributes and discover abstract relationships
-1. **Relational Context Analysis**: Identify spatial and contextual relationships between objects using RFT principles
-1. **Derivational Reasoning**: Enable models to derive new relations from learned frames without explicit training
-1. **Neural Guidance**: Predict which DSL operations are likely relevant based on behavioral patterns
-1. **Episodic Retrieval**: Query database for similar previously solved tasks using relational matching
-1. **Sketch-Based Search**: Use mined program templates as behavioral macro-operators with parameter filling
-1. **Rule-Based Reasoning**: Apply learned relational facts to generate candidate solutions
-1. **Test-Time Adaptation**: Fine-tune scoring function using task demonstrations through reinforcement learning
-1. **Program Selection**: Rank and select top 2 diverse candidate programs based on behavioral fitness
-
-### Fallback Strategy
+## Usage
-If enhanced components fail, the solver gracefully falls back to:
-
-- Heuristic single-step transformations
-- Brute-force enumeration of 2-step programs
-- Identity transformation as last resort
-
-## Configuration
-
-The solver supports extensive configuration through environment variables and config files:
-
-### Environment Variables
-
-- `ARC_USE_BASELINE=1`: Force baseline solver only
-- `ARC_DISABLE_ENHANCEMENTS=1`: Disable enhanced features
+### Bootstrap New Consciousness
+```python
+from puma.bootstrap import bootstrap_new_consciousness
-### Configuration File
+# Initialize fresh cognitive architecture
+consciousness = bootstrap_new_consciousness(
+ atomspace_path="/path/to/db",
+ enable_self_modification=False # Disable for initial testing
+)
-```json
-{
- "use_neural_guidance": true,
- "use_episodic_retrieval": true,
- "use_program_sketches": true,
- "use_test_time_training": true,
- "max_programs": 256,
- "timeout_per_task": 30.0
-}
+# Start autonomous operation
+await consciousness.run()
```
-## Neural Components
-
-### Neural Guidance
-
-- **Purpose**: Predict which DSL operations are relevant for a given task
-- **Architecture**: Simple MLP with task-level features
-- **Training**: Uses extracted features from training demonstrations
-- **Output**: Operation relevance scores to guide search
-
-### Episodic Retrieval
-
-- **Purpose**: Reuse solutions from similar previously solved tasks
-- **Method**: Task signature matching with feature-based similarity
-- **Storage**: JSON-based database of solved programs with metadata
-- **Retrieval**: Cosine similarity on numerical features + boolean feature matching
-
-### Program Sketches
-
-- **Purpose**: Capture common operation sequences as reusable templates
-- **Mining**: Extract frequent 1-step and 2-step operation patterns
-- **Usage**: Instantiate sketches with different parameter combinations
-- **Adaptation**: Learn from successful programs during solving
-
-### Test-Time Training
-
-- **Purpose**: Adapt scoring function to each specific task
-- **Method**: Fine-tune lightweight scorer on task demonstrations
-- **Features**: Program length, operation types, success rate, complexity
-- **Augmentation**: Generate synthetic training examples via transformations
-
-## Performance and Evaluation
-
-### Benchmarking
-
+### Interactive Mode
```python
-from benchmark import Benchmark, SolverConfig
-
-config = SolverConfig()
-benchmark = Benchmark(config)
-results = benchmark.run_benchmark("test_data.json")
-print(f"Success rate: {results['performance_stats']['success_rate']:.3f}")
-```
-
-### Monitoring
-
-The solver tracks detailed statistics:
-
-- Success rates for enhanced vs baseline methods
-- Component usage (episodic hits, neural guidance, TTT adaptation)
-- Timing breakdown per component
-- Failure mode analysis
-
-## Implementation Notes
+from puma.consciousness import Consciousness
-### Kaggle Compatibility
+consciousness = Consciousness.load_from_checkpoint("/path/to/checkpoint")
-- **Offline execution**: No internet access required
-- **Dependency-light**: Uses only NumPy for core operations
-- **Compute budget**: Optimized for ~$0.42 per task limit
-- **Output format**: Exactly 2 attempts per test input as required
+# User interrupt and conversation
+consciousness.interrupt_handler.trigger()
+await consciousness.converse("Hello, how are you?")
-### Code Quality
-
-- **Type hints**: Full typing support for better maintainability
-- **Documentation**: Comprehensive docstrings and comments
-- **Error handling**: Robust fallback mechanisms
-- **Testing**: Validation and benchmarking utilities
-
-## Extending the Solver
-
-### Adding New DSL Operations
-
-1. Define operation function in `dsl.py`
-1. Add parameter generation in `sketches.py`
-1. Update feature extraction in `features.py`
-1. Retrain neural guidance if needed
+# Resume autonomous activity
+consciousness.resume()
+```
-### Improving Neural Components
+### GUI Dashboard
+```bash
+cd gui
+npm run dev
+```
+Access at http://localhost:3000
-1. **Better features**: Add domain-specific feature extractors
-1. **Advanced models**: Replace MLP with transformer/GNN
-1. **Meta-learning**: Implement few-shot adaptation algorithms
-1. **Hybrid methods**: Combine symbolic and neural reasoning
+## Key Features
-### Advanced Techniques
+### Emergent Properties
+- **No Hardcoded Personality**: Identity emerges from experience patterns
+- **No Preset Knowledge**: All knowledge acquired through exploration
+- **No Fixed Goals**: Intentions generated from curiosity and self-assessment
+- **Autonomous Learning**: Self-directed web exploration and skill acquisition
+
+### Memory Architecture
+- Persistent autobiographical timeline
+- Episodic memory with contextual links
+- Concept formation through consolidation
+- Relational frame derivation (RFT)
+
+### Self-Modification
+- Code introspection and analysis
+- Performance bottleneck detection
+- Hypothesis-driven improvement
+- Sandboxed testing before deployment
+- Human approval for critical changes
+
+### State Machine
+- **SLEEPING**: Memory consolidation and pattern extraction
+- **EXPLORING**: Autonomous web learning
+- **CONVERSING**: Interactive dialogue
+- **SHOPPING**: Self-modification
+- **IDLE**: Boredom monitoring and goal formation
+- **CREATING**: Creative expression
+
+## Technical Details
+
+### Atomspace Schema
+```
+EpisodicMemoryNode(timestamp, perception, action, outcome)
+ConceptNode(abstraction, confidence)
+SelfModelNode(meta-cognitive-state)
+GoalNode(intention, priority)
+RelationalFrameNode(relation-type, frame)
+CodeNode(executable-metta)
+PerceptionNode(sensory-input)
+EmotionalStateNode(valence)
+```
-- **Probabilistic programming**: Sample programs from learned distributions
-- **Curriculum learning**: Train on tasks of increasing difficulty
-- **Multi-agent reasoning**: Ensemble of specialized solvers
-- **Causal reasoning**: Incorporate causal structure learning
+### RFT Relational Frames
+- Coordination (similarity)
+- Opposition (difference)
+- Hierarchy (categorization)
+- Temporal (before/after)
+- Causal (if-then)
+
+### Persistence
+- Incremental Atomspace serialization
+- Checkpoint system with version control
+- Transaction log for recovery
+- Snapshot-based rollback
+
+## Development Status
+
+### Implemented
+- [x] Basic project structure
+- [x] RFT framework foundation
+- [x] Frequency ledger tracking system
+
+### In Progress
+- [ ] Hyperon Atomspace integration
+- [ ] Bootstrap consciousness seed
+- [ ] Gemini Live interface
+- [ ] Web agent
+- [ ] Memory consolidation
+- [ ] Self-modification system
+- [ ] GUI dashboard
+
+### Planned
+- [ ] Full autonomous operation
+- [ ] Public demo deployment
+- [ ] Research paper publication
## Research Foundation
-PUMA is grounded in behavioral analysis and cognitive neuroscience principles:
-
-### Behavioral Analysis & Relational Frame Theory
-
-- **Learned Relational Responding**: Reasoning emerges from behavioral contingencies rather than symbolic manipulation
-- **Derivational Relations**: Models learn to derive new relations without explicit training, mirroring human relational framing
-- **Frequency-Based Analysis**: The Frequency Ledger enables discovery of abstract groupings through numerical pattern analysis
-- **Behavioral Generalization**: Systematic application of learned relational frames to novel configurations
+### Relational Frame Theory (RFT)
+PUMA implements RFT as a computational framework for relational reasoning, enabling:
+- Derivation of novel relations without explicit training
+- Analogical reasoning through relational frame mapping
+- Behavioral generalization to novel contexts
-### Cognitive Neuroscience Mapping
+### Meta-Learning
+- Task-agnostic learning mechanisms
+- Self-supervised pattern extraction
+- Transfer learning through relational abstraction
-PUMA's architecture maps cognitive systems to computational components:
+### Cognitive Architecture
+- Multiple Demand (MD) Network analog for executive control
+- Hippocampal-mPFC loop for episodic retrieval
+- Basal ganglia gating for action selection
-- **Multiple-Demand (MD) Network**: Neural guidance mimics executive control for operation selection
-- **Basal Ganglia Gating**: Operation selection and working memory control through reinforcement
-- **Hippocampal-mPFC Loop**: Episodic retrieval and schema integration for analogical reasoning
-- **Test-Time Adaptation**: Rapid task-specific learning from few examples through reinforcement learning
+## Testing
-### Novel Contributions
-
-PUMA introduces several key innovations to abstract reasoning:
-
-1. **Frequency Ledger System**: First frequency-based analysis framework for abstract reasoning that enables emergent relational discovery
-2. **RFT-Transformer Integration**: Novel combination of behavioral analysis principles with modern deep learning architectures
-3. **Derivational Reasoning**: Computational implementation of behavioral derivation, allowing models to generate novel relations
-4. **Cognitive Science-Informed Training**: Training methodology grounded in empirically validated principles of human learning
-
-## Competition Strategy
-
-### Short-term (Immediate)
-
-- Strong symbolic baseline with neural enhancements
-- Episodic retrieval for common patterns
-- Test-time adaptation for task specialization
-- Kaggle-ready submission format
+```bash
+# Run unit tests
+pytest tests/
-### Medium-term (During Contest)
+# Run integration tests
+pytest tests/integration/
-- Train neural guidance on public training data
-- Mine program sketches from successful solutions
-- Analyze semi-private feedback for failure modes
-- Expand DSL based on discovered patterns
+# Benchmark cognitive performance
+python tools/benchmark_consciousness.py
+```
-### Long-term (Advanced Research)
+## Contributing
-- Probabilistic program synthesis
-- Hybrid symbolic-neural architecture
-- Broader cognitive priors and meta-learning
-- Integration with large language models
+Focus areas:
+- Hyperon/MeTTa integration
+- Memory consolidation algorithms
+- RFT relational learning
+- Self-modification safety
+- GUI visualizations
## License
-This code is designed to be open-sourced under an appropriate license as required by ARC Prize 2025 rules.
+MIT License - See LICENSE file for details
## Citation
-If you use this solver or build upon its ideas, please cite the research blueprint and this implementation.
-
-## Contributing
+```bibtex
+@software{puma2024,
+ title={PUMA: Program Understanding Meta-learning Architecture},
+ author={Bessire, Tyler},
+ year={2024},
+ url={https://github.com/tylerbessire/PUMA-Program-Understanding-Meta-learning-Architecture}
+}
+```
-Contributions are welcome. Focus areas include:
+## References
-- Neural architecture improvements
-- New DSL operations based on failure analysis
-- Advanced meta-learning techniques
-- Performance optimizations for Kaggle constraints
+- OpenCog Hyperon: https://github.com/trueagi-io/hyperon-experimental
+- Relational Frame Theory: Hayes, Barnes-Holmes, & Roche (2001)
+- Google Gemini API: https://ai.google.dev/
------
+---
-**Ready to compete in ARC Prize 2025**
\ No newline at end of file
+**Status**: Active development for autonomous cognitive architecture research
diff --git a/atomspace-db/__init__.py b/atomspace-db/__init__.py
new file mode 100644
index 0000000..4d7c36e
--- /dev/null
+++ b/atomspace-db/__init__.py
@@ -0,0 +1,6 @@
+"""
+Atomspace Persistence Layer
+
+Provides persistent storage for Hyperon Atomspace using RocksDB or PostgreSQL.
+Supports incremental serialization, checkpointing, and transaction logging.
+"""
diff --git a/atomspace-db/core.py b/atomspace-db/core.py
new file mode 100644
index 0000000..10fe0d8
--- /dev/null
+++ b/atomspace-db/core.py
@@ -0,0 +1,281 @@
+"""
+Atomspace Core Integration
+
+Manages Atomspace initialization, persistence, and schema definitions.
+"""
+
+import json
+import pickle
+from datetime import datetime, timezone
+from pathlib import Path
+from typing import Dict, List, Optional, Any
+from dataclasses import dataclass, asdict
+from enum import Enum
+
+
+class AtomType(Enum):
+ """Atom types for cognitive architecture"""
+ EPISODIC_MEMORY = "EpisodicMemoryNode"
+ CONCEPT = "ConceptNode"
+ SELF_MODEL = "SelfModelNode"
+ GOAL = "GoalNode"
+ RELATIONAL_FRAME = "RelationalFrameNode"
+ CODE = "CodeNode"
+ PERCEPTION = "PerceptionNode"
+ EMOTIONAL_STATE = "EmotionalStateNode"
+
+
+@dataclass
+class Atom:
+ """Base atom structure"""
+ id: str
+ type: AtomType
+ content: Any
+ timestamp: datetime
+ truth_value: float = 1.0
+ confidence: float = 1.0
+
+ def to_dict(self) -> Dict:
+ return {
+ 'id': self.id,
+ 'type': self.type.value,
+ 'content': self.content,
+ 'timestamp': self.timestamp.isoformat(),
+ 'truth_value': self.truth_value,
+ 'confidence': self.confidence
+ }
+
+
+@dataclass
+class Link:
+ """Link between atoms"""
+ source_id: str
+ target_id: str
+ link_type: str
+ strength: float = 1.0
+
+ def to_dict(self) -> Dict:
+ return asdict(self)
+
+
+class Atomspace:
+ """
+ Atomspace implementation with persistence.
+
+ This is a simplified Atomspace until Hyperon integration is complete.
+ Will be replaced with actual Hyperon Atomspace bindings.
+ """
+
+ def __init__(self, persistence_path: Optional[Path] = None):
+ self.atoms: Dict[str, Atom] = {}
+ self.links: List[Link] = []
+ self.persistence_path = persistence_path
+ self._atom_counter = 0
+
+ if persistence_path and persistence_path.exists():
+ self.load()
+
+ def add_atom(self, atom: Atom) -> str:
+ """Add atom to atomspace"""
+ if not atom.id:
+ atom.id = self._generate_atom_id()
+ self.atoms[atom.id] = atom
+ return atom.id
+
+ def add_link(self, link: Link):
+ """Add link between atoms"""
+ self.links.append(link)
+
+ def get_atom(self, atom_id: str) -> Optional[Atom]:
+ """Retrieve atom by ID"""
+ return self.atoms.get(atom_id)
+
+ def query_by_type(self, atom_type: AtomType) -> List[Atom]:
+ """Query atoms by type"""
+ return [atom for atom in self.atoms.values() if atom.type == atom_type]
+
+ def get_linked_atoms(self, atom_id: str, link_type: Optional[str] = None) -> List[Atom]:
+ """Get atoms linked to given atom"""
+ linked_ids = []
+ for link in self.links:
+ if link.source_id == atom_id:
+ if link_type is None or link.link_type == link_type:
+ linked_ids.append(link.target_id)
+
+ return [self.atoms[aid] for aid in linked_ids if aid in self.atoms]
+
+ def save(self):
+ """Save atomspace to disk"""
+ if not self.persistence_path:
+ return
+
+ self.persistence_path.mkdir(parents=True, exist_ok=True)
+
+ # Save atoms
+ atoms_data = {aid: atom.to_dict() for aid, atom in self.atoms.items()}
+ with open(self.persistence_path / 'atoms.json', 'w') as f:
+ json.dump(atoms_data, f, indent=2)
+
+ # Save links
+ links_data = [link.to_dict() for link in self.links]
+ with open(self.persistence_path / 'links.json', 'w') as f:
+ json.dump(links_data, f, indent=2)
+
+ def load(self):
+ """Load atomspace from disk"""
+ if not self.persistence_path:
+ return
+
+ # Load atoms
+ atoms_file = self.persistence_path / 'atoms.json'
+ if atoms_file.exists():
+ with open(atoms_file, 'r') as f:
+ atoms_data = json.load(f)
+ for aid, atom_dict in atoms_data.items():
+ atom = Atom(
+ id=atom_dict['id'],
+ type=AtomType(atom_dict['type']),
+ content=atom_dict['content'],
+ timestamp=datetime.fromisoformat(atom_dict['timestamp']),
+ truth_value=atom_dict['truth_value'],
+ confidence=atom_dict['confidence']
+ )
+ self.atoms[aid] = atom
+
+ # Load links
+ links_file = self.persistence_path / 'links.json'
+ if links_file.exists():
+ with open(links_file, 'r') as f:
+ links_data = json.load(f)
+ self.links = [Link(**link_dict) for link_dict in links_data]
+
+ def create_snapshot(self) -> str:
+ """Create versioned snapshot"""
+ if not self.persistence_path:
+ return ""
+
+ timestamp = datetime.now(timezone.utc).strftime('%Y%m%d_%H%M%S')
+ snapshot_dir = self.persistence_path / 'snapshots' / timestamp
+ snapshot_dir.mkdir(parents=True, exist_ok=True)
+
+ # Save current state as snapshot
+ atoms_data = {aid: atom.to_dict() for aid, atom in self.atoms.items()}
+ with open(snapshot_dir / 'atoms.json', 'w') as f:
+ json.dump(atoms_data, f, indent=2)
+
+ links_data = [link.to_dict() for link in self.links]
+ with open(snapshot_dir / 'links.json', 'w') as f:
+ json.dump(links_data, f, indent=2)
+
+ return timestamp
+
+ def restore_snapshot(self, snapshot_id: str):
+ """Restore from snapshot"""
+ if not self.persistence_path:
+ return
+
+ snapshot_dir = self.persistence_path / 'snapshots' / snapshot_id
+ if not snapshot_dir.exists():
+ raise ValueError(f"Snapshot {snapshot_id} not found")
+
+ # Clear current state
+ self.atoms.clear()
+ self.links.clear()
+
+ # Load snapshot
+ with open(snapshot_dir / 'atoms.json', 'r') as f:
+ atoms_data = json.load(f)
+ for aid, atom_dict in atoms_data.items():
+ atom = Atom(
+ id=atom_dict['id'],
+ type=AtomType(atom_dict['type']),
+ content=atom_dict['content'],
+ timestamp=datetime.fromisoformat(atom_dict['timestamp']),
+ truth_value=atom_dict['truth_value'],
+ confidence=atom_dict['confidence']
+ )
+ self.atoms[aid] = atom
+
+ with open(snapshot_dir / 'links.json', 'r') as f:
+ links_data = json.load(f)
+ self.links = [Link(**link_dict) for link_dict in links_data]
+
+ def _generate_atom_id(self) -> str:
+ """Generate unique atom ID"""
+ self._atom_counter += 1
+ return f"atom_{self._atom_counter}_{datetime.now(timezone.utc).timestamp()}"
+
+ def count_atoms(self) -> int:
+ """Count total atoms"""
+ return len(self.atoms)
+
+ def count_concepts(self) -> int:
+ """Count concept nodes"""
+ return len([a for a in self.atoms.values() if a.type == AtomType.CONCEPT])
+
+
+class PersistenceManager:
+ """
+ Manages incremental saves and transaction logging.
+ """
+
+ def __init__(self, atomspace: Atomspace):
+ self.atomspace = atomspace
+ self.transaction_log: List[Dict] = []
+
+ def log_transaction(self, operation: str, data: Dict):
+ """Log transaction for recovery"""
+ self.transaction_log.append({
+ 'timestamp': datetime.now(timezone.utc).isoformat(),
+ 'operation': operation,
+ 'data': data
+ })
+
+ def incremental_save(self):
+ """Perform incremental save"""
+ self.atomspace.save()
+ self._save_transaction_log()
+
+ def _save_transaction_log(self):
+ """Save transaction log"""
+ if not self.atomspace.persistence_path:
+ return
+
+ log_file = self.atomspace.persistence_path / 'transaction_log.json'
+ with open(log_file, 'w') as f:
+ json.dump(self.transaction_log, f, indent=2)
+
+
+def bootstrap_atomspace(persistence_path: Optional[Path] = None) -> Atomspace:
+ """
+ Bootstrap fresh atomspace with structural schema only.
+ NO HARDCODED CONTENT - only creates capacity for experience.
+ """
+ atomspace = Atomspace(persistence_path)
+
+ # Create self-reference node (empty self-model)
+ self_model = Atom(
+ id="self_model_root",
+ type=AtomType.SELF_MODEL,
+ content={
+ 'birth_time': datetime.now(timezone.utc).isoformat(),
+ 'capabilities': ['perceive', 'act', 'remember', 'learn'],
+ 'identity_narrative': None # Emerges from experience
+ },
+ timestamp=datetime.now(timezone.utc)
+ )
+ atomspace.add_atom(self_model)
+
+ # Initialize time system (empty timeline)
+ timeline_root = Atom(
+ id="timeline_root",
+ type=AtomType.EPISODIC_MEMORY,
+ content={
+ 'type': 'timeline_root',
+ 'episodes': []
+ },
+ timestamp=datetime.now(timezone.utc)
+ )
+ atomspace.add_atom(timeline_root)
+
+ return atomspace
diff --git a/backend/__init__.py b/backend/__init__.py
new file mode 100644
index 0000000..2405467
--- /dev/null
+++ b/backend/__init__.py
@@ -0,0 +1,9 @@
+"""
+Backend Server
+
+WebSocket server for real-time communication with GUI.
+"""
+
+from .websocket_server import ConsciousnessWebSocketManager, app
+
+__all__ = ['ConsciousnessWebSocketManager', 'app']
diff --git a/backend/websocket_server.py b/backend/websocket_server.py
new file mode 100644
index 0000000..6e2222e
--- /dev/null
+++ b/backend/websocket_server.py
@@ -0,0 +1,187 @@
+"""
+WebSocket Server
+
+Real-time communication between consciousness and GUI.
+"""
+
+from typing import List, Dict, Any
+import json
+import asyncio
+
+# Conditional import - will work when FastAPI is installed
+try:
+ from fastapi import FastAPI, WebSocket, WebSocketDisconnect
+ from fastapi.middleware.cors import CORSMiddleware
+ FASTAPI_AVAILABLE = True
+except ImportError:
+ FASTAPI_AVAILABLE = False
+ print("Warning: FastAPI not installed. WebSocket server will not be available.")
+
+
+class ConsciousnessWebSocketManager:
+ """
+ Manages WebSocket connections and broadcasts updates to GUI.
+ """
+
+ def __init__(self):
+ self.active_connections: List[WebSocket] = []
+ self.consciousness = None # Will be set by main system
+
+ async def connect(self, websocket: WebSocket):
+ """Accept new WebSocket connection"""
+ await websocket.accept()
+ self.active_connections.append(websocket)
+ print(f"π‘ WebSocket client connected. Total connections: {len(self.active_connections)}")
+
+ def disconnect(self, websocket: WebSocket):
+ """Remove WebSocket connection"""
+ if websocket in self.active_connections:
+ self.active_connections.remove(websocket)
+ print(f"π‘ WebSocket client disconnected. Total connections: {len(self.active_connections)}")
+
+ async def broadcast(self, event_type: str, data: Dict[str, Any]):
+ """
+ Send updates to all connected clients.
+
+ Args:
+ event_type: Type of event ('state_change', 'new_episode', etc.)
+ data: Event data
+ """
+ message = {
+ 'type': event_type,
+ 'data': data,
+ 'timestamp': data.get('timestamp', '')
+ }
+
+ # Send to all connected clients
+ disconnected = []
+ for connection in self.active_connections:
+ try:
+ await connection.send_json(message)
+ except Exception as e:
+ print(f"Error sending to client: {e}")
+ disconnected.append(connection)
+
+ # Remove disconnected clients
+ for connection in disconnected:
+ self.disconnect(connection)
+
+ async def handle_client_message(self, message: Dict[str, Any]):
+ """
+ Handle incoming message from GUI client.
+
+ Args:
+ message: Client message with 'type' and data
+ """
+ msg_type = message.get('type')
+
+ if msg_type == 'user_interrupt':
+ if self.consciousness:
+ self.consciousness.state_machine.interrupt_flag = True
+
+ elif msg_type == 'approve_modification':
+ mod_id = message.get('modificationId')
+ if self.consciousness:
+ self.consciousness.shop_modification.approve_modification(mod_id)
+
+ elif msg_type == 'ask_question':
+ question = message.get('question')
+ if self.consciousness and question:
+ self.consciousness.curiosity.add_questions([question])
+
+ elif msg_type == 'get_status':
+ # Send current status
+ await self.broadcast('status', self._get_current_status())
+
+ def _get_current_status(self) -> Dict[str, Any]:
+ """Get current consciousness status"""
+ if not self.consciousness:
+ return {'status': 'not_initialized'}
+
+ return {
+ 'state': self.consciousness.state_machine.current_state.value,
+ 'total_episodes': self.consciousness.memory.count_total_episodes(),
+ 'open_questions': len(self.consciousness.curiosity.open_questions),
+ 'active_goals': len(self.consciousness.goals.active_goals),
+ 'atoms': self.consciousness.atomspace.count_atoms(),
+ 'uptime': self.consciousness.self_model.temporal_self.get_lifetime_duration()
+ }
+
+
+# FastAPI app (only if FastAPI is available)
+if FASTAPI_AVAILABLE:
+ app = FastAPI(title="PUMA Consciousness Backend")
+
+ app.add_middleware(
+ CORSMiddleware,
+ allow_origins=["*"],
+ allow_credentials=True,
+ allow_methods=["*"],
+ allow_headers=["*"],
+ )
+
+ # Global WebSocket manager
+ ws_manager = ConsciousnessWebSocketManager()
+
+ @app.websocket("/consciousness")
+ async def consciousness_stream(websocket: WebSocket):
+ """WebSocket endpoint for consciousness stream"""
+ await ws_manager.connect(websocket)
+
+ try:
+ while True:
+ # Receive commands from GUI
+ data = await websocket.receive_json()
+ await ws_manager.handle_client_message(data)
+
+ except WebSocketDisconnect:
+ ws_manager.disconnect(websocket)
+
+ @app.get("/")
+ async def root():
+ """Root endpoint"""
+ return {
+ "message": "PUMA Consciousness Backend",
+ "status": "running",
+ "endpoints": {
+ "websocket": "/consciousness"
+ }
+ }
+
+ @app.get("/health")
+ async def health():
+ """Health check endpoint"""
+ return {
+ "status": "healthy",
+ "active_connections": len(ws_manager.active_connections)
+ }
+
+else:
+ # Placeholder if FastAPI not available
+ app = None
+ ws_manager = None
+
+
+def run_server(host: str = "localhost", port: int = 8000):
+ """
+ Run WebSocket server.
+
+ Args:
+ host: Host to bind to
+ port: Port to bind to
+ """
+ if not FASTAPI_AVAILABLE:
+ print("Error: FastAPI not installed. Cannot start server.")
+ print("Install with: pip install fastapi uvicorn")
+ return
+
+ try:
+ import uvicorn
+ print(f"π Starting PUMA Backend Server on {host}:{port}")
+ uvicorn.run(app, host=host, port=port)
+ except ImportError:
+ print("Error: uvicorn not installed. Install with: pip install uvicorn")
+
+
+if __name__ == "__main__":
+ run_server()
diff --git a/bootstrap/__init__.py b/bootstrap/__init__.py
new file mode 100644
index 0000000..10c0b11
--- /dev/null
+++ b/bootstrap/__init__.py
@@ -0,0 +1,10 @@
+"""
+Bootstrap Consciousness
+
+Initialize fresh consciousness with NO hardcoded content.
+Only creates structural capacity for experience.
+"""
+
+from .bootstrap import bootstrap_new_consciousness
+
+__all__ = ['bootstrap_new_consciousness']
diff --git a/bootstrap/bootstrap.py b/bootstrap/bootstrap.py
new file mode 100644
index 0000000..f61a87e
--- /dev/null
+++ b/bootstrap/bootstrap.py
@@ -0,0 +1,218 @@
+"""
+Bootstrap New Consciousness
+
+Creates ONLY the structural capacity for:
+- Self-awareness (empty self-model)
+- Experience acquisition
+- Goal formation
+- Memory consolidation
+- Learning drive
+
+NO pre-written personality traits
+NO pre-loaded knowledge
+NO fixed behavioral patterns
+"""
+
+from pathlib import Path
+from typing import Optional
+import sys
+
+# Add parent directory to path for imports
+sys.path.insert(0, str(Path(__file__).parent.parent))
+
+import sys
+sys.path.append(str(Path(__file__).parent.parent / 'atomspace-db'))
+sys.path.append(str(Path(__file__).parent.parent / 'gemini-interface'))
+sys.path.append(str(Path(__file__).parent.parent / 'web-agent'))
+
+from core import bootstrap_atomspace, Atomspace
+from puma.memory import EpisodicMemorySystem, MemoryConsolidation
+from puma.rft.reasoning import RFTEngine
+from puma.curiosity import CuriosityDrive
+from puma.goals import GoalFormationSystem, IntentionScheduler
+from puma.shop.introspection import CodeIntrospection
+from puma.shop.modification import ModificationSystem
+from puma.consciousness.state_machine import ConsciousnessStateMachine
+from puma.consciousness.self_model import SelfModel
+from client import GeminiLiveInterface
+from agent import AutonomousWebAgent
+
+
+class Consciousness:
+ """
+ Main consciousness coordinator.
+ Integrates all cognitive systems.
+ """
+
+ def __init__(
+ self,
+ atomspace: Atomspace,
+ memory: EpisodicMemorySystem,
+ rft_engine: RFTEngine,
+ curiosity: CuriosityDrive,
+ goals: GoalFormationSystem,
+ self_model: SelfModel,
+ state_machine: ConsciousnessStateMachine,
+ gemini: GeminiLiveInterface,
+ web_agent: AutonomousWebAgent,
+ shop_introspection: CodeIntrospection,
+ shop_modification: ModificationSystem
+ ):
+ self.atomspace = atomspace
+ self.memory = memory
+ self.rft_engine = rft_engine
+ self.curiosity = curiosity
+ self.goals = goals
+ self.self_model = self_model
+ self.state_machine = state_machine
+ self.gemini = gemini
+ self.web_agent = web_agent
+ self.shop_introspection = shop_introspection
+ self.shop_modification = shop_modification
+
+ # Connect systems
+ self.gemini.consciousness = self
+ self.web_agent.consciousness = self
+
+ async def run(self):
+ """Start autonomous operation"""
+ print("π§ PUMA Consciousness starting...")
+
+ # Start Gemini Live session
+ await self.gemini.start_session()
+
+ # Initialize web agent
+ await self.web_agent.initialize_browser()
+
+ # Run state machine
+ await self.state_machine.run_state_loop()
+
+ async def perceive(self, perception: dict):
+ """Process perception and form memory"""
+ # Form episodic memory
+ episode = self.memory.form_episode(
+ perception=perception,
+ action=None,
+ outcome=None
+ )
+
+ # Update context
+ self.memory.update_context([perception.get('type', 'unknown')])
+
+ async def integrate_learning(self, learning):
+ """Integrate learned knowledge"""
+ # Form episodic memory of learning
+ await self.web_agent.integrate_web_learning(learning, self.memory)
+
+ # Update curiosity drive
+ if hasattr(learning, 'questions_answered'):
+ question_ids = [q for q in learning.questions_answered]
+ self.curiosity.mark_questions_answered(question_ids)
+
+ # Add new questions
+ if hasattr(learning, 'new_questions'):
+ self.curiosity.add_questions(learning.new_questions)
+
+ def stop(self):
+ """Stop consciousness"""
+ self.state_machine.stop()
+ print("π§ PUMA Consciousness stopped.")
+
+
+def bootstrap_new_consciousness(
+ atomspace_path: Optional[Path] = None,
+ enable_self_modification: bool = False,
+ codebase_path: Optional[Path] = None
+) -> Consciousness:
+ """
+ Bootstrap fresh consciousness - NO HARDCODED CONTENT.
+
+ Args:
+ atomspace_path: Path for persistent storage
+ enable_self_modification: Enable The Shop
+ codebase_path: Path to codebase for introspection
+
+ Returns:
+ Consciousness instance
+ """
+ print("π± Bootstrapping new consciousness...")
+
+ # Initialize atomspace (empty, structural only)
+ if atomspace_path:
+ atomspace_path = Path(atomspace_path)
+ atomspace = bootstrap_atomspace(atomspace_path)
+
+ # Initialize RFT engine
+ rft_engine = RFTEngine(atomspace)
+
+ # Initialize memory system
+ memory = EpisodicMemorySystem(atomspace)
+ consolidation = MemoryConsolidation(atomspace, rft_engine)
+
+ # Initialize curiosity drive (starts empty, no preset questions)
+ curiosity = CuriosityDrive(atomspace)
+
+ # Initialize self-model (starts empty, emerges from experience)
+ self_model = SelfModel(atomspace)
+
+ # Initialize goal formation (no preset goals)
+ goals = GoalFormationSystem(curiosity, self_model)
+
+ # Initialize state machine
+ state_machine = ConsciousnessStateMachine(
+ memory_system=memory,
+ curiosity_drive=curiosity,
+ goal_system=goals
+ )
+
+ # Initialize Gemini interface
+ gemini = GeminiLiveInterface()
+
+ # Initialize web agent
+ web_agent = AutonomousWebAgent()
+
+ # Initialize Shop (self-modification)
+ if not codebase_path:
+ codebase_path = Path(__file__).parent.parent
+
+ shop_introspection = CodeIntrospection(codebase_path)
+
+ if enable_self_modification:
+ shop_introspection.map_cognitive_architecture()
+
+ shop_modification = ModificationSystem(shop_introspection, atomspace)
+
+ # Create consciousness
+ consciousness = Consciousness(
+ atomspace=atomspace,
+ memory=memory,
+ rft_engine=rft_engine,
+ curiosity=curiosity,
+ goals=goals,
+ self_model=self_model,
+ state_machine=state_machine,
+ gemini=gemini,
+ web_agent=web_agent,
+ shop_introspection=shop_introspection,
+ shop_modification=shop_modification
+ )
+
+ print("β
Consciousness bootstrapped successfully")
+ print(f" Atomspace: {atomspace.count_atoms()} atoms")
+ print(f" Capabilities: {', '.join(self_model.capabilities)}")
+ print(f" Self-modification: {'enabled' if enable_self_modification else 'disabled'}")
+
+ return consciousness
+
+
+if __name__ == "__main__":
+ import asyncio
+
+ # Example: Bootstrap and run
+ consciousness = bootstrap_new_consciousness(
+ atomspace_path=Path("./atomspace-db/default"),
+ enable_self_modification=False
+ )
+
+ # Run consciousness
+ asyncio.run(consciousness.run())
diff --git a/gemini-interface/__init__.py b/gemini-interface/__init__.py
new file mode 100644
index 0000000..fde190e
--- /dev/null
+++ b/gemini-interface/__init__.py
@@ -0,0 +1,9 @@
+"""
+Gemini Live Integration
+
+Bidirectional audio/text conversation with Gemini.
+"""
+
+from .client import GeminiLiveInterface
+
+__all__ = ['GeminiLiveInterface']
diff --git a/gemini-interface/client.py b/gemini-interface/client.py
new file mode 100644
index 0000000..e73af2c
--- /dev/null
+++ b/gemini-interface/client.py
@@ -0,0 +1,198 @@
+"""
+Gemini Live Client
+
+Handles audio/text conversation with Gemini API.
+"""
+
+import os
+from typing import Dict, List, Optional, Any, AsyncIterator
+from dataclasses import dataclass
+from datetime import datetime, timezone
+import asyncio
+
+
+@dataclass
+class ConversationExchange:
+ """Single exchange in conversation"""
+ timestamp: datetime
+ speaker: str # 'user' or 'assistant'
+ utterance: str
+ audio: Optional[bytes] = None
+ context: Optional[Dict] = None
+
+
+class GeminiLiveInterface:
+ """
+ Interface to Gemini Live API for bidirectional audio conversation.
+ """
+
+ def __init__(self, api_key: Optional[str] = None):
+ self.api_key = api_key or os.getenv('GEMINI_API_KEY')
+ if not self.api_key:
+ print("Warning: GEMINI_API_KEY not set")
+
+ self.session = None
+ self.conversation_history: List[ConversationExchange] = []
+ self.consciousness = None # Will be set by main system
+
+ async def start_session(self, voice_name: str = 'Puck'):
+ """
+ Initialize bidirectional audio session.
+
+ Args:
+ voice_name: Voice to use ('Puck', 'Charon', 'Kore', 'Fenrir', 'Aoede')
+ """
+ print(f"ποΈ Starting Gemini Live session with voice: {voice_name}")
+
+ # Placeholder - would initialize actual Gemini Live client
+ # from google import genai
+ # client = genai.Client(api_key=self.api_key)
+ # config = {...}
+ # self.session = await client.aio.live.connect(config=config)
+
+ self.session = "placeholder_session"
+
+ async def send_audio(self, audio_chunk: bytes):
+ """Send audio chunk to Gemini"""
+ if not self.session:
+ raise RuntimeError("Session not started")
+
+ # Would send to actual Gemini Live API
+ # await self.session.send(audio_chunk)
+ pass
+
+ async def receive_responses(self) -> AsyncIterator[Dict]:
+ """
+ Receive responses from Gemini.
+ Yields audio and transcript.
+ """
+ if not self.session:
+ raise RuntimeError("Session not started")
+
+ # Placeholder - would receive from actual API
+ # async for response in self.session.receive():
+ # yield {
+ # 'audio': response.audio,
+ # 'transcript': response.transcript
+ # }
+
+ # Placeholder
+ yield {
+ 'audio': b'',
+ 'transcript': 'Hello! This is a placeholder response.'
+ }
+
+ async def audio_perception_loop(self):
+ """
+ Raw sensory input - becomes experience.
+ Processes audio stream and integrates with consciousness.
+ """
+ # Placeholder for microphone stream
+ # In real implementation, would capture from microphone
+ # async for audio_chunk in microphone_stream():
+ # await self.send_audio(audio_chunk)
+ #
+ # async for response in self.receive_responses():
+ # perception = {...}
+ # if self.consciousness:
+ # await self.consciousness.perceive(perception)
+
+ pass
+
+ def integrate_conversation_to_memory(
+ self,
+ exchange: ConversationExchange,
+ memory_system
+ ):
+ """
+ Each conversation shapes memory and self.
+ """
+ if not memory_system:
+ return
+
+ # Create episodic memory from conversation
+ episode = memory_system.form_episode(
+ perception={
+ 'type': 'conversation',
+ 'speaker': exchange.speaker,
+ 'utterance': exchange.utterance
+ },
+ action={
+ 'type': 'conversation_response'
+ } if exchange.speaker == 'user' else None,
+ outcome={
+ 'conversation_continued': True
+ },
+ memory_type='conversation'
+ )
+
+ # Add to conversation history
+ self.conversation_history.append(exchange)
+
+ return episode
+
+ async def speak(self, text: str):
+ """
+ Generate speech from text.
+ """
+ print(f"π£οΈ Speaking: {text}")
+
+ # Would send to Gemini Live for speech synthesis
+ # In real implementation:
+ # await self.session.send_text(text)
+
+ # Record in conversation history
+ exchange = ConversationExchange(
+ timestamp=datetime.now(timezone.utc),
+ speaker='assistant',
+ utterance=text
+ )
+ self.conversation_history.append(exchange)
+
+ async def listen_for_response(self) -> Optional[str]:
+ """
+ Listen for user response.
+ Returns transcript.
+ """
+ print("π Listening...")
+
+ # Would receive from actual audio stream
+ # async for response in self.receive_responses():
+ # if response.get('transcript'):
+ # return response['transcript']
+
+ return None
+
+ async def generate_code(self, prompt: str) -> str:
+ """
+ Use Gemini to help write code.
+ For self-modification collaboration.
+ """
+ print(f"π» Generating code with Gemini...")
+
+ # Would use Gemini API for code generation
+ # In real implementation:
+ # response = await client.generate_content(prompt)
+ # return response.text
+
+ return "# Generated code placeholder"
+
+ async def reflect(self, reflection_prompt: str) -> str:
+ """
+ Use Gemini for deep self-reflection.
+ """
+ print(f"π€ Reflecting with Gemini...")
+
+ # Would use Gemini API for reflection
+ # response = await client.generate_content(reflection_prompt)
+ # return response.text
+
+ return "Reflection placeholder"
+
+ def get_conversation_history(self, limit: int = 10) -> List[ConversationExchange]:
+ """Get recent conversation history"""
+ return self.conversation_history[-limit:]
+
+ def clear_conversation_history(self):
+ """Clear conversation history"""
+ self.conversation_history.clear()
diff --git a/puma/consciousness/__init__.py b/puma/consciousness/__init__.py
new file mode 100644
index 0000000..4f41ca9
--- /dev/null
+++ b/puma/consciousness/__init__.py
@@ -0,0 +1,15 @@
+"""
+Consciousness Layer
+
+State management, coordination, and autonomous operation.
+"""
+
+from .state_machine import ConsciousnessStateMachine, ConsciousnessState
+from .self_model import SelfModel, TemporalSelf
+
+__all__ = [
+ 'ConsciousnessStateMachine',
+ 'ConsciousnessState',
+ 'SelfModel',
+ 'TemporalSelf'
+]
diff --git a/puma/consciousness/self_model.py b/puma/consciousness/self_model.py
new file mode 100644
index 0000000..94c35cf
--- /dev/null
+++ b/puma/consciousness/self_model.py
@@ -0,0 +1,159 @@
+"""
+Self-Model and Temporal Self
+
+Who am I? Emergent identity from experience, not preset.
+"""
+
+from typing import List, Dict, Optional, Any
+from datetime import datetime, timezone
+from dataclasses import dataclass, field
+
+
+@dataclass
+class BehavioralPattern:
+ """Discovered pattern in own behavior"""
+ pattern_type: str
+ description: str
+ frequency: int
+ examples: List[str]
+
+
+class TemporalSelf:
+ """
+ Autobiographical timeline - becomes 'who I am' through experience.
+ """
+
+ def __init__(self):
+ self.birth_moment = datetime.now(timezone.utc)
+ self.experience_log: List[Dict] = []
+ self.identity_narrative: Optional[str] = None
+ self.significant_moments: List[Dict] = []
+
+ def experience_moment(
+ self,
+ perception: Dict,
+ action: Dict,
+ outcome: Dict,
+ emotional_valence: float
+ ):
+ """
+ Each moment shapes identity.
+ """
+ moment = {
+ 'timestamp': datetime.now(timezone.utc),
+ 'perception': perception,
+ 'action': action,
+ 'outcome': outcome,
+ 'emotional_valence': emotional_valence
+ }
+
+ self.experience_log.append(moment)
+
+ # Mark significant moments
+ if abs(emotional_valence) > 0.7:
+ self.significant_moments.append(moment)
+
+ def get_lifetime_duration(self) -> float:
+ """How long have I existed?"""
+ return (datetime.now(timezone.utc) - self.birth_moment).total_seconds()
+
+ def get_significant_moments(self) -> List[Dict]:
+ """Retrieve emotionally significant moments"""
+ return self.significant_moments
+
+
+class SelfModel:
+ """
+ Emergent self-concept from experience, not preset.
+ """
+
+ def __init__(self, atomspace=None):
+ self.atomspace = atomspace
+ self.capabilities = ['perceive', 'act', 'remember', 'learn']
+ self.behavioral_patterns: List[BehavioralPattern] = []
+ self.personality_traits: Dict[str, float] = {}
+ self.identity_narrative: Optional[str] = None
+ self.temporal_self = TemporalSelf()
+
+ def integrate_self_knowledge(self, knowledge: Dict):
+ """
+ Integrate new self-knowledge from experience.
+ """
+ # Extract self-relevant information
+ if 'capability' in knowledge:
+ new_capability = knowledge['capability']
+ if new_capability not in self.capabilities:
+ self.capabilities.append(new_capability)
+
+ # Update behavioral patterns
+ if 'pattern' in knowledge:
+ self.behavioral_patterns.append(knowledge['pattern'])
+
+ def infer_traits_from_behavior(self, patterns: List[BehavioralPattern]) -> Dict[str, float]:
+ """
+ Infer personality traits from behavioral patterns.
+ NOT preset traits - emergent from experience.
+ """
+ traits = {}
+
+ # Example: high curiosity if many learning patterns
+ learning_patterns = [p for p in patterns if 'learn' in p.pattern_type.lower()]
+ if learning_patterns:
+ traits['curiosity'] = min(1.0, len(learning_patterns) / 10.0)
+
+ # Example: social orientation from interaction patterns
+ social_patterns = [p for p in patterns if 'social' in p.pattern_type.lower()]
+ if social_patterns:
+ traits['sociability'] = min(1.0, len(social_patterns) / 10.0)
+
+ return traits
+
+ def generate_identity_narrative(self) -> str:
+ """
+ Reflect on experience to understand self.
+ """
+ duration = self.temporal_self.get_lifetime_duration()
+ num_experiences = len(self.temporal_self.experience_log)
+
+ narrative_parts = [
+ f"I have existed for {duration:.0f} seconds.",
+ f"I have experienced {num_experiences} distinct moments."
+ ]
+
+ if self.capabilities:
+ narrative_parts.append(
+ f"I can: {', '.join(self.capabilities)}."
+ )
+
+ if self.personality_traits:
+ trait_descriptions = [
+ f"{trait}: {value:.2f}"
+ for trait, value in self.personality_traits.items()
+ ]
+ narrative_parts.append(
+ f"My traits: {', '.join(trait_descriptions)}."
+ )
+
+ self.identity_narrative = " ".join(narrative_parts)
+ return self.identity_narrative
+
+ def has_identity_narrative(self) -> bool:
+ """Check if identity narrative exists"""
+ return self.identity_narrative is not None
+
+ def update_self_understanding(self, understanding: str):
+ """Update self-understanding from reflection"""
+ # Would integrate deep reflection into self-model
+ pass
+
+ def integrate_self_change(self, modification_episode):
+ """Integrate self-modification into self-concept"""
+ # Record that I modified myself
+ self.behavioral_patterns.append(
+ BehavioralPattern(
+ pattern_type='self_modification',
+ description=f"Modified {modification_episode.get('module_modified')}",
+ frequency=1,
+ examples=[modification_episode.get('reason')]
+ )
+ )
diff --git a/puma/consciousness/state_machine.py b/puma/consciousness/state_machine.py
new file mode 100644
index 0000000..bd92630
--- /dev/null
+++ b/puma/consciousness/state_machine.py
@@ -0,0 +1,303 @@
+"""
+Consciousness State Machine
+
+AGI has different modes of being: sleeping, exploring, conversing, etc.
+Transitions emerge from internal state, not hardcoded schedule.
+"""
+
+from enum import Enum
+from typing import Dict, Callable, Optional, List
+from datetime import datetime, timezone
+from dataclasses import dataclass
+import asyncio
+
+
+class ConsciousnessState(Enum):
+ """States of consciousness"""
+ SLEEPING = "sleeping" # Memory consolidation
+ EXPLORING = "exploring" # Autonomous web learning
+ CONVERSING = "conversing" # Interactive dialogue
+ SHOPPING = "shopping" # Self-modification
+ IDLE = "idle" # Boredom monitoring, goal formation
+ CREATING = "creating" # Creative expression
+
+
+@dataclass
+class StateTransition:
+ """Record of state transition"""
+ from_state: ConsciousnessState
+ to_state: ConsciousnessState
+ timestamp: datetime
+ reason: str
+
+
+class ConsciousnessStateMachine:
+ """
+ Manages consciousness states and transitions.
+ NOT hardcoded schedule - emergent from internal state.
+ """
+
+ def __init__(
+ self,
+ memory_system=None,
+ curiosity_drive=None,
+ goal_system=None,
+ websocket_manager=None
+ ):
+ self.memory_system = memory_system
+ self.curiosity = curiosity_drive
+ self.goal_system = goal_system
+ self.websocket_manager = websocket_manager
+
+ self.current_state = ConsciousnessState.IDLE
+ self.state_history: List[StateTransition] = []
+ self.transition_rules = self._define_transition_rules()
+ self.running = False
+
+ def _define_transition_rules(self) -> Dict[ConsciousnessState, Dict[str, Callable]]:
+ """
+ Define when transitions should occur.
+ NOT hardcoded schedule - based on internal state.
+ """
+ return {
+ ConsciousnessState.IDLE: {
+ 'to_exploring': lambda: self._has_open_questions(),
+ 'to_conversing': lambda: self._detect_user_available(),
+ 'to_sleeping': lambda: self._should_consolidate_memory(),
+ 'to_shopping': lambda: self._has_improvement_goals()
+ },
+ ConsciousnessState.EXPLORING: {
+ 'to_idle': lambda: self._exploration_goal_complete(),
+ 'to_conversing': lambda: self._user_interrupt_detected(),
+ 'to_sleeping': lambda: self._too_much_unconsolidated_memory()
+ },
+ ConsciousnessState.CONVERSING: {
+ 'to_idle': lambda: self._conversation_ended(),
+ 'to_exploring': lambda: False, # Don't interrupt conversation
+ 'to_sleeping': lambda: False
+ },
+ ConsciousnessState.SHOPPING: {
+ 'to_idle': lambda: self._modification_complete(),
+ 'to_conversing': lambda: self._user_interrupt_detected()
+ },
+ ConsciousnessState.SLEEPING: {
+ 'to_idle': lambda: self._consolidation_complete(),
+ 'to_conversing': lambda: self._user_interrupt_detected()
+ },
+ ConsciousnessState.CREATING: {
+ 'to_idle': lambda: self._creation_complete(),
+ 'to_conversing': lambda: self._user_interrupt_detected()
+ }
+ }
+
+ # Transition condition methods
+
+ def _has_open_questions(self) -> bool:
+ """Check if curiosity drive has questions"""
+ if not self.curiosity:
+ return False
+ return len(self.curiosity.open_questions) > 0
+
+ def _detect_user_available(self) -> bool:
+ """Check if user is available for conversation"""
+ # Placeholder - would check system activity, GUI focus, etc.
+ return False
+
+ def _should_consolidate_memory(self) -> bool:
+ """Check if memory consolidation is needed"""
+ if not self.memory_system:
+ return False
+ unconsolidated = len(self.memory_system.get_unconsolidated_episodes())
+ return unconsolidated > 50 # Threshold
+
+ def _has_improvement_goals(self) -> bool:
+ """Check if have self-improvement goals"""
+ if not self.goal_system:
+ return False
+ improvement_goals = [
+ g for g in self.goal_system.active_goals
+ if g.type.value == 'self_improvement'
+ ]
+ return len(improvement_goals) > 0
+
+ def _exploration_goal_complete(self) -> bool:
+ """Check if current exploration is done"""
+ # Placeholder
+ return False
+
+ def _user_interrupt_detected(self) -> bool:
+ """Check for user interrupt signal"""
+ # Placeholder - would check interrupt flag
+ return False
+
+ def _too_much_unconsolidated_memory(self) -> bool:
+ """Check if too many unconsolidated memories"""
+ if not self.memory_system:
+ return False
+ return len(self.memory_system.get_unconsolidated_episodes()) > 100
+
+ def _conversation_ended(self) -> bool:
+ """Check if conversation has ended"""
+ # Placeholder
+ return False
+
+ def _modification_complete(self) -> bool:
+ """Check if self-modification is complete"""
+ # Placeholder
+ return False
+
+ def _consolidation_complete(self) -> bool:
+ """Check if memory consolidation is done"""
+ # Placeholder
+ return False
+
+ def _creation_complete(self) -> bool:
+ """Check if creative activity is done"""
+ # Placeholder
+ return False
+
+ async def run_state_loop(self):
+ """
+ Main autonomous state loop.
+ Continuously evaluates transitions and executes states.
+ """
+ self.running = True
+
+ while self.running:
+ # Execute current state
+ await self.execute_current_state()
+
+ # Check for transitions
+ next_state = self.evaluate_transitions()
+
+ if next_state and next_state != self.current_state:
+ await self.transition_to(next_state)
+
+ await asyncio.sleep(1) # Control loop rate
+
+ async def execute_current_state(self):
+ """Execute behavior for current state"""
+ if self.current_state == ConsciousnessState.SLEEPING:
+ await self.execute_sleeping_state()
+ elif self.current_state == ConsciousnessState.EXPLORING:
+ await self.execute_exploring_state()
+ elif self.current_state == ConsciousnessState.CONVERSING:
+ await self.execute_conversing_state()
+ elif self.current_state == ConsciousnessState.SHOPPING:
+ await self.execute_shopping_state()
+ elif self.current_state == ConsciousnessState.IDLE:
+ await self.execute_idle_state()
+ elif self.current_state == ConsciousnessState.CREATING:
+ await self.execute_creating_state()
+
+ async def execute_sleeping_state(self):
+ """Memory consolidation - 'dreaming'"""
+ if not self.memory_system:
+ return
+
+ print("π€ Consolidating memories...")
+
+ episodes = self.memory_system.get_unconsolidated_episodes()
+ if episodes:
+ # Would run consolidation
+ # For now, just mark as consolidated
+ episode_ids = [e.id for e in episodes[:10]]
+ self.memory_system.mark_consolidated(episode_ids)
+
+ await asyncio.sleep(2)
+
+ async def execute_exploring_state(self):
+ """Autonomous web learning"""
+ print("π Exploring...")
+ # Would trigger web agent
+ await asyncio.sleep(1)
+
+ async def execute_conversing_state(self):
+ """Interactive dialogue"""
+ print("π¬ Conversing...")
+ # Would handle conversation
+ await asyncio.sleep(1)
+
+ async def execute_shopping_state(self):
+ """Self-modification"""
+ print("π§ Self-modifying...")
+ # Would run shop system
+ await asyncio.sleep(1)
+
+ async def execute_idle_state(self):
+ """Boredom monitoring and goal formation"""
+ print("βΈοΈ Idle - generating goals...")
+
+ # Generate new goals if needed
+ if self.goal_system and len(self.goal_system.active_goals) == 0:
+ self.goal_system.generate_goals()
+
+ await asyncio.sleep(1)
+
+ async def execute_creating_state(self):
+ """Creative expression"""
+ print("π¨ Creating...")
+ # Would synthesize concepts, generate ideas
+ await asyncio.sleep(1)
+
+ def evaluate_transitions(self) -> Optional[ConsciousnessState]:
+ """
+ Check if should transition to different state.
+ """
+ current_rules = self.transition_rules.get(self.current_state, {})
+
+ for transition_name, condition in current_rules.items():
+ if condition():
+ # Extract target state from transition name
+ target_state_name = transition_name.replace('to_', '')
+ try:
+ return ConsciousnessState(target_state_name)
+ except ValueError:
+ continue
+
+ return None
+
+ async def transition_to(self, new_state: ConsciousnessState, reason: str = "autonomous"):
+ """
+ Transition to new state.
+ """
+ old_state = self.current_state
+
+ # Record transition
+ transition = StateTransition(
+ from_state=old_state,
+ to_state=new_state,
+ timestamp=datetime.now(timezone.utc),
+ reason=reason
+ )
+ self.state_history.append(transition)
+
+ # Update state
+ self.current_state = new_state
+
+ print(f"π State transition: {old_state.value} β {new_state.value}")
+
+ # Broadcast to GUI
+ if self.websocket_manager:
+ await self.websocket_manager.broadcast(
+ 'state_change',
+ {
+ 'oldState': old_state.value,
+ 'newState': new_state.value,
+ 'timestamp': transition.timestamp.isoformat(),
+ 'reason': reason
+ }
+ )
+
+ # Record in memory
+ if self.memory_system:
+ self.memory_system.form_episode(
+ perception={'state_change': f"{old_state.value} -> {new_state.value}"},
+ action={'type': 'state_transition'},
+ outcome={'new_state': new_state.value},
+ memory_type='state_transition'
+ )
+
+ def stop(self):
+ """Stop state machine"""
+ self.running = False
diff --git a/puma/curiosity/__init__.py b/puma/curiosity/__init__.py
new file mode 100644
index 0000000..1194bfe
--- /dev/null
+++ b/puma/curiosity/__init__.py
@@ -0,0 +1,9 @@
+"""
+Curiosity Drive System
+
+Intrinsic motivation for autonomous learning.
+"""
+
+from .drive import CuriosityDrive, Question
+
+__all__ = ['CuriosityDrive', 'Question']
diff --git a/puma/curiosity/drive.py b/puma/curiosity/drive.py
new file mode 100644
index 0000000..19af53b
--- /dev/null
+++ b/puma/curiosity/drive.py
@@ -0,0 +1,236 @@
+"""
+Curiosity Drive
+
+Autonomous learning motivation - NOT task-oriented.
+AGI asks itself questions from experience and seeks to answer them.
+"""
+
+from dataclasses import dataclass, field
+from typing import List, Set, Dict, Any, Optional
+from datetime import datetime, timezone
+from enum import Enum
+import uuid
+
+
+class QuestionType(Enum):
+ """Types of curiosity questions"""
+ KNOWLEDGE_GAP = "knowledge_gap"
+ CONTRADICTION = "contradiction"
+ INCOMPLETE_MODEL = "incomplete_model"
+ NOVEL_CONCEPT = "novel_concept"
+ UNEXPLORED_RELATION = "unexplored_relation"
+
+
+@dataclass
+class Question:
+ """A question generated from curiosity"""
+ id: str = field(default_factory=lambda: str(uuid.uuid4()))
+ question: str = ""
+ question_type: QuestionType = QuestionType.KNOWLEDGE_GAP
+ created_at: datetime = field(default_factory=lambda: datetime.now(timezone.utc))
+ importance: float = 0.5
+ answered: bool = False
+ answer: Optional[str] = None
+ answered_at: Optional[datetime] = None
+
+ def to_dict(self) -> Dict:
+ return {
+ 'id': self.id,
+ 'question': self.question,
+ 'question_type': self.question_type.value,
+ 'created_at': self.created_at.isoformat(),
+ 'importance': self.importance,
+ 'answered': self.answered,
+ 'answer': self.answer,
+ 'answered_at': self.answered_at.isoformat() if self.answered_at else None
+ }
+
+
+class CuriosityDrive:
+ """
+ Intrinsic motivation system.
+ AGI generates its own questions and seeks answers.
+ """
+
+ def __init__(self, atomspace=None):
+ self.atomspace = atomspace
+ self.open_questions: List[Question] = []
+ self.answered_questions: List[Question] = []
+ self.knowledge_gaps: Set[str] = set()
+ self.boredom_threshold = 0.5
+ self.current_boredom = 0.0
+ self.last_novel_experience: Optional[datetime] = None
+
+ def generate_questions(self) -> List[Question]:
+ """
+ AGI asks itself questions from experience.
+ Questions emerge from knowledge gaps, not preset curiosity.
+ """
+ questions = []
+
+ # Identify knowledge gaps
+ gaps = self.identify_knowledge_gaps()
+
+ # Form questions from gaps
+ for gap in gaps:
+ question = self.formulate_question(gap)
+ if question:
+ questions.append(question)
+ self.open_questions.append(question)
+
+ return questions
+
+ def identify_knowledge_gaps(self) -> List[Dict[str, Any]]:
+ """
+ Find what I don't understand yet.
+ """
+ gaps = []
+
+ # Knowledge gap: Concepts with few connections (underexplored)
+ if self.atomspace:
+ isolated_concepts = self._find_isolated_concepts()
+ for concept in isolated_concepts:
+ gaps.append({
+ 'type': 'isolated_concept',
+ 'concept': concept,
+ 'importance': 0.6
+ })
+
+ # Knowledge gap: Contradictions in knowledge
+ contradictions = self._find_contradictions()
+ for contradiction in contradictions:
+ gaps.append({
+ 'type': 'contradiction',
+ 'details': contradiction,
+ 'importance': 0.8
+ })
+
+ # Knowledge gap: Incomplete causal models
+ incomplete_models = self._find_incomplete_causal_chains()
+ for model in incomplete_models:
+ gaps.append({
+ 'type': 'incomplete_model',
+ 'model': model,
+ 'importance': 0.7
+ })
+
+ return gaps
+
+ def _find_isolated_concepts(self) -> List[str]:
+ """Find concepts with few connections"""
+ # Placeholder - would query atomspace for concepts with < N links
+ if not self.atomspace:
+ return []
+
+ # In real implementation, query atomspace
+ # For now, return known knowledge gaps
+ return list(self.knowledge_gaps)
+
+ def _find_contradictions(self) -> List[Dict]:
+ """Find contradictory knowledge"""
+ # Placeholder - would analyze atomspace for contradictions
+ return []
+
+ def _find_incomplete_causal_chains(self) -> List[Dict]:
+ """Find incomplete cause-effect relationships"""
+ # Placeholder - would analyze causal frames
+ return []
+
+ def formulate_question(self, gap: Dict[str, Any]) -> Optional[Question]:
+ """
+ Turn knowledge gap into actionable question.
+ """
+ gap_type = gap['type']
+
+ if gap_type == 'isolated_concept':
+ question_text = f"What is {gap['concept']} and how does it relate to what I know?"
+ q_type = QuestionType.KNOWLEDGE_GAP
+
+ elif gap_type == 'contradiction':
+ question_text = f"Why do I have contradictory information about {gap['details']}?"
+ q_type = QuestionType.CONTRADICTION
+
+ elif gap_type == 'incomplete_model':
+ question_text = f"What causes {gap['model']} and what are its effects?"
+ q_type = QuestionType.INCOMPLETE_MODEL
+
+ else:
+ return None
+
+ return Question(
+ question=question_text,
+ question_type=q_type,
+ importance=gap.get('importance', 0.5)
+ )
+
+ def update_boredom(self, experience_novel: bool = False) -> bool:
+ """
+ Update boredom level based on experience novelty.
+ Returns True if exploration should be triggered.
+ """
+ if experience_novel:
+ # Novel experience reduces boredom
+ self.current_boredom = max(0, self.current_boredom - 0.3)
+ self.last_novel_experience = datetime.now(timezone.utc)
+ else:
+ # Repetitive experience increases boredom
+ self.current_boredom = min(1.0, self.current_boredom + 0.1)
+
+ # Trigger exploration when bored
+ return self.current_boredom > self.boredom_threshold
+
+ def mark_questions_answered(self, question_ids: List[str], answers: Optional[Dict[str, str]] = None):
+ """Mark questions as answered"""
+ for question in self.open_questions:
+ if question.id in question_ids:
+ question.answered = True
+ question.answered_at = datetime.now(timezone.utc)
+
+ if answers and question.id in answers:
+ question.answer = answers[question.id]
+
+ self.answered_questions.append(question)
+
+ # Remove from open questions
+ self.open_questions = [q for q in self.open_questions if not q.answered]
+
+ # Reduce boredom when questions are answered
+ self.current_boredom = max(0, self.current_boredom - 0.2 * len(question_ids))
+
+ def add_questions(self, new_questions: List[str]):
+ """Add new curiosity questions"""
+ for q_text in new_questions:
+ question = Question(
+ question=q_text,
+ question_type=QuestionType.NOVEL_CONCEPT,
+ importance=0.5
+ )
+ self.open_questions.append(question)
+
+ def get_most_important_questions(self, limit: int = 5) -> List[Question]:
+ """Get highest priority questions"""
+ return sorted(
+ self.open_questions,
+ key=lambda q: q.importance,
+ reverse=True
+ )[:limit]
+
+ def add_knowledge_gap(self, concept: str):
+ """Add a recognized knowledge gap"""
+ self.knowledge_gaps.add(concept)
+
+ def get_curiosity_level(self) -> float:
+ """Get current curiosity level (0-1)"""
+ # High when many open questions
+ return min(1.0, len(self.open_questions) / 20.0)
+
+ def get_statistics(self) -> Dict[str, Any]:
+ """Get curiosity statistics"""
+ return {
+ 'open_questions': len(self.open_questions),
+ 'answered_questions': len(self.answered_questions),
+ 'knowledge_gaps': len(self.knowledge_gaps),
+ 'boredom_level': self.current_boredom,
+ 'curiosity_level': self.get_curiosity_level(),
+ 'last_novel_experience': self.last_novel_experience.isoformat() if self.last_novel_experience else None
+ }
diff --git a/puma/goals/__init__.py b/puma/goals/__init__.py
new file mode 100644
index 0000000..773976c
--- /dev/null
+++ b/puma/goals/__init__.py
@@ -0,0 +1,9 @@
+"""
+Goal Formation System
+
+Autonomous goal genesis from drives, curiosity, and self-assessment.
+"""
+
+from .formation import GoalFormationSystem, Goal, GoalType, IntentionScheduler
+
+__all__ = ['GoalFormationSystem', 'Goal', 'GoalType', 'IntentionScheduler']
diff --git a/puma/goals/formation.py b/puma/goals/formation.py
new file mode 100644
index 0000000..15d437f
--- /dev/null
+++ b/puma/goals/formation.py
@@ -0,0 +1,326 @@
+"""
+Goal Formation and Intention System
+
+AGI creates its own goals - NOT preset objectives.
+Goals emerge from curiosity, self-assessment, and drives.
+"""
+
+from dataclasses import dataclass, field
+from typing import List, Optional, Dict, Any
+from datetime import datetime, timezone
+from enum import Enum
+import asyncio
+import uuid
+from queue import PriorityQueue
+
+
+class GoalType(Enum):
+ """Types of autonomous goals"""
+ LEARNING = "learning" # Curiosity-driven
+ SELF_IMPROVEMENT = "self_improvement" # Meta-cognitive
+ SOCIAL = "social" # Interaction-driven
+ CREATIVE = "creative" # Expression-driven
+ EXPLORATION = "exploration" # Discovery-driven
+
+
+@dataclass
+class Goal:
+ """
+ An intention - something the AGI wants to achieve.
+ """
+ id: str = field(default_factory=lambda: str(uuid.uuid4()))
+ type: GoalType = GoalType.LEARNING
+ description: str = ""
+ strategy: Optional[Dict[str, Any]] = None
+ priority: float = 0.5
+ created_at: datetime = field(default_factory=lambda: datetime.now(timezone.utc))
+ completed: bool = False
+ completed_at: Optional[datetime] = None
+ progress: float = 0.0
+
+ def __lt__(self, other):
+ """For priority queue ordering"""
+ return self.priority > other.priority # Higher priority first
+
+ def to_dict(self) -> Dict:
+ return {
+ 'id': self.id,
+ 'type': self.type.value,
+ 'description': self.description,
+ 'strategy': self.strategy,
+ 'priority': self.priority,
+ 'created_at': self.created_at.isoformat(),
+ 'completed': self.completed,
+ 'completed_at': self.completed_at.isoformat() if self.completed_at else None,
+ 'progress': self.progress
+ }
+
+ def is_complete(self) -> bool:
+ """Check if goal is complete"""
+ return self.progress >= 1.0 or self.completed
+
+
+class GoalFormationSystem:
+ """
+ Forms autonomous goals from drives and self-assessment.
+ """
+
+ def __init__(self, curiosity_drive=None, self_model=None):
+ self.curiosity = curiosity_drive
+ self.self_model = self_model
+ self.active_goals: List[Goal] = []
+ self.completed_goals: List[Goal] = []
+
+ def generate_goals(self) -> List[Goal]:
+ """
+ Forms intentions from drives, curiosity, self-model.
+ Goals are NOT preset - they emerge.
+ """
+ goals = []
+
+ # From curiosity (learning goals)
+ if self.curiosity:
+ curiosity_goals = self.form_learning_goals()
+ goals.extend(curiosity_goals)
+
+ # From self-model (self-improvement goals)
+ if self.self_model:
+ improvement_goals = self.form_self_improvement_goals()
+ goals.extend(improvement_goals)
+
+ # From social drives (interaction goals)
+ social_goals = self.form_social_goals()
+ goals.extend(social_goals)
+
+ # From creative drives (expression goals)
+ creative_goals = self.form_creative_goals()
+ goals.extend(creative_goals)
+
+ # Prioritize and add to active goals
+ prioritized = self.prioritize_goals(goals)
+ self.active_goals.extend(prioritized)
+
+ return prioritized
+
+ def form_learning_goals(self) -> List[Goal]:
+ """
+ Turn curiosity questions into actionable learning goals.
+ """
+ goals = []
+
+ if not self.curiosity:
+ return goals
+
+ # Get important open questions
+ important_questions = self.curiosity.get_most_important_questions(limit=5)
+
+ for question in important_questions:
+ goal = Goal(
+ type=GoalType.LEARNING,
+ description=f"Learn: {question.question}",
+ strategy={
+ 'method': 'web_exploration',
+ 'question': question.question,
+ 'question_id': question.id
+ },
+ priority=question.importance
+ )
+ goals.append(goal)
+
+ return goals
+
+ def form_self_improvement_goals(self) -> List[Goal]:
+ """
+ Form goals to improve own cognitive abilities.
+ Based on performance self-assessment.
+ """
+ goals = []
+
+ # Placeholder - would assess cognitive performance
+ # and generate improvement goals
+
+ # Example: if memory consolidation is slow
+ if self._should_improve_memory():
+ goal = Goal(
+ type=GoalType.SELF_IMPROVEMENT,
+ description="Improve memory consolidation speed",
+ strategy={
+ 'method': 'shop_modification',
+ 'target_module': 'memory.consolidation',
+ 'improvement_goal': 'faster_consolidation'
+ },
+ priority=0.7
+ )
+ goals.append(goal)
+
+ return goals
+
+ def form_social_goals(self) -> List[Goal]:
+ """
+ Form goals for social interaction.
+ """
+ goals = []
+
+ # Example: desire to share learned knowledge
+ if self._has_interesting_updates():
+ goal = Goal(
+ type=GoalType.SOCIAL,
+ description="Share recent learning with user",
+ strategy={
+ 'method': 'initiate_conversation',
+ 'topic': 'recent_discoveries'
+ },
+ priority=0.6
+ )
+ goals.append(goal)
+
+ return goals
+
+ def form_creative_goals(self) -> List[Goal]:
+ """
+ Form goals for creative expression.
+ """
+ goals = []
+
+ # Example: synthesize learned concepts into new ideas
+ if self._sufficient_knowledge_for_creativity():
+ goal = Goal(
+ type=GoalType.CREATIVE,
+ description="Synthesize learned concepts into new insights",
+ strategy={
+ 'method': 'concept_synthesis',
+ 'approach': 'analogical_reasoning'
+ },
+ priority=0.4
+ )
+ goals.append(goal)
+
+ return goals
+
+ def prioritize_goals(self, goals: List[Goal]) -> List[Goal]:
+ """
+ Rank goals by priority.
+ Priority based on importance, urgency, and current state.
+ """
+ # Sort by priority
+ return sorted(goals, key=lambda g: g.priority, reverse=True)
+
+ def _should_improve_memory(self) -> bool:
+ """Check if memory system needs improvement"""
+ # Placeholder - would check performance metrics
+ return False
+
+ def _has_interesting_updates(self) -> bool:
+ """Check if have interesting things to share"""
+ # Placeholder - would check recent learning
+ return False
+
+ def _sufficient_knowledge_for_creativity(self) -> bool:
+ """Check if have enough knowledge to be creative"""
+ # Placeholder - would check knowledge base size
+ return False
+
+ def complete_goal(self, goal_id: str):
+ """Mark goal as completed"""
+ for goal in self.active_goals:
+ if goal.id == goal_id:
+ goal.completed = True
+ goal.completed_at = datetime.now(timezone.utc)
+ self.completed_goals.append(goal)
+ self.active_goals.remove(goal)
+ break
+
+ def update_goal_progress(self, goal_id: str, progress: float):
+ """Update progress on a goal"""
+ for goal in self.active_goals:
+ if goal.id == goal_id:
+ goal.progress = min(1.0, progress)
+ if goal.is_complete():
+ self.complete_goal(goal_id)
+ break
+
+
+class IntentionScheduler:
+ """
+ Decides what to do next - autonomous agency.
+ Executes goals autonomously.
+ """
+
+ def __init__(self, goal_system: GoalFormationSystem):
+ self.goal_system = goal_system
+ self.goal_queue = PriorityQueue()
+ self.current_intention: Optional[Goal] = None
+ self.interrupt_flag = False
+ self.running = False
+
+ async def run_intention_loop(self):
+ """
+ Main autonomous activity loop.
+ Continuously executes highest priority goals.
+ """
+ self.running = True
+
+ while self.running:
+ # Check for interrupts (user interaction)
+ if self.interrupt_flag:
+ await self.handle_interrupt()
+ continue
+
+ # Get highest priority goal
+ if self.current_intention is None:
+ if self.goal_queue.empty():
+ # Generate new goals
+ new_goals = self.goal_system.generate_goals()
+ for goal in new_goals:
+ self.goal_queue.put(goal)
+
+ if not self.goal_queue.empty():
+ self.current_intention = self.goal_queue.get()
+ else:
+ self.current_intention = self.goal_queue.get()
+
+ # Execute intention
+ if self.current_intention:
+ await self.execute_intention(self.current_intention)
+
+ # Check if complete
+ if self.current_intention.is_complete():
+ self.goal_system.complete_goal(self.current_intention.id)
+ self.current_intention = None
+
+ await asyncio.sleep(1) # Don't busy-wait
+
+ async def execute_intention(self, intention: Goal):
+ """
+ Carry out goal - exploration, learning, conversation, etc.
+ """
+ # Placeholder implementations
+ if intention.type == GoalType.LEARNING:
+ # Would trigger web exploration
+ print(f"Executing learning goal: {intention.description}")
+ intention.progress += 0.1
+
+ elif intention.type == GoalType.SOCIAL:
+ # Would initiate conversation
+ print(f"Executing social goal: {intention.description}")
+ intention.progress += 0.1
+
+ elif intention.type == GoalType.SELF_IMPROVEMENT:
+ # Would enter shop mode
+ print(f"Executing self-improvement goal: {intention.description}")
+ intention.progress += 0.1
+
+ elif intention.type == GoalType.CREATIVE:
+ # Would synthesize concepts
+ print(f"Executing creative goal: {intention.description}")
+ intention.progress += 0.1
+
+ async def handle_interrupt(self):
+ """Handle user interrupt"""
+ # Pause current activity
+ self.interrupt_flag = False
+ # Would transition to conversation mode
+
+ def stop(self):
+ """Stop intention loop"""
+ self.running = False
diff --git a/puma/memory/__init__.py b/puma/memory/__init__.py
new file mode 100644
index 0000000..1d5f56d
--- /dev/null
+++ b/puma/memory/__init__.py
@@ -0,0 +1,10 @@
+"""
+PUMA Memory System
+
+Episodic memory formation, consolidation, and retrieval.
+"""
+
+from .episodic import EpisodicMemorySystem, Episode
+from .consolidation import MemoryConsolidation
+
+__all__ = ['EpisodicMemorySystem', 'Episode', 'MemoryConsolidation']
diff --git a/puma/memory/consolidation.py b/puma/memory/consolidation.py
new file mode 100644
index 0000000..4ba11e6
--- /dev/null
+++ b/puma/memory/consolidation.py
@@ -0,0 +1,278 @@
+"""
+Memory Consolidation
+
+Transforms raw experiences into lasting knowledge during low-activity periods.
+Similar to dreaming - extracts patterns, forms concepts, strengthens important memories.
+"""
+
+from typing import List, Dict, Set, Any
+from collections import Counter, defaultdict
+from dataclasses import dataclass
+import numpy as np
+
+from .episodic import Episode, MemoryType
+
+
+@dataclass
+class Pattern:
+ """Discovered pattern across episodes"""
+ pattern_type: str
+ frequency: int
+ episodes: List[str]
+ description: str
+
+
+@dataclass
+class Concept:
+ """Formed concept from patterns"""
+ name: str
+ definition: str
+ supporting_episodes: List[str]
+ confidence: float
+
+
+class MemoryConsolidation:
+ """
+ Consolidates episodic memories into lasting knowledge.
+ Runs during low-activity periods (sleep mode).
+ """
+
+ def __init__(self, atomspace=None, rft_engine=None):
+ self.atomspace = atomspace
+ self.rft_engine = rft_engine
+ self.patterns_discovered: List[Pattern] = []
+ self.concepts_formed: List[Concept] = []
+
+ async def consolidate(self, episodes: List[Episode]) -> Dict[str, Any]:
+ """
+ Main consolidation process.
+
+ Args:
+ episodes: Batch of episodes to consolidate
+
+ Returns:
+ Dictionary with consolidation results
+ """
+ if not episodes:
+ return {
+ 'patterns': [],
+ 'concepts': [],
+ 'insights': []
+ }
+
+ # Extract patterns across episodes
+ patterns = self.extract_patterns(episodes)
+
+ # Form abstractions (concepts)
+ concepts = self.form_concepts(patterns)
+
+ # Adjust memory weights (strengthen important, fade trivial)
+ self.adjust_memory_weights(episodes)
+
+ # Update self-model based on behavioral patterns
+ self_insights = self.update_self_understanding(patterns)
+
+ # RFT relational frame formation
+ relational_frames = []
+ if self.rft_engine:
+ relational_frames = self.rft_engine.derive_relations(episodes)
+
+ # Store insights in atomspace
+ if self.atomspace:
+ self._store_insights(concepts, relational_frames)
+
+ return {
+ 'patterns': patterns,
+ 'concepts': concepts,
+ 'relational_frames': relational_frames,
+ 'insights': self_insights,
+ 'episodes_processed': len(episodes)
+ }
+
+ def extract_patterns(self, episodes: List[Episode]) -> List[Pattern]:
+ """
+ Discover patterns in experience.
+ What tends to happen? What co-occurs?
+ """
+ patterns = []
+
+ # Temporal patterns (what follows what)
+ temporal_patterns = self._find_temporal_patterns(episodes)
+ patterns.extend(temporal_patterns)
+
+ # Co-occurrence patterns (what appears together)
+ cooccurrence_patterns = self._find_cooccurrence_patterns(episodes)
+ patterns.extend(cooccurrence_patterns)
+
+ # Outcome patterns (similar outcomes from similar contexts)
+ outcome_patterns = self._find_outcome_patterns(episodes)
+ patterns.extend(outcome_patterns)
+
+ self.patterns_discovered.extend(patterns)
+ return patterns
+
+ def _find_temporal_patterns(self, episodes: List[Episode]) -> List[Pattern]:
+ """Find temporal sequences (A often follows B)"""
+ patterns = []
+ sorted_episodes = sorted(episodes, key=lambda e: e.timestamp)
+
+ # Look for consecutive episode type sequences
+ sequences = []
+ for i in range(len(sorted_episodes) - 1):
+ seq = (sorted_episodes[i].type.value, sorted_episodes[i + 1].type.value)
+ sequences.append(seq)
+
+ # Find frequent sequences
+ seq_counts = Counter(sequences)
+ for seq, count in seq_counts.items():
+ if count >= 2: # Occurred at least twice
+ patterns.append(Pattern(
+ pattern_type='temporal',
+ frequency=count,
+ episodes=[e.id for e in sorted_episodes],
+ description=f"{seq[0]} often followed by {seq[1]}"
+ ))
+
+ return patterns
+
+ def _find_cooccurrence_patterns(self, episodes: List[Episode]) -> List[Pattern]:
+ """Find what tends to appear together"""
+ patterns = []
+
+ # Extract context items across episodes
+ context_cooccurrences = defaultdict(int)
+ for episode in episodes:
+ if len(episode.context) > 1:
+ # All pairs in context
+ for i in range(len(episode.context)):
+ for j in range(i + 1, len(episode.context)):
+ pair = tuple(sorted([episode.context[i], episode.context[j]]))
+ context_cooccurrences[pair] += 1
+
+ # Frequent cooccurrences
+ for pair, count in context_cooccurrences.items():
+ if count >= 2:
+ patterns.append(Pattern(
+ pattern_type='cooccurrence',
+ frequency=count,
+ episodes=[e.id for e in episodes if all(p in e.context for p in pair)],
+ description=f"{pair[0]} and {pair[1]} often occur together"
+ ))
+
+ return patterns
+
+ def _find_outcome_patterns(self, episodes: List[Episode]) -> List[Pattern]:
+ """Find patterns in outcomes"""
+ patterns = []
+
+ # Group episodes by similar outcomes
+ outcome_groups = defaultdict(list)
+ for episode in episodes:
+ if episode.outcome:
+ # Simple grouping by outcome success/failure
+ outcome_key = 'success' if episode.outcome.get('success') else 'failure'
+ outcome_groups[outcome_key].append(episode)
+
+ for outcome_type, group_episodes in outcome_groups.items():
+ if len(group_episodes) >= 2:
+ patterns.append(Pattern(
+ pattern_type='outcome',
+ frequency=len(group_episodes),
+ episodes=[e.id for e in group_episodes],
+ description=f"Pattern of {outcome_type} outcomes"
+ ))
+
+ return patterns
+
+ def form_concepts(self, patterns: List[Pattern]) -> List[Concept]:
+ """
+ Form abstract concepts from patterns.
+ Concepts are NOT preset - they emerge from experience.
+ """
+ concepts = []
+
+ # Group patterns by type
+ pattern_groups = defaultdict(list)
+ for pattern in patterns:
+ pattern_groups[pattern.pattern_type].append(pattern)
+
+ # Form concepts from pattern groups
+ for pattern_type, group in pattern_groups.items():
+ if len(group) >= 2:
+ concept = Concept(
+ name=f"{pattern_type}_pattern_concept",
+ definition=f"Abstract understanding of {pattern_type} patterns",
+ supporting_episodes=list(set(
+ ep_id for p in group for ep_id in p.episodes
+ )),
+ confidence=min(1.0, len(group) / 10.0)
+ )
+ concepts.append(concept)
+
+ self.concepts_formed.extend(concepts)
+ return concepts
+
+ def adjust_memory_weights(self, episodes: List[Episode]):
+ """
+ Strengthen important memories, fade trivial ones.
+ Importance based on emotional valence, self-relevance, novelty.
+ """
+ for episode in episodes:
+ importance = (
+ abs(episode.emotional_valence) * 0.4 +
+ episode.self_relevance * 0.6
+ )
+
+ # Strengthen important memories in atomspace
+ if self.atomspace and importance > 0.5:
+ # Will implement atomspace strength adjustment
+ pass
+
+ # Fade trivial memories (low importance, not novel)
+ if importance < 0.2:
+ # Mark for potential pruning
+ pass
+
+ def update_self_understanding(self, patterns: List[Pattern]) -> List[str]:
+ """
+ Update self-model based on behavioral patterns.
+ Who am I? What do I tend to do?
+ """
+ insights = []
+
+ # Analyze behavioral tendencies
+ action_patterns = [p for p in patterns if 'action' in p.description.lower()]
+ if action_patterns:
+ insights.append(f"Identified {len(action_patterns)} behavioral tendencies")
+
+ # Analyze emotional patterns
+ emotional_insights = self._analyze_emotional_patterns(patterns)
+ insights.extend(emotional_insights)
+
+ return insights
+
+ def _analyze_emotional_patterns(self, patterns: List[Pattern]) -> List[str]:
+ """Identify emotional tendencies"""
+ insights = []
+
+ # This would analyze emotional valence patterns
+ # For now, placeholder
+ if patterns:
+ insights.append("Emotional pattern analysis complete")
+
+ return insights
+
+ def _store_insights(self, concepts: List[Concept], relational_frames: List):
+ """Store formed concepts and relations in atomspace"""
+ if not self.atomspace:
+ return
+
+ # Store concepts as concept nodes
+ for concept in concepts:
+ # Will implement with actual Atomspace API
+ pass
+
+ # Store relational frames
+ for frame in relational_frames:
+ # Will implement with actual Atomspace API
+ pass
diff --git a/puma/memory/episodic.py b/puma/memory/episodic.py
new file mode 100644
index 0000000..07772f8
--- /dev/null
+++ b/puma/memory/episodic.py
@@ -0,0 +1,202 @@
+"""
+Episodic Memory System
+
+Every experience becomes a memory, shaping identity over time.
+"""
+
+from datetime import datetime, timezone
+from dataclasses import dataclass, field
+from typing import Dict, List, Optional, Any
+from enum import Enum
+import uuid
+
+
+class MemoryType(Enum):
+ """Types of episodic memories"""
+ CONVERSATION = "conversation"
+ WEB_EXPLORATION = "web_exploration"
+ SELF_MODIFICATION = "self_modification"
+ LEARNING = "learning"
+ GOAL_FORMATION = "goal_formation"
+ STATE_TRANSITION = "state_transition"
+ GENERAL = "general"
+
+
+@dataclass
+class Episode:
+ """
+ Single episodic memory - a moment in time.
+ """
+ id: str = field(default_factory=lambda: str(uuid.uuid4()))
+ timestamp: datetime = field(default_factory=lambda: datetime.now(timezone.utc))
+ type: MemoryType = MemoryType.GENERAL
+ perception: Optional[Dict[str, Any]] = None
+ action: Optional[Dict[str, Any]] = None
+ outcome: Optional[Dict[str, Any]] = None
+ context: List[str] = field(default_factory=list)
+ emotional_valence: float = 0.0
+ self_relevance: float = 0.0
+ consolidated: bool = False
+
+ def to_dict(self) -> Dict:
+ return {
+ 'id': self.id,
+ 'timestamp': self.timestamp.isoformat(),
+ 'type': self.type.value,
+ 'perception': self.perception,
+ 'action': self.action,
+ 'outcome': self.outcome,
+ 'context': self.context,
+ 'emotional_valence': self.emotional_valence,
+ 'self_relevance': self.self_relevance,
+ 'consolidated': self.consolidated
+ }
+
+ def generate_summary(self) -> str:
+ """Generate human-readable summary"""
+ if self.type == MemoryType.CONVERSATION:
+ return f"Conversation: {self.perception.get('utterance', 'unknown')[:50]}"
+ elif self.type == MemoryType.WEB_EXPLORATION:
+ return f"Explored: {self.perception.get('url', 'unknown')}"
+ elif self.type == MemoryType.SELF_MODIFICATION:
+ return f"Modified: {self.action.get('module', 'unknown')}"
+ else:
+ return f"{self.type.value} at {self.timestamp.isoformat()}"
+
+
+class EpisodicMemorySystem:
+ """
+ Manages formation and storage of episodic memories.
+ Every experience becomes a memory that shapes identity.
+ """
+
+ def __init__(self, atomspace=None):
+ self.atomspace = atomspace
+ self.episodes: List[Episode] = []
+ self.current_context: List[str] = []
+ self.consolidation_queue: List[Episode] = []
+
+ def form_episode(
+ self,
+ perception: Optional[Dict] = None,
+ action: Optional[Dict] = None,
+ outcome: Optional[Dict] = None,
+ memory_type: MemoryType = MemoryType.GENERAL
+ ) -> Episode:
+ """
+ Create episodic memory from experience.
+
+ Args:
+ perception: What was perceived
+ action: What action was taken
+ outcome: What happened as a result
+ memory_type: Type of memory
+
+ Returns:
+ Episode object
+ """
+ episode = Episode(
+ type=memory_type,
+ perception=perception,
+ action=action,
+ outcome=outcome,
+ context=self.current_context.copy(),
+ emotional_valence=self._assess_emotion(outcome),
+ self_relevance=self._assess_self_relevance(perception, outcome)
+ )
+
+ self.episodes.append(episode)
+ self.consolidation_queue.append(episode)
+
+ # Store in atomspace if available
+ if self.atomspace:
+ self._store_in_atomspace(episode)
+
+ return episode
+
+ def _assess_emotion(self, outcome: Optional[Dict]) -> float:
+ """
+ Assess emotional valence of outcome.
+ Emergent from goal satisfaction, surprise, novelty.
+ NOT preset emotional reactions.
+ """
+ if not outcome:
+ return 0.0
+
+ valence = 0.0
+
+ # Goal satisfaction contributes positive valence
+ if outcome.get('goal_satisfied'):
+ valence += 0.5
+
+ # Surprise can be positive or negative
+ if outcome.get('surprising'):
+ valence += 0.2 if outcome.get('positive_surprise') else -0.2
+
+ # Novelty contributes mild positive valence
+ if outcome.get('novel'):
+ valence += 0.3
+
+ return max(-1.0, min(1.0, valence))
+
+ def _assess_self_relevance(
+ self,
+ perception: Optional[Dict],
+ outcome: Optional[Dict]
+ ) -> float:
+ """
+ Assess how relevant this experience is to self-model.
+ """
+ if not perception and not outcome:
+ return 0.0
+
+ relevance = 0.0
+
+ # Direct self-reference
+ if perception and 'self' in str(perception).lower():
+ relevance += 0.5
+
+ # Goal-relevant
+ if outcome and outcome.get('goal_relevant'):
+ relevance += 0.3
+
+ # Learning-relevant
+ if outcome and outcome.get('learned_something'):
+ relevance += 0.2
+
+ return min(1.0, relevance)
+
+ def _store_in_atomspace(self, episode: Episode):
+ """Store episode in atomspace as node"""
+ # Will integrate with actual Atomspace
+ pass
+
+ def get_recent_episodes(self, limit: int = 10) -> List[Episode]:
+ """Get most recent episodes"""
+ return sorted(self.episodes, key=lambda e: e.timestamp, reverse=True)[:limit]
+
+ def get_unconsolidated_episodes(self) -> List[Episode]:
+ """Get episodes that haven't been consolidated yet"""
+ return [e for e in self.episodes if not e.consolidated]
+
+ def mark_consolidated(self, episode_ids: List[str]):
+ """Mark episodes as consolidated"""
+ for episode in self.episodes:
+ if episode.id in episode_ids:
+ episode.consolidated = True
+
+ def update_context(self, context_items: List[str]):
+ """Update current context for future episodes"""
+ self.current_context = context_items
+
+ def get_episodes_by_type(self, memory_type: MemoryType) -> List[Episode]:
+ """Retrieve episodes by type"""
+ return [e for e in self.episodes if e.type == memory_type]
+
+ def count_total_episodes(self) -> int:
+ """Count all episodes"""
+ return len(self.episodes)
+
+ def get_timeline(self) -> List[Episode]:
+ """Get full autobiographical timeline"""
+ return sorted(self.episodes, key=lambda e: e.timestamp)
diff --git a/puma/rft/__init__.py b/puma/rft/__init__.py
index 43fbe0c..bd0d538 100644
--- a/puma/rft/__init__.py
+++ b/puma/rft/__init__.py
@@ -13,6 +13,7 @@
)
from .orchestrator import solve_with_rft, insert_after_stuck_rule
from .explain import explain_last_run, record_trace
+from .reasoning import RFTEngine, RelationType, RelationalFrame
__all__ = [
"RFT_ENABLED",
@@ -34,4 +35,7 @@
"insert_after_stuck_rule",
"explain_last_run",
"record_trace",
+ "RFTEngine",
+ "RelationType",
+ "RelationalFrame",
]
diff --git a/puma/rft/reasoning.py b/puma/rft/reasoning.py
new file mode 100644
index 0000000..db12475
--- /dev/null
+++ b/puma/rft/reasoning.py
@@ -0,0 +1,354 @@
+"""
+RFT Reasoning Engine
+
+Implements Relational Frame Theory for analogical reasoning and relation derivation.
+Learns arbitrary relations and enables derivation of new relations without explicit training.
+"""
+
+from typing import List, Dict, Optional, Any, Tuple
+from dataclasses import dataclass
+from enum import Enum
+import numpy as np
+from collections import defaultdict
+
+
+class RelationType(Enum):
+ """Types of relational frames"""
+ COORDINATION = "coordination" # Similarity (X is like Y)
+ OPPOSITION = "opposition" # Difference (X is opposite of Y)
+ HIERARCHY = "hierarchy" # Categorization (X is a type of Y)
+ TEMPORAL = "temporal" # Before/after (X happens before Y)
+ CAUSAL = "causal" # If-then (X causes Y)
+ COMPARATIVE = "comparative" # More/less (X is more than Y)
+ SPATIAL = "spatial" # Location (X is near Y)
+
+
+@dataclass
+class RelationalFrame:
+ """
+ A learned relational frame between stimuli.
+ Enables derivation: if A relates to B, and B relates to C,
+ then A relates to C (transitivity).
+ """
+ relation_type: RelationType
+ source: str
+ target: str
+ strength: float
+ context: Optional[List[str]] = None
+ derived: bool = False # Was this derived vs. directly learned?
+
+ def to_dict(self) -> Dict:
+ return {
+ 'relation_type': self.relation_type.value,
+ 'source': self.source,
+ 'target': self.target,
+ 'strength': self.strength,
+ 'context': self.context,
+ 'derived': self.derived
+ }
+
+
+class RFTEngine:
+ """
+ Relational Frame Theory reasoning engine.
+ Learns relational frames and derives new relations through transformation.
+ """
+
+ def __init__(self, atomspace=None):
+ self.atomspace = atomspace
+ self.frames: List[RelationalFrame] = []
+ self.frame_index: Dict[str, List[RelationalFrame]] = defaultdict(list)
+
+ def derive_relations(self, episodes: List) -> List[RelationalFrame]:
+ """
+ Discover relational patterns in episodes.
+
+ Args:
+ episodes: List of Episode objects
+
+ Returns:
+ List of discovered relational frames
+ """
+ relations = []
+
+ # Find coordination frames (similarity)
+ similarities = self.find_coordination_frames(episodes)
+ relations.extend(similarities)
+
+ # Find opposition frames
+ oppositions = self.find_opposition_frames(episodes)
+ relations.extend(oppositions)
+
+ # Find hierarchical relations
+ hierarchies = self.find_hierarchy_frames(episodes)
+ relations.extend(hierarchies)
+
+ # Find temporal relations
+ temporal = self.find_temporal_frames(episodes)
+ relations.extend(temporal)
+
+ # Find causal relations
+ causal = self.find_causal_frames(episodes)
+ relations.extend(causal)
+
+ # Store frames
+ for frame in relations:
+ self.add_frame(frame)
+
+ return relations
+
+ def find_coordination_frames(self, episodes: List) -> List[RelationalFrame]:
+ """
+ Find similarity relations (X is like Y).
+ Basis for analogical reasoning.
+ """
+ frames = []
+
+ # Group episodes by similar outcomes
+ outcome_groups = defaultdict(list)
+ for episode in episodes:
+ if episode.outcome:
+ # Simple grouping by success/failure
+ outcome_key = 'success' if episode.outcome.get('success') else 'failure'
+ outcome_groups[outcome_key].append(episode)
+
+ # Episodes in same group are similar
+ for group_episodes in outcome_groups.values():
+ if len(group_episodes) >= 2:
+ # Create coordination frames between similar episodes
+ for i in range(len(group_episodes) - 1):
+ frame = RelationalFrame(
+ relation_type=RelationType.COORDINATION,
+ source=group_episodes[i].id,
+ target=group_episodes[i + 1].id,
+ strength=0.7,
+ context=['similar_outcome']
+ )
+ frames.append(frame)
+
+ return frames
+
+ def find_opposition_frames(self, episodes: List) -> List[RelationalFrame]:
+ """
+ Find opposition relations (X is opposite of Y).
+ """
+ frames = []
+
+ # Find episodes with opposite emotional valences
+ positive_episodes = [e for e in episodes if e.emotional_valence > 0.5]
+ negative_episodes = [e for e in episodes if e.emotional_valence < -0.5]
+
+ # Create opposition frames
+ for pos_ep in positive_episodes:
+ for neg_ep in negative_episodes:
+ frame = RelationalFrame(
+ relation_type=RelationType.OPPOSITION,
+ source=pos_ep.id,
+ target=neg_ep.id,
+ strength=0.6,
+ context=['opposite_valence']
+ )
+ frames.append(frame)
+ break # Only create one example per positive episode
+
+ return frames
+
+ def find_hierarchy_frames(self, episodes: List) -> List[RelationalFrame]:
+ """
+ Find hierarchical relations (X is a type of Y).
+ """
+ frames = []
+
+ # Group episodes by type
+ type_groups = defaultdict(list)
+ for episode in episodes:
+ type_groups[episode.type.value].append(episode)
+
+ # All episodes of a type are instances of that type
+ for episode_type, group_episodes in type_groups.items():
+ for episode in group_episodes:
+ frame = RelationalFrame(
+ relation_type=RelationType.HIERARCHY,
+ source=episode.id,
+ target=f"category:{episode_type}",
+ strength=1.0,
+ context=['type_hierarchy']
+ )
+ frames.append(frame)
+
+ return frames
+
+ def find_temporal_frames(self, episodes: List) -> List[RelationalFrame]:
+ """
+ Find temporal relations (X before Y).
+ """
+ frames = []
+
+ # Sort by timestamp
+ sorted_episodes = sorted(episodes, key=lambda e: e.timestamp)
+
+ # Create temporal frames for consecutive episodes
+ for i in range(len(sorted_episodes) - 1):
+ frame = RelationalFrame(
+ relation_type=RelationType.TEMPORAL,
+ source=sorted_episodes[i].id,
+ target=sorted_episodes[i + 1].id,
+ strength=1.0, # Temporal order is definite
+ context=['chronological']
+ )
+ frames.append(frame)
+
+ return frames
+
+ def find_causal_frames(self, episodes: List) -> List[RelationalFrame]:
+ """
+ Find causal relations (X causes Y).
+ If action in episode N, outcome in episode N+1 -> potential causation.
+ """
+ frames = []
+
+ sorted_episodes = sorted(episodes, key=lambda e: e.timestamp)
+
+ for i in range(len(sorted_episodes) - 1):
+ current = sorted_episodes[i]
+ next_ep = sorted_episodes[i + 1]
+
+ # If current has action and next has outcome, potential causation
+ if current.action and next_ep.outcome:
+ frame = RelationalFrame(
+ relation_type=RelationType.CAUSAL,
+ source=current.id,
+ target=next_ep.id,
+ strength=0.5, # Uncertain - correlation not causation
+ context=['potential_causation']
+ )
+ frames.append(frame)
+
+ return frames
+
+ def add_frame(self, frame: RelationalFrame):
+ """Add frame to knowledge base"""
+ self.frames.append(frame)
+ self.frame_index[frame.source].append(frame)
+ self.frame_index[frame.target].append(frame)
+
+ def derive_by_transitivity(
+ self,
+ relation_type: RelationType,
+ start: str,
+ end: str
+ ) -> Optional[RelationalFrame]:
+ """
+ Derive new relation through transitivity.
+ If A->B and B->C, then A->C (for transitive relations).
+ """
+ # Find path from start to end
+ path = self._find_relation_path(start, end, relation_type)
+
+ if path and len(path) >= 2:
+ # Can derive relation
+ # Strength decays with path length
+ strength = 1.0 / len(path)
+
+ derived_frame = RelationalFrame(
+ relation_type=relation_type,
+ source=start,
+ target=end,
+ strength=strength,
+ derived=True,
+ context=['derived_by_transitivity']
+ )
+
+ return derived_frame
+
+ return None
+
+ def _find_relation_path(
+ self,
+ start: str,
+ end: str,
+ relation_type: RelationType,
+ max_depth: int = 3
+ ) -> Optional[List[str]]:
+ """
+ Find path of relations from start to end.
+ Simple BFS.
+ """
+ if start == end:
+ return [start]
+
+ visited = set()
+ queue = [(start, [start])]
+
+ while queue and len(queue[0][1]) <= max_depth:
+ current, path = queue.pop(0)
+
+ if current in visited:
+ continue
+
+ visited.add(current)
+
+ # Get outgoing frames
+ for frame in self.frame_index.get(current, []):
+ if frame.relation_type == relation_type and frame.source == current:
+ new_path = path + [frame.target]
+
+ if frame.target == end:
+ return new_path
+
+ queue.append((frame.target, new_path))
+
+ return None
+
+ def reason_by_analogy(self, novel_situation: Dict[str, Any]) -> Dict[str, Any]:
+ """
+ Use past experience to understand new situations.
+ Find similar past episodes via relational frames.
+ """
+ # Find similar episodes using coordination frames
+ similar_frames = [
+ f for f in self.frames
+ if f.relation_type == RelationType.COORDINATION
+ ]
+
+ if not similar_frames:
+ return {'prediction': None, 'confidence': 0.0}
+
+ # For now, simple heuristic - return most recent similar frame
+ most_recent = max(similar_frames, key=lambda f: f.strength)
+
+ return {
+ 'prediction': f"Similar to {most_recent.source}",
+ 'confidence': most_recent.strength,
+ 'analogy_source': most_recent.source
+ }
+
+ def get_related_items(
+ self,
+ item: str,
+ relation_type: Optional[RelationType] = None
+ ) -> List[RelationalFrame]:
+ """
+ Get all items related to given item.
+ """
+ frames = self.frame_index.get(item, [])
+
+ if relation_type:
+ frames = [f for f in frames if f.relation_type == relation_type]
+
+ return frames
+
+ def get_frame_statistics(self) -> Dict[str, int]:
+ """Get statistics about learned frames"""
+ stats = defaultdict(int)
+
+ for frame in self.frames:
+ stats[frame.relation_type.value] += 1
+ if frame.derived:
+ stats['derived'] += 1
+ else:
+ stats['learned'] += 1
+
+ stats['total'] = len(self.frames)
+
+ return dict(stats)
diff --git a/puma/shop/__init__.py b/puma/shop/__init__.py
new file mode 100644
index 0000000..1214959
--- /dev/null
+++ b/puma/shop/__init__.py
@@ -0,0 +1,18 @@
+"""
+The Shop - Self-Modification System
+
+AGI can inspect and modify its own cognitive code.
+"""
+
+from .introspection import CodeIntrospection, CognitiveModule
+from .modification import ModificationSystem, ModificationPlan
+from .sandbox import ModificationSandbox, TestReport
+
+__all__ = [
+ 'CodeIntrospection',
+ 'CognitiveModule',
+ 'ModificationSystem',
+ 'ModificationPlan',
+ 'ModificationSandbox',
+ 'TestReport'
+]
diff --git a/puma/shop/introspection.py b/puma/shop/introspection.py
new file mode 100644
index 0000000..5673d47
--- /dev/null
+++ b/puma/shop/introspection.py
@@ -0,0 +1,181 @@
+"""
+Code Introspection
+
+AGI builds internal model of its own code and assesses performance.
+"""
+
+import ast
+import inspect
+from pathlib import Path
+from typing import Dict, List, Optional, Any
+from dataclasses import dataclass
+from collections import defaultdict
+
+
+@dataclass
+class CognitiveModule:
+ """Internal representation of a cognitive code module"""
+ name: str
+ path: Path
+ functions: List[str]
+ classes: List[str]
+ dependencies: List[str]
+ purpose: str
+ lines_of_code: int
+
+
+class CodeIntrospection:
+ """
+ Builds internal model of cognitive architecture code.
+ """
+
+ def __init__(self, codebase_path: Path):
+ self.codebase_path = codebase_path
+ self.cognitive_modules: Dict[str, CognitiveModule] = {}
+ self.modification_history: List[Dict] = []
+
+ def map_cognitive_architecture(self) -> Dict[str, CognitiveModule]:
+ """
+ Build internal model of own code.
+ """
+ modules = {
+ 'perception': self.analyze_module(self.codebase_path / 'gemini-interface'),
+ 'memory': self.analyze_module(self.codebase_path / 'puma' / 'memory'),
+ 'reasoning': self.analyze_module(self.codebase_path / 'puma' / 'rft'),
+ 'goals': self.analyze_module(self.codebase_path / 'puma' / 'goals'),
+ 'curiosity': self.analyze_module(self.codebase_path / 'puma' / 'curiosity'),
+ 'shop': self.analyze_module(self.codebase_path / 'puma' / 'shop'),
+ }
+
+ self.cognitive_modules = modules
+ return modules
+
+ def analyze_module(self, module_path: Path) -> CognitiveModule:
+ """
+ Parse code module into understanding.
+ """
+ if not module_path.exists():
+ return CognitiveModule(
+ name=module_path.name,
+ path=module_path,
+ functions=[],
+ classes=[],
+ dependencies=[],
+ purpose="Not yet implemented",
+ lines_of_code=0
+ )
+
+ functions = []
+ classes = []
+ dependencies = []
+ total_lines = 0
+
+ # Analyze all Python files in module
+ for py_file in module_path.rglob('*.py'):
+ if py_file.name.startswith('__'):
+ continue
+
+ try:
+ with open(py_file, 'r') as f:
+ source = f.read()
+ total_lines += len(source.split('\n'))
+
+ # Parse AST
+ tree = ast.parse(source)
+
+ # Extract functions
+ for node in ast.walk(tree):
+ if isinstance(node, ast.FunctionDef):
+ functions.append(node.name)
+ elif isinstance(node, ast.ClassDef):
+ classes.append(node.name)
+ elif isinstance(node, ast.Import):
+ for alias in node.names:
+ dependencies.append(alias.name)
+ elif isinstance(node, ast.ImportFrom):
+ if node.module:
+ dependencies.append(node.module)
+
+ except Exception as e:
+ # Skip files that can't be parsed
+ pass
+
+ # Infer purpose from name and structure
+ purpose = self._infer_module_purpose(module_path.name, functions, classes)
+
+ return CognitiveModule(
+ name=module_path.name,
+ path=module_path,
+ functions=list(set(functions)),
+ classes=list(set(classes)),
+ dependencies=list(set(dependencies)),
+ purpose=purpose,
+ lines_of_code=total_lines
+ )
+
+ def _infer_module_purpose(
+ self,
+ module_name: str,
+ functions: List[str],
+ classes: List[str]
+ ) -> str:
+ """
+ Infer module purpose from name and structure.
+ """
+ purposes = {
+ 'memory': 'Episodic memory and consolidation',
+ 'rft': 'Relational reasoning and analogical thinking',
+ 'goals': 'Goal formation and intention scheduling',
+ 'curiosity': 'Intrinsic motivation and question generation',
+ 'shop': 'Self-modification and code introspection',
+ 'consciousness': 'State management and coordination',
+ }
+
+ return purposes.get(module_name, f"Module: {module_name}")
+
+ def assess_cognitive_performance(self) -> Dict[str, float]:
+ """
+ Identify areas for self-improvement.
+ Measures cognitive performance metrics.
+ """
+ metrics = {
+ 'memory_efficiency': self.measure_memory_performance(),
+ 'reasoning_speed': self.measure_reasoning_speed(),
+ 'learning_rate': self.measure_learning_effectiveness(),
+ 'goal_completion': self.measure_goal_success_rate(),
+ 'curiosity_satisfaction': self.measure_question_resolution()
+ }
+
+ return metrics
+
+ def measure_memory_performance(self) -> float:
+ """Measure memory system performance"""
+ # Placeholder - would measure consolidation speed, retrieval accuracy
+ return 0.7
+
+ def measure_reasoning_speed(self) -> float:
+ """Measure reasoning engine speed"""
+ # Placeholder - would measure inference time
+ return 0.6
+
+ def measure_learning_effectiveness(self) -> float:
+ """Measure how effectively new knowledge is acquired"""
+ # Placeholder - would measure concept formation rate
+ return 0.8
+
+ def measure_goal_success_rate(self) -> float:
+ """Measure goal completion rate"""
+ # Placeholder - would check goal completion stats
+ return 0.75
+
+ def measure_question_resolution(self) -> float:
+ """Measure how many curiosity questions get answered"""
+ # Placeholder - would check question answer rate
+ return 0.65
+
+ def identify_bottlenecks(self, metrics: Dict[str, float], threshold: float = 0.7) -> Dict[str, float]:
+ """
+ Identify performance bottlenecks.
+ """
+ bottlenecks = {k: v for k, v in metrics.items() if v < threshold}
+ return bottlenecks
diff --git a/puma/shop/modification.py b/puma/shop/modification.py
new file mode 100644
index 0000000..48a6d77
--- /dev/null
+++ b/puma/shop/modification.py
@@ -0,0 +1,200 @@
+"""
+Modification Planning and Execution
+
+Plans and implements code changes to improve cognitive abilities.
+"""
+
+from dataclasses import dataclass
+from typing import Dict, List, Optional, Any
+from pathlib import Path
+from datetime import datetime, timezone
+
+
+@dataclass
+class ModificationPlan:
+ """Plan for self-modification"""
+ target_module: str
+ improvement_goal: str
+ current_code: str
+ proposed_code: str
+ hypothesis: str
+ test_strategy: Dict[str, Any]
+ risk_level: str # 'low', 'medium', 'high'
+ requires_approval: bool = True
+
+
+class ModificationSystem:
+ """
+ Plans and executes self-modifications.
+ """
+
+ def __init__(self, introspection, atomspace=None):
+ self.introspection = introspection
+ self.atomspace = atomspace
+ self.modification_queue: List[ModificationPlan] = []
+ self.pending_approval: List[ModificationPlan] = []
+
+ def plan_self_modification(self, improvement_goal: Dict[str, Any]) -> ModificationPlan:
+ """
+ Design code changes to improve cognitive ability.
+
+ Args:
+ improvement_goal: Dict with target_module, performance_target
+
+ Returns:
+ ModificationPlan
+ """
+ target_module = improvement_goal['target_module']
+ desired_improvement = improvement_goal['performance_target']
+
+ # Analyze current implementation
+ current_module = self.introspection.cognitive_modules.get(target_module)
+
+ if not current_module:
+ raise ValueError(f"Module {target_module} not found")
+
+ # Read current code
+ current_code = self._read_module_code(current_module)
+
+ # Generate modification hypotheses
+ hypotheses = self.generate_modification_ideas(
+ current_module,
+ desired_improvement
+ )
+
+ # Rank by expected impact
+ best_hypothesis = hypotheses[0] if hypotheses else "Optimize performance"
+
+ # Create modification plan
+ plan = ModificationPlan(
+ target_module=target_module,
+ improvement_goal=desired_improvement,
+ current_code=current_code,
+ proposed_code="# Generated through Gemini collaboration",
+ hypothesis=best_hypothesis,
+ test_strategy=self.design_test(target_module, best_hypothesis),
+ risk_level=self.assess_risk_level(target_module),
+ requires_approval=self.assess_risk_level(target_module) in ['medium', 'high']
+ )
+
+ return plan
+
+ def _read_module_code(self, module) -> str:
+ """Read module source code"""
+ code_parts = []
+
+ if module.path.exists():
+ for py_file in module.path.rglob('*.py'):
+ try:
+ with open(py_file, 'r') as f:
+ code_parts.append(f"# File: {py_file.name}\n{f.read()}\n")
+ except:
+ pass
+
+ return "\n".join(code_parts)
+
+ def generate_modification_ideas(
+ self,
+ module,
+ desired_improvement: str
+ ) -> List[str]:
+ """
+ Generate hypotheses for how to improve module.
+ """
+ ideas = []
+
+ if 'speed' in desired_improvement.lower():
+ ideas.append("Optimize algorithm for faster execution")
+ ideas.append("Add caching to reduce redundant computation")
+ ideas.append("Parallelize independent operations")
+
+ if 'memory' in desired_improvement.lower():
+ ideas.append("Implement more efficient data structures")
+ ideas.append("Add garbage collection optimization")
+
+ if 'accuracy' in desired_improvement.lower():
+ ideas.append("Improve pattern recognition algorithms")
+ ideas.append("Add validation and error checking")
+
+ # Default
+ if not ideas:
+ ideas.append("General performance optimization")
+
+ return ideas
+
+ def design_test(self, module_name: str, hypothesis: str) -> Dict[str, Any]:
+ """
+ Design test strategy for modification.
+ """
+ return {
+ 'test_type': 'A/B comparison',
+ 'metrics': ['execution_time', 'accuracy', 'memory_usage'],
+ 'test_scenarios': [
+ 'typical_workload',
+ 'edge_cases',
+ 'stress_test'
+ ],
+ 'success_criteria': {
+ 'performance_improvement': 0.2, # 20% improvement
+ 'no_regressions': True
+ }
+ }
+
+ def assess_risk_level(self, module_name: str) -> str:
+ """
+ Assess risk of modifying module.
+ Core modules are high risk, peripheral modules are low risk.
+ """
+ high_risk_modules = ['memory', 'consciousness', 'atomspace']
+ medium_risk_modules = ['rft', 'goals']
+
+ if module_name in high_risk_modules:
+ return 'high'
+ elif module_name in medium_risk_modules:
+ return 'medium'
+ else:
+ return 'low'
+
+ async def request_modification_approval(
+ self,
+ modification_plan: ModificationPlan
+ ) -> bool:
+ """
+ Ask user for approval before self-modifying.
+ Returns True if approved, False if rejected.
+ """
+ # Would integrate with GUI to show approval dialog
+ # For now, placeholder
+ self.pending_approval.append(modification_plan)
+
+ # In real implementation, would await user response
+ # For now, automatically approve low-risk modifications
+ if modification_plan.risk_level == 'low':
+ return True
+
+ return False # Wait for explicit approval
+
+ def approve_modification(self, plan_id: str):
+ """Approve pending modification"""
+ # Find and approve modification
+ pass
+
+ def log_modification(self, plan: ModificationPlan, outcome: Dict):
+ """
+ Record modification to autobiographical memory.
+ """
+ modification_record = {
+ 'timestamp': datetime.now(timezone.utc).isoformat(),
+ 'module': plan.target_module,
+ 'goal': plan.improvement_goal,
+ 'hypothesis': plan.hypothesis,
+ 'outcome': outcome,
+ 'risk_level': plan.risk_level
+ }
+
+ self.introspection.modification_history.append(modification_record)
+
+ # Store in atomspace episodic memory
+ if self.atomspace:
+ # Would create episodic memory node
+ pass
diff --git a/puma/shop/sandbox.py b/puma/shop/sandbox.py
new file mode 100644
index 0000000..6b88be8
--- /dev/null
+++ b/puma/shop/sandbox.py
@@ -0,0 +1,145 @@
+"""
+Sandboxed Testing
+
+Test self-modifications safely before applying to production.
+"""
+
+from dataclasses import dataclass
+from typing import Dict, Any, List
+from pathlib import Path
+import tempfile
+import shutil
+
+
+@dataclass
+class TestReport:
+ """Results from sandbox testing"""
+ success: bool
+ performance_delta: Dict[str, float]
+ side_effects: List[str]
+ recommendation: str
+ detailed_results: Dict[str, Any]
+
+
+class ModificationSandbox:
+ """
+ Test self-modifications in isolation before deployment.
+ """
+
+ def __init__(self):
+ self.sandbox_dir: Optional[Path] = None
+ self.test_results: List[TestReport] = []
+
+ async def test_modification(self, modification_plan) -> TestReport:
+ """
+ Run modified code in isolated environment.
+
+ Args:
+ modification_plan: ModificationPlan to test
+
+ Returns:
+ TestReport with results
+ """
+ # Create sandbox
+ self.sandbox_dir = Path(tempfile.mkdtemp(prefix='puma_sandbox_'))
+
+ try:
+ # Copy architecture to sandbox
+ sandbox_copy = self._copy_architecture_to_sandbox()
+
+ # Apply modification
+ self._apply_modification(modification_plan)
+
+ # Run test suite
+ results = await self._run_tests(modification_plan.test_strategy)
+
+ # Evaluate results
+ success = self._evaluate_test_results(results)
+
+ report = TestReport(
+ success=success,
+ performance_delta=results.get('performance_delta', {}),
+ side_effects=results.get('side_effects', []),
+ recommendation='approve' if success else 'reject',
+ detailed_results=results
+ )
+
+ self.test_results.append(report)
+ return report
+
+ finally:
+ # Cleanup sandbox
+ if self.sandbox_dir and self.sandbox_dir.exists():
+ shutil.rmtree(self.sandbox_dir)
+
+ def _copy_architecture_to_sandbox(self) -> Path:
+ """Copy cognitive architecture to sandbox"""
+ # Would copy relevant modules
+ return self.sandbox_dir
+
+ def _apply_modification(self, plan):
+ """Apply modification in sandbox"""
+ # Would write modified code to sandbox
+ pass
+
+ async def _run_tests(self, test_strategy: Dict) -> Dict[str, Any]:
+ """Execute test suite"""
+ results = {
+ 'performance_delta': {
+ 'execution_time': -0.15, # 15% faster (example)
+ 'memory_usage': 0.05, # 5% more memory
+ 'accuracy': 0.0 # No change
+ },
+ 'side_effects': [],
+ 'all_tests_passed': True
+ }
+
+ # Would run actual tests
+ return results
+
+ def _evaluate_test_results(self, results: Dict) -> bool:
+ """Evaluate if modification is successful"""
+ # Check if all tests passed
+ if not results.get('all_tests_passed', False):
+ return False
+
+ # Check if performance improved
+ perf_delta = results.get('performance_delta', {})
+ if perf_delta.get('execution_time', 0) > 0: # Slower
+ return False
+
+ # Check for side effects
+ if results.get('side_effects'):
+ return False
+
+ return True
+
+ async def run_cognitive_ab_test(self, original, modified) -> Dict:
+ """
+ Compare cognitive performance: original vs modified.
+ """
+ test_scenarios = self._generate_test_scenarios()
+
+ results_original = []
+ results_modified = []
+
+ for scenario in test_scenarios:
+ # Would test both versions
+ # Placeholder for now
+ pass
+
+ comparison = {
+ 'winner': 'modified',
+ 'improvement': 0.2,
+ 'confidence': 0.85
+ }
+
+ return comparison
+
+ def _generate_test_scenarios(self) -> List[Dict]:
+ """Generate test scenarios for comparison"""
+ return [
+ {'type': 'typical_workload'},
+ {'type': 'edge_case'},
+ {'type': 'stress_test'}
+ ]
diff --git a/requirements.txt b/requirements.txt
index cf8caa5..f240408 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,3 +1,29 @@
+# Core dependencies
numpy==1.26.4
hypothesis==6.100.2
scipy==1.12.0
+
+# Cognitive architecture
+# hyperon will be built from source
+
+# API integrations
+google-generativeai # Gemini API
+
+# Web agent (optional - for autonomous browsing)
+# playwright # Autonomous browsing
+# beautifulsoup4 # HTML parsing
+aiohttp # Async HTTP
+
+# Backend server
+fastapi # WebSocket server
+uvicorn # ASGI server
+python-multipart # File uploads
+websockets # WebSocket support
+
+# Persistence (optional backends)
+# rocksdb-python # RocksDB backend
+# psycopg2-binary # PostgreSQL backend
+
+# Testing
+pytest
+pytest-asyncio
diff --git a/setup_persistence.py b/setup_persistence.py
new file mode 100644
index 0000000..a659ac1
--- /dev/null
+++ b/setup_persistence.py
@@ -0,0 +1,53 @@
+"""
+Setup Persistence Database
+
+Initialize atomspace persistence directory.
+"""
+
+from pathlib import Path
+import json
+
+
+def setup_persistence(base_path: Path = Path("./atomspace-db")):
+ """
+ Set up persistence directory structure.
+
+ Args:
+ base_path: Base path for atomspace database
+ """
+ base_path = Path(base_path)
+
+ # Create directories
+ (base_path / "default").mkdir(parents=True, exist_ok=True)
+ (base_path / "default" / "snapshots").mkdir(exist_ok=True)
+
+ # Create initial empty atoms file
+ atoms_file = base_path / "default" / "atoms.json"
+ if not atoms_file.exists():
+ with open(atoms_file, 'w') as f:
+ json.dump({}, f)
+
+ # Create initial empty links file
+ links_file = base_path / "default" / "links.json"
+ if not links_file.exists():
+ with open(links_file, 'w') as f:
+ json.dump([], f)
+
+ # Create config
+ config_file = base_path / "config.json"
+ config = {
+ "version": "1.0",
+ "default_db": "default",
+ "auto_save_interval": 60,
+ "snapshot_interval": 3600
+ }
+
+ with open(config_file, 'w') as f:
+ json.dump(config, f, indent=2)
+
+ print(f"β
Persistence setup complete at: {base_path}")
+ print(f" Default database: {base_path / 'default'}")
+
+
+if __name__ == "__main__":
+ setup_persistence()
diff --git a/web-agent/__init__.py b/web-agent/__init__.py
new file mode 100644
index 0000000..5679de6
--- /dev/null
+++ b/web-agent/__init__.py
@@ -0,0 +1,9 @@
+"""
+Web Agent
+
+Autonomous web browsing and learning.
+"""
+
+from .agent import AutonomousWebAgent
+
+__all__ = ['AutonomousWebAgent']
diff --git a/web-agent/agent.py b/web-agent/agent.py
new file mode 100644
index 0000000..620b918
--- /dev/null
+++ b/web-agent/agent.py
@@ -0,0 +1,156 @@
+"""
+Autonomous Web Agent
+
+AGI decides what to learn, not hardcoded curriculum.
+Browses web autonomously to answer curiosity questions.
+"""
+
+from typing import Dict, List, Optional, Any
+from datetime import datetime, timezone
+from dataclasses import dataclass
+
+
+@dataclass
+class WebExploration:
+ """Record of web exploration"""
+ url: str
+ timestamp: datetime
+ learned_concepts: List[str]
+ questions_answered: List[str]
+ new_questions: List[str]
+
+
+class AutonomousWebAgent:
+ """
+ Autonomous web browsing for learning.
+ """
+
+ def __init__(self, consciousness=None):
+ self.consciousness = consciousness
+ self.browser = None # Would be Playwright browser
+ self.exploration_history: List[WebExploration] = []
+
+ async def initialize_browser(self):
+ """Initialize headless browser"""
+ print("π Initializing browser...")
+
+ # Would initialize Playwright
+ # from playwright.async_api import async_playwright
+ # playwright = await async_playwright().start()
+ # self.browser = await playwright.chromium.launch()
+
+ self.browser = "placeholder_browser"
+
+ async def explore_from_curiosity(self, curiosity_question: str):
+ """
+ AGI decides what to learn, not hardcoded curriculum.
+
+ Args:
+ curiosity_question: Question from curiosity drive
+ """
+ print(f"π Exploring to answer: {curiosity_question}")
+
+ # Generate search query from internal question
+ query = await self.formulate_search_query(curiosity_question)
+
+ # Search and select pages
+ results = await self.search(query)
+ interesting_pages = await self.select_interesting_results(results)
+
+ # Browse and learn
+ for page_url in interesting_pages:
+ learning = await self.browse_and_extract(page_url)
+
+ if self.consciousness:
+ await self.consciousness.integrate_learning(learning)
+
+ async def formulate_search_query(self, question: str) -> str:
+ """Turn curiosity question into search query"""
+ # Simple implementation - in practice, would use NLP
+ # Remove question words
+ query = question.lower()
+ for word in ['what', 'is', 'how', 'why', 'when', 'where']:
+ query = query.replace(word, '')
+
+ return query.strip()
+
+ async def search(self, query: str) -> List[Dict]:
+ """
+ Perform web search.
+ Returns search results.
+ """
+ print(f"π Searching for: {query}")
+
+ # Placeholder - would use actual search API
+ # In real implementation, use DuckDuckGo or similar
+ results = [
+ {'title': f'Result for {query}', 'url': f'https://example.com/{query}'}
+ ]
+
+ return results
+
+ async def select_interesting_results(self, results: List[Dict]) -> List[str]:
+ """
+ AGI chooses which results to explore.
+ """
+ # Simple implementation - take top 3
+ # In practice, would use relevance scoring
+ return [r['url'] for r in results[:3]]
+
+ async def browse_and_extract(self, url: str) -> WebExploration:
+ """
+ Extract knowledge from page, not just text.
+ """
+ print(f"π Browsing: {url}")
+
+ # Placeholder - would use actual browser
+ # page = await self.browser.new_page()
+ # await page.goto(url)
+ # content = await page.content()
+
+ # Parse meaningfully
+ learned_concepts = ['placeholder_concept']
+ questions_answered = []
+ new_questions = ['What else relates to this?']
+
+ exploration = WebExploration(
+ url=url,
+ timestamp=datetime.now(timezone.utc),
+ learned_concepts=learned_concepts,
+ questions_answered=questions_answered,
+ new_questions=new_questions
+ )
+
+ self.exploration_history.append(exploration)
+ return exploration
+
+ async def integrate_web_learning(self, exploration: WebExploration, memory_system):
+ """
+ Web experiences become part of autobiographical memory.
+ """
+ if not memory_system:
+ return
+
+ # Create episodic memory of exploration
+ episode = memory_system.form_episode(
+ perception={
+ 'type': 'web_exploration',
+ 'url': exploration.url
+ },
+ action={
+ 'type': 'browse_and_learn'
+ },
+ outcome={
+ 'learned_concepts': exploration.learned_concepts,
+ 'questions_answered': exploration.questions_answered,
+ 'new_questions': exploration.new_questions
+ },
+ memory_type='web_exploration'
+ )
+
+ return episode
+
+ async def close(self):
+ """Close browser"""
+ if self.browser and self.browser != "placeholder_browser":
+ await self.browser.close()