# Project export: Truman

This document was generated by HackStack to give an AI agent context about a hackathon project. Sections are labeled with their provenance; content marked as truncated was cut to keep this document small.

## Project metadata

- Hackathon: TreeHacks 2026
- Tagline: If the amount of time we spend with AI will only increase, why not make those interactions truly meaningful?
- Devpost: https://devpost.com/software/truman
- GitHub: https://github.com/jack-herrmann/truman
- Video: https://www.youtube.com/embed/uyHDbkDQwYQ?enablejsapi=1&hl=en_US&rel=0&start=&version=3&wmode=transparent
- Team: 2 GitHub contributor(s) — Jack Herrmann (16 commits), Claude (1 commits)

## Devpost submission (written by the team)

No Devpost description available.

## README (from the GitHub repository)

# The Story — Agentic Personalities

We'll all spend more and more time interacting with AI. Why not make those interactions genuinely **meaningful**?

This project creates **individual** and **consistent** artificial personalities. Not persona prompts or character sheets: deep, learned, psychologically grounded agents that behave consistently across contexts and distinctly from each other.

> **Heads up — this project makes heavy use of LLM API calls** (creating priors, generating sandbox environments, decoding personalities). Free-tier quotas (Gemini, Groq) **will** be exhausted during normal use. Before you set up any API keys, see what the system actually produces by opening the pre-saved demo below.

---

## See It First (no install, no API key, nothing)

Remember when your mum used to hang your drawing on the fridge? Psychologists use projective drawing tasks to generate hypotheses about personality development. It's simple: *draw yourself.*

We can compare what happens when children draw themselves, when current LLMs draw themselves, and when our agentic personalities draw themselves.

Open **[`demos/draw_yourself_presaved.html`](demos/draw_yourself_presaved.html)** in your browser.

You'll see four personalities — each drawing a self-portrait in real time. The Stoic draws a plain, centered figure with no embellishment. The Anxious one produces a small, hunched form surrounded by scribbled-out false starts. The Romantic fills the canvas with flowing lines, flowers, and stars. The Pragmatist sketches a neatly dressed figure with glasses and polished shoes.

**This is not a mockup.** These drawings are a materialization of real output from the personality pipeline. Each of the four agents was created through the full process — latent vector sampling, LLM-based personality generation, kernel-to-drawing-spec mapping — and the resulting drawing commands were transcribed into the HTML file so you can see exactly what the system produces without spending a single API call. The only manual additions were to make the HTML look a bit nicer. The personalities, their traits, and the drawing decisions are all real; the HTML is just a recording.

Pretty cool! But why? Beyond the proof-of-concept, this becomes a platform: accurately simulated populations for enterprise testing, highly personalized agents — for example, in old age, meaningful connections are one of the most important health indicators — and any application where individual, consistent personalities matter.

---

## Try It Yourself (30 seconds, no API key)

```bash
pip install -r requirements.txt
python3 draw_yourself.py --kernel data/checkpoints/test_kernel.json --no-window --save out.png
```

Open `out.png` — you should see a self-portrait drawn entirely from a saved personality kernel (no LLM needed at draw time). The figure's posture, mood, position, and whether it carries an umbrella all come from the personality's latent vector, stress profile, and emotional baseline.

> If you get `No module named 'pydantic'` or similar, make sure you ran `pip install -r requirements.txt` first (use a virtual environment if your system Python is managed).

---

## Create Your Own Personalities (needs a free API key)

Get a free API key from [Google AI Studio](https://aistudio.google.com/apikey) or [Groq Console](https://console.groq.com), then:

```bash
export GEMINI_API_KEY="your-key-here"    # or GROQ_API_KEY for Groq

# Draw 4 new personalities (creates them via LLM, then draws)
python3 draw_yourself.py --save my_drawing.png

# Compare 3 personalities side-by-side across moral dilemmas
python3 demo.py

# Chat with the shipped test personality
python3 demos/conversation.py --kernel data/checkpoints/test_kernel.json
```

**Free-tier rate limits:** Gemini free tier has a daily request quota. If you hit `Rate limit exceeded`, wait a minute and retry, or switch to Groq (set `GROQ_API_KEY` and change `llm.provider` to `groq` in `config.yaml`).

---

## Why

The common denominator of deep personalities, stripped of ideological bias, is that they are **individual** and **consistent**. This project is an engine for such personalities: a high-dimensional latent space of Gaussians (mean = trait direction, variance = stability), a **prior** from classic literature, reinforcement learning in a **Truman Show**-style sandbox, and rewards for **consistency** and **individuality**. At test time we do latent-variable inference via Thompson sampling.

## Architecture

```
Novels (e.g. Gutenberg; scraping via Bright Data when needed)
    |
    v
CharacterExtractor (LLM: e.g. NVIDIA Nemotron) --> CharacterDataset --> Embeddings
                                                                        |
                                                                        v
                                                                  PersonalityVAE
                                                                        |
                                                         +--------------+--------------+
                                                         v                              v
                                                  z (latent mean)                 s (per-dim stability)
                                                         |                              |
                                                         +--------------+--------------+
                                                                        |
                                                                        v
                                                         PersonalityDecoder (LLM)
                                                                        |
                                                                        v
                                                                  PersonalityKernel
                                                                        |
                                                         +--------------+--------------+
                                                         v              v              v
                                                  PromptBuilder   StateManager   EpisodicMemory
                                                         |              |              |
                                                         +--------------+--------------+
                                                                        |
                                                                        v
                                                                  PersonalityAgent
                                                                        |
                                                                        v
                                                             TrumanWorld (sandbox)
                                                   LLM-generated non-deterministic social situations
                                                                        |
                                                         +--------------+--------------+
                                                         v              v              v
                                                 CoherenceReward  IndividualityReward  Probes
                                                         |              |              |
                                                         +--------------+--------------+
                                                                        |
                                                                        v
                                                                  KernelOptimizer
                                                             (evolutionary search)
```

## Core Concepts

**Personality as latent Gaussians.** Each personality is a point in a learned latent space: **mean** (z) encodes trait direction, **variance** (sigma) encodes per-dimension stabili

[README truncated for size]

## Detected evidence (automated analysis)

Indexed codebase: 62 recognized source files, 222 KB.
- Anthropic (technology) — detected in the code
- HTML (language) — detected in the code
- OpenAI (technology) — detected in the code
- Python (language) — detected in the code
- PyTorch (technology) — detected in the code
- AI coding agent: Claude Code — evidence: commit authorship or trailers
- AI coding agent: Cursor — evidence: commit authorship or trailers

## Codebase structure (from repository index)

### Files (105 of 105)

```
.gitignore
config.yaml
data/characters/a_tale_of_two_cities_sydney_carton.json
data/characters/anna_karenina_anna_karenina.json
data/characters/crime_and_punishment_raskolnikov.json
data/characters/david_copperfield_david_copperfield.json
data/characters/don_quixote_don_quixote.json
data/characters/dracula_count_dracula.json
data/characters/embeddings.npy
data/characters/emma_emma_woodhouse.json
data/characters/frankenstein_the_creature.json
data/characters/frankenstein_victor_frankenstein.json
data/characters/great_expectations_pip.json
data/characters/heart_of_darkness_kurtz.json
data/characters/heart_of_darkness_marlow.json
data/characters/jane_eyre_jane_eyre.json
data/characters/les_miserables_jean_valjean.json
data/characters/madame_bovary_emma_bovary.json
data/characters/middlemarch_dorothea_brooke.json
data/characters/moby_dick_captain_ahab.json
data/characters/notes_from_underground_the_underground_man.json
data/characters/pride_and_prejudice_elizabeth_bennet.json
data/characters/sense_and_sensibility_elinor_dashwood.json
data/characters/the_adventures_of_huckleberry_finn_huckleberry_finn.json
data/characters/the_brothers_karamazov_dmitri_karamazov.json
data/characters/the_brothers_karamazov_ivan_karamazov.json
data/characters/the_count_of_monte_cristo_edmond_dantes.json
data/characters/the_great_gatsby_jay_gatsby.json
data/characters/the_odyssey_odysseus.json
data/characters/the_picture_of_dorian_gray_dorian_gray.json
data/characters/the_scarlet_letter_hester_prynne.json
data/characters/the_trial_josef_k.json
data/characters/war_and_peace_prince_andrei.json
data/characters/wuthering_heights_heathcliff.json
data/checkpoints/test_kernel.json
data/checkpoints/vae.pt
demo.py
demos/conversation.py
demos/draw_yourself_presaved.html
demos/interpolate.py
draw_yourself.py
intuition/__init__.py
intuition/agent/__init__.py
intuition/agent/agent.py
intuition/agent/prompt_builder.py
intuition/agent/state.py
intuition/api.py
intuition/core/__init__.py
intuition/core/kernel.py
intuition/core/memory.py
intuition/core/traces.py
intuition/corpus/__init__.py
intuition/corpus/dataset.py
intuition/corpus/extractor.py
intuition/corpus/fetcher.py
intuition/corpus/gutenberg.py
intuition/environment/__init__.py
intuition/environment/curriculum.py
intuition/environment/narrator.py
intuition/environment/situations.py
intuition/environment/world.py
intuition/evaluation/__init__.py
intuition/evaluation/consistency.py
intuition/evaluation/individuality.py
intuition/evaluation/probes.py
intuition/evaluation/report.py
intuition/latent/__init__.py
intuition/latent/decoder.py
intuition/latent/space.py
intuition/latent/vae.py
intuition/llm/__init__.py
intuition/llm/client.py
intuition/llm/embeddings.py
intuition/llm/templates.py
intuition/training/__init__.py
intuition/training/discriminator.py
intuition/training/optimizer.py
intuition/training/rewards.py
intuition/training/trainer.py
person_in_rain.py
prompts/agent_system.j2
prompts/decode_kernel.j2
prompts/extract_character.j2
prompts/narrator_system.j2
prompts/situation_generate.j2
prompts/synthesize_character.j2
pytest.ini
README.md
requirements.txt
scripts/download_corpus.py
scripts/evaluate.py
scripts/extract_characters.py
scripts/generate_seed_data.py
scripts/train_personalities.py
scripts/train_vae.py
tests/conftest.py
tests/test_agent.py
tests/test_api.py
tests/test_core.py
tests/test_dataset.py
tests/test_demo_smoke.py
tests/test_fetcher.py
tests/test_latent.py
tests/test_llm_client.py
tests/test_rewards.py
```

### Dependencies

- requirements.txt: anthropic@>=0.25, brightdata-sdk@>=2.0, google-genai@>=1.0, httpx@>=0.24, jinja2@>=3.1, numpy@>=1.24, openai@>=1.0, pydantic@>=2.0, pygame@>=2.0, pytest@>=7.0, pytest-asyncio@>=0.21, pyyaml@>=6.0, rich@>=13.0, scikit-learn@>=1.3, torch@>=2.0

### Recent commits (newest first)

- final for now
- Merge pull request #3 from jack-herrmann/claude/full-review-testing-chI2l
- Fix test suite (50/50 passing), bugs, and data quality issues
- final for now
- polish
- polish
- polish
- polish
- hotfix
- hotfix
- hotfix
- polished descriptive files
- polished demos
- presentation
- architecture polishing
- tests and sims
- character cache
- character cache
- Implement Intuition AI Personality Engine
- base structure

## Key source files (fetched from GitHub, selected and truncated for size)

### requirements.txt

```
# Intuition — Agentic Personalities (dependencies)

# Core
pydantic>=2.0
pyyaml>=6.0
jinja2>=3.1
numpy>=1.24

# ML
torch>=2.0
scikit-learn>=1.3

# LLM Providers
anthropic>=0.25
openai>=1.0
google-genai>=1.0

# HTTP (for Gutenberg downloads and NeMo NIM client)
httpx>=0.24

# Scraping (optional — for Bright Data proxy/anti-bot when needed)
brightdata-sdk>=2.0

# CLI formatting
rich>=13.0

# Visual demo (draw_yourself.py)
pygame>=2.0

# Testing
pytest>=7.0
pytest-asyncio>=0.21

```

### person_in_rain.py

```python
#!/usr/bin/env python3
"""Launcher for the "draw yourself" demo (personalities draw a self-portrait).

This file is kept for backward compatibility. The actual demo lives in
draw_yourself.py. Run either:

  python draw_yourself.py --save out.png
  python person_in_rain.py --save out.png
"""
from draw_yourself import main

if __name__ == "__main__":
    main()

```

### config.yaml

```yaml
# Intuition — Agentic Personalities configuration
# LLM used for: prior (character extraction), PersonalityDecoder, Truman Show sandbox.
# Set provider below and the matching env var (e.g. GEMINI_API_KEY, NIM_PROXY_BASE_URL).

# ── LLM Provider ──────────────────────────────────────────────────────
# Free: gemini (GEMINI_API_KEY), groq (GROQ_API_KEY)
# NVIDIA NeMo / NIM (extraction + Truman Show): provider: nemo, NIM_PROXY_BASE_URL or llm.base_url
# Paid: anthropic (ANTHROPIC_API_KEY), openai (OPENAI_API_KEY)
llm:
  provider: gemini
  model: gemini-2.0-flash
  max_tokens: 4096
  temperature: 0.7
  # base_url: http://localhost:8000   # for provider: nemo
  # nemo:
  #   base_url: http://localhost:8000

embeddings:
  provider: local  # "openai" for production, "local" for dev (free, no API key)
  model: text-embedding-3-small
  dimension: 512

latent:
  dimension: 32
  hidden_dims: [512, 256]
  epochs: 200
  learning_rate: 0.001
  kl_weight: 0.001

training:
  population_size: 12
  num_generations: 10
  episode_length: 12
  coherence_weight: 0.5
  individuality_weight: 0.5
  elite_fraction: 0.25
  mutation_radius: 0.3

evaluation:
  probe_repetitions: 3
  eval_episode_length: 8

corpus:
  novels_dir: data/novels
  characters_dir: data/characters
  # ── Bright Data (optional) ──────────────────────────────────────────
  # Uses Bright Data SDK for proxy/anti-bot scraping when downloading novels.
  # To enable: uncomment below, OR set USE_BRIGHTDATA=1, OR pass --brightdata.
  # Auth: export BRIGHTDATA_API_TOKEN="your-token"
  # Get a token: https://brightdata.com → Dashboard → API tokens
  # use_brightdata: true

paths:
  novels: data/novels
  characters: data/characters
  checkpoints: data/checkpoints
  reports: data/reports

```

### demo.py

```python
#!/usr/bin/env python3
"""Intuition demo — create distinct agentic personalities and compare their behavior.

Creates 3 personalities from the latent space (or directly via LLM if no VAE
checkpoint exists), puts them through identical situations, and prints a
side-by-side comparison: different z → meaningfully different perception,
emotion, and action.

Uses the LLM provider set in config.yaml (e.g. GEMINI_API_KEY, NIM_PROXY_BASE_URL for NeMo).
"""

from __future__ import annotations

import asyncio
import logging
import sys
from pathlib import Path

import numpy as np

# ---------------------------------------------------------------------------
# Configuration
# ---------------------------------------------------------------------------

SITUATIONS = [
    (
        "You discover that a close friend has been lying to you for months "
        "about something important.  They finally confess, in tears, and ask "
        "for your forgiveness."
    ),
    (
        "You are walking alone at night and come across a stranger sitting on "
        "a park bench, quietly weeping.  No one else is around."
    ),
    (
        "You receive unexpected public recognition for work you know was "
        "largely done by someone else on your team.  The audience applauds.  "
        "The other person is watching from the back of the room."
    ),
]

PERSONALITY_SEEDS = [
    {"name": "Alpha", "z_scale": 1.0, "seed": 42},
    {"name": "Beta",  "z_scale": 1.0, "seed": 137},
    {"name": "Gamma", "z_scale": 1.0, "seed": 7},
]

# ---------------------------------------------------------------------------
# Helpers
# ---------------------------------------------------------------------------

def _header(text: str, width: int = 78) -> str:
    return f"\n{'─' * width}\n  {text}\n{'─' * width}"


def _section(label: str) -> str:
    return f"\n  ┌─ {label} {'─' * max(1, 60 - len(label))}"


def _wrap(text: str, indent: int = 6, width: int = 72) -> str:
    """Simple word-wrap for terminal output."""
    import textwrap
    return textwrap.fill(
        text, width=width,
        initial_indent=" " * indent,
        subsequent_indent=" " * indent,
    )

# ---------------------------------------------------------------------------
# Core demo logic
# ---------------------------------------------------------------------------

async def create_personalities(n: int = 3):
    """Create *n* distinct personalities."""
    from intuition.api import _load_config, _get_llm, create_personality

    config = _load_config()
    llm = _get_llm()
    latent_dim = config.get("latent", {}).get("dimension", 32)

    # Check if we have a VAE checkpoint for latent-space sampling
    vae_path = (
        Path(config.get("paths", {}).get("checkpoints", "data/checkpoints"))
        / "vae.pt"
    )
    has_vae = vae_path.exists()

    kernels = []
    for i, seed_cfg in enumerate(PERSONALITY_SEEDS[:n]):
        rng = np.random.default_rng(seed_cfg["seed"])
        z = (rng.standard_normal(latent_dim) * seed_cfg["z_scale"]).astype(
            np.float32
        )
        print(f"  Creating personality {i + 1}/{n} "
              f"(seed={seed_cfg['seed']}, "
              f"{'VAE' if has_vae else 'direct'} mode)...")
        kernel = await create_personality(z=z.tolist(), llm=llm)
        kernels.append(kernel)
        print(f"    → {kernel.name}")

    return kernels


async def run_situations(kernels, situations):
    """Run every personality through every situation and collect traces."""
    from intuition.api import create_agent

    agents = [await create_agent(k) for k in kernels]
    all_traces: list[list] = []  # [situation_idx][kernel_idx]

    for s_idx, situation in enumerate(situations):
        print(f"\n  Situation {s_idx + 1}/{len(situations)}...")
        traces_for_situation = []
        for a_idx, agent in enumerate(agents):
            trace = await agent.act(situation)
            traces_for_situation.append(trace)
        all_traces.append(traces_for_situation)

    return all_traces


def print_comparison(kernels, situations, all_traces):
    """Print a rich side-by-side comparison."""
    try:
        from rich.console import Console
        from rich.panel import Panel
        from rich.table import Table
        from rich.text import Text
        _use_rich = True
        console = Console(width=100)
    except ImportError:
        _use_rich = False

    if _use_rich:
        _print_rich(console, kernels, situations, all_traces)
    else:
        _print_plain(kernels, situations, all_traces)


def _print_rich(console, kernels, situations, all_traces):
    from rich.panel import Panel
    from rich.table import Table

    # ── Personality summaries ──
    console.print("\n[bold cyan]╔══ PERSONALITIES CREATED ══╗[/bold cyan]\n")
    for i, k in enumerate(kernels):
        values_str = ", ".join(v.name for v in k.values[:3])
        fault_str = " / ".join(
            f"{f.tension[0]} vs {f.tension[1]}" for f in k.fault_lines[:2]
        )
        console.print(Panel(
            f"[bold]{k.name}[/bold]\n\n"
            f"[dim]Core values:[/dim]  {values_str}\n"
            f"[dim]Fault lines:[/dim] {fault_str}\n"
            f"[dim]Stress:[/dim]      {k.stress_profile.primary_response}\n\n"
            f"{k.behavioral_summary[:300]}…" if len(k.behavioral_summary) > 300
            else f"{k.behavioral_summary}",
            title=f"Personality {i + 1}",
            border_style="cyan",
            width=96,
        ))

    # ── Situation comparisons ──
    console.print("\n[bold yellow]╔══ BEHAVIORAL COMPARISON ══╗[/bold yellow]\n")

    for s_idx, situation in enumerate(situations):
        console.print(Panel(
            situation,
            title=f"Situation {s_idx + 1}",
            border_style="yellow",
            width=96,
        ))

        table = Table(show_header=True, header_style="bold", width=96)
        table.add_column("", style="cyan", width=14)
        for k in kernels:
            
[truncated — 4633 more characters]
```

### draw_yourself.py

```python
#!/usr/bin/env python3
"""Draw yourself — agentic personalities draw a self-portrait (no LLM at draw time).

Inspired by projective drawing tasks used in personality development research:
we let each personality "draw themselves" from their kernel. The drawing is
driven entirely by the PersonalityKernel (mean z, variance σ, stress profile,
emotional baseline, etc.): kernel → spec → renderer. So the personalities we
train are the ones doing the drawing.

================================================================================
STEPS TO RUN (so you actually get a drawing)
================================================================================

1. Install dependencies (once):
   pip install -r requirements.txt

2. Choose one of two ways to run:

   A) Use saved personality kernel(s) — NO API KEY needed to draw:
      python draw_yourself.py --kernel path/to/kernel.json --save my_drawing.png
      (Kernel = a PersonalityKernel JSON saved from training or create_personality.)

   B) Create 4 new personalities then draw — API KEY required:
      Set an API key for your chosen LLM provider, then run:
      export GEMINI_API_KEY="your_key"   # or OPENAI_API_KEY, ANTHROPIC_API_KEY, etc.
      python draw_yourself.py --save my_drawing.png
      (Uses config.yaml llm.provider; default is gemini.)

3. To get a PNG file without opening a window (e.g. on a server):
   python draw_yourself.py --no-window --save my_drawing.png
   If you omit --save when using --no-window, the file is saved as
   draw_yourself_output.png so you always get a drawing.

4. If you see "pygame not installed":
   pip install pygame

5. If personality creation fails with an API error:
   Set the correct env var for your provider (see config.yaml comments)
   or put the key in config.yaml (do not commit keys to git).
================================================================================
"""

from __future__ import annotations

import argparse
import asyncio
import logging
import sys
from pathlib import Path

import re

from pydantic import BaseModel, Field


def _friendly_error(exc: Exception) -> str:
    """Extract a short, readable message from verbose LLM API exceptions."""
    raw = str(exc)

    # Rate-limit / quota errors
    if "429" in raw or "RESOURCE_EXHAUSTED" in raw:
        retry = re.search(r"retry in ([\d.]+)", raw, re.IGNORECASE)
        wait = f" Try again in ~{int(float(retry.group(1)))+1}s." if retry else ""
        return f"Rate limit exceeded (HTTP 429). Your Gemini free-tier quota is used up.{wait}"

    # Auth errors
    if "401" in raw or "403" in raw or "UNAUTHENTICATED" in raw or "PERMISSION_DENIED" in raw:
        return "Authentication failed. Check that your API key is correct."

    # Schema / bad-request errors
    if "400" in raw or "INVALID_ARGUMENT" in raw:
        # Try to pull out the actual message
        m = re.search(r"'message':\s*'([^']{10,120})", raw)
        short = m.group(1).rstrip("\\n") if m else "Invalid request"
        return f"API rejected the request: {short}"

    # Anything else — truncate to something reasonable
    if len(raw) > 200:
        return raw[:180] + "..."
    return raw

# ---------------------------------------------------------------------------
# Drawing spec (filled from PersonalityKernel; renderer draws from it)
# ---------------------------------------------------------------------------
# Framed as "draw yourself": figure = self, rain = environmental pressure,
# umbrella = protection, mood = how you see your world.


class DrawYourselfSpec(BaseModel):
    """Self-portrait drawing spec derived from a PersonalityKernel."""

    person_x: float = Field(ge=0.0, le=1.0, description="Horizontal position (0=left, 1=right)")
    person_y: float = Field(ge=0.0, le=1.0, description="Vertical position (0=top, 1=bottom)")
    person_size: float = Field(ge=0.05, le=0.6, description="Size of figure (0.05=small, 0.4=large)")
    has_umbrella: bool = Field(description="Whether the figure has protection/shelter")
    umbrella_angle: float = Field(ge=-90.0, le=90.0, default=0.0, description="Tilt in degrees")
    rain_intensity: float = Field(ge=0.0, le=1.0, description="Environmental pressure (0=none, 1=heavy)")
    posture: str = Field(description="hunched | erect | crouched | fleeing")
    mood: str = Field(description="dark | somber | neutral | hopeful | calm | stormy | lonely")


# ---------------------------------------------------------------------------
# Kernel → spec (our personalities do the drawing)
# ---------------------------------------------------------------------------


def kernel_to_drawing_spec(kernel) -> DrawYourselfSpec:
    """Map our trained PersonalityKernel to a self-portrait spec. No LLM."""
    from intuition.core.kernel import PersonalityKernel
    import math

    k = kernel  # type: PersonalityKernel
    z = k.z if k.z else [0.0] * 2
    z0 = z[0] if len(z) > 0 else 0.0
    z1 = z[1] if len(z) > 1 else 0.0
    person_x = 0.5 + 0.3 * math.tanh(z0)
    person_y = 0.5 + 0.3 * math.tanh(z1)

    dominance = getattr(k.social_style, "dominance", 0.5)
    person_size = 0.12 + 0.25 * max(0.0, min(1.0, dominance))

    response = (k.stress_profile.primary_response or "freeze").lower()
    if "fight" in response:
        posture = "erect"
    elif "flight" in response or "flee" in response:
        posture = "fleeing"
    elif "fawn" in response:
        posture = "crouched"
    else:
        posture = "hunched"

    valence = getattr(k.emotional_baseline, "default_valence", 0.0)
    if valence < -0.3:
        mood = "dark"
    elif valence < 0.0:
        mood = "somber"
    elif valence > 0.3:
        mood = "hopeful"
    elif valence > 0.1:
        mood = "calm"
    else:
        mood = "neutral"

    reactivity = getattr(k.emotional_baseline, "reactivity", 0.5)
    rain_intensity = 0.3 + 0.6 * max(0.0, min(1.0, reactivity))

    threshold = getattr(k.stress_profile, "threshold", 0.5)
    conflict = (getattr(k.social_style
[truncated — 9953 more characters]
```

### intuition/__init__.py

```python
"""Intuition — Agentic Personalities.

Create individual and consistent artificial personalities (latent space + prior + Truman Show).

Usage:
    from intuition import create_personality, create_agent

    kernel = await create_personality()
    agent = await create_agent(kernel)
    response = await agent.respond("Tell me about yourself.")
"""

from intuition.core.kernel import PersonalityKernel
from intuition.core.traces import BehavioralTrace
from intuition.core.memory import EpisodicMemory, Episode
from intuition.api import create_personality, create_agent, load_personality, save_personality

__all__ = [
    "PersonalityKernel",
    "BehavioralTrace",
    "EpisodicMemory",
    "Episode",
    "create_personality",
    "create_agent",
    "load_personality",
    "save_personality",
]

```

### scripts/extract_characters.py

```python
#!/usr/bin/env python3
"""Extract character profiles from downloaded novels."""
import asyncio, logging
from rich.console import Console
from rich.logging import RichHandler
from intuition.corpus.dataset import CharacterDataset
from intuition.corpus.extractor import CharacterExtractor
from intuition.corpus.gutenberg import GutenbergCorpus
from intuition.llm.client import LLMClient
logging.basicConfig(level=logging.INFO, handlers=[RichHandler(console=Console(stderr=True))])

async def main():
    corpus = GutenbergCorpus("data/novels")
    dataset = CharacterDataset("data/characters")
    llm = LLMClient()
    extractor = CharacterExtractor(llm, min_evidence=5)
    for key in corpus.list_available():
        meta = corpus.get_metadata(key)
        text = corpus.read(key)
        profiles = await extractor.extract_from_novel(text, meta["title"], meta["author"])
        for p in profiles:
            dataset.add(p)
        print(f"{meta['title']}: {len(profiles)} characters")

if __name__ == "__main__":
    asyncio.run(main())

```

### tests/test_demo_smoke.py

```python
"""Smoke tests for demo entry points (import and minimal run with mocks)."""
from __future__ import annotations

from unittest.mock import AsyncMock, patch

import pytest


def test_demo_module_imports():
    """Main demo module can be imported without error."""
    import demo  # noqa: F401
    assert hasattr(demo, "main")
    assert hasattr(demo, "create_personalities")
    assert hasattr(demo, "run_situations")


async def test_demo_create_personalities_mocked(sample_kernel):
    """create_personalities returns kernels when create_personality is mocked."""
    from demo import create_personalities
    from unittest.mock import AsyncMock, MagicMock, patch

    mock_llm = MagicMock()
    with patch("intuition.api.create_personality", new_callable=AsyncMock) as create_mock, \
         patch("intuition.api._get_llm", return_value=mock_llm), \
         patch("intuition.api._load_config", return_value={"llm": {}, "latent": {"dimension": 32}}):
        create_mock.return_value = sample_kernel
        kernels = await create_personalities(n=2)
    assert len(kernels) == 2
    assert all(k.name == sample_kernel.name for k in kernels)

```

### scripts/evaluate.py

```python
#!/usr/bin/env python3
"""Run evaluation on trained personalities."""
import asyncio, logging
from pathlib import Path
import yaml
from rich.console import Console
from rich.logging import RichHandler
from intuition.agent.agent import PersonalityAgent
from intuition.core.kernel import PersonalityKernel
from intuition.environment.world import TrumanWorld
from intuition.evaluation.report import run_evaluation
from intuition.llm.client import LLMClient
from intuition.llm.embeddings import LocalEmbeddings, OpenAIEmbeddings
logging.basicConfig(level=logging.INFO, handlers=[RichHandler(console=Console(stderr=True))])

async def main():
    config = yaml.safe_load(Path("config.yaml").read_text())
    llm = LLMClient(**config.get("llm", {}))
    emb_cfg = config.get("embeddings", {})
    emb = (OpenAIEmbeddings() if emb_cfg.get("provider") == "openai" else LocalEmbeddings(dim=emb_cfg.get("dimension", 512)))
    cp = Path(config.get("paths", {}).get("checkpoints", "data/checkpoints"))
    kernel_files = sorted(cp.glob("final_top*.json"))
    if not kernel_files:
        print("No trained personalities found.")
        return
    kernels = [PersonalityKernel.load(str(f)) for f in kernel_files]
    agents = [PersonalityAgent(k, llm) for k in kernels]
    world = TrumanWorld(llm)
    traces = [await world.run_evaluation_episode(a, 8) for a in agents]
    report = await run_evaluation(agents, llm, emb, traces)
    print(report.summary())
    report.save(str(cp / "evaluation_report.json"))

if __name__ == "__main__":
    asyncio.run(main())

```

### scripts/train_personalities.py

```python
#!/usr/bin/env python3
"""Run the personality training loop."""
import asyncio, logging
from pathlib import Path
import yaml
from rich.console import Console
from rich.logging import RichHandler
from intuition.corpus.dataset import CharacterDataset
from intuition.latent.decoder import PersonalityDecoder
from intuition.latent.space import PersonalitySpace
from intuition.latent.vae import PersonalityVAE
from intuition.llm.client import LLMClient
from intuition.llm.embeddings import LocalEmbeddings, OpenAIEmbeddings
from intuition.training.optimizer import KernelOptimizer
from intuition.training.trainer import TrainingLoop
logging.basicConfig(level=logging.INFO, handlers=[RichHandler(console=Console(stderr=True))])

async def main():
    config = yaml.safe_load(Path("config.yaml").read_text())
    llm = LLMClient(**config.get("llm", {}))
    emb_cfg = config.get("embeddings", {})
    emb = (OpenAIEmbeddings() if emb_cfg.get("provider") == "openai" else LocalEmbeddings(dim=emb_cfg.get("dimension", 512)))
    cp = Path(config.get("paths", {}).get("checkpoints", "data/checkpoints"))
    vae = PersonalityVAE.load(str(cp / "vae.pt"))
    dataset = CharacterDataset()
    space = PersonalitySpace(vae, dataset, dataset.load_embeddings())
    decoder = PersonalityDecoder(space, llm)
    tc = config.get("training", {})
    optimizer = KernelOptimizer(space, decoder, population_size=tc.get("population_size", 12))
    trainer = TrainingLoop(llm, emb, optimizer, episode_length=tc.get("episode_length", 12), save_dir=str(cp))
    await trainer.train(num_generations=tc.get("num_generations", 10))

if __name__ == "__main__":
    asyncio.run(main())

```

[52 more indexed source files omitted to keep this export small. The full file list is in the Codebase structure section above.]