Eliza

A pattern-matching conversational agent built on a rule-based engine with persona support, session persistence, and both CLI and web interfaces. Inspired by the original ELIZA program. ESM-only TypeScript project running on Node.js + Express.

Overview

Eliza implements classic ELIZA-style pattern matching with modern enhancements: weighted rule selection, topic memory, response variety tracking, and a full persona system with 16 distinct conversational personas. The project shipped through 22 development phases across 4 milestones (v1.0–v1.4), with 350 tests and 91% coverage at completion.

The engine is cleanly separated from the web layer — it can run headless via CLI, serve a REST API via Express, or be imported as a library via browser.ts exports. A StorageProvider interface abstracts persistence, with a FileStorage implementation using atomic writes and a write queue to prevent data corruption.

Architecture

src/
  engine/         — Core: pattern matching, context tracking, topic memory, response variety
  personas/       — 16 persona definitions (rules, greetings, state transitions)
  middleware/     — Express middleware (session, CORS, security, validation)
  cli/            — Interactive CLI with readline-based REPL
  server.ts       — Express web server (API + static frontend)
  browser.ts      — Browser-compatible engine exports
  index.ts        — Library entry point
  persistence/    — StorageProvider interface + FileStorage implementation
  rules/          — JSON rule loader with AJV schema validation

Key modules:

ModulePurpose
engine/matcher.tsPattern matching with capture groups and $N substitution
engine/topics.tsTopicMemory — tracks user topics with configurable return gaps
engine/variety.tsResponseHistory — cycles through responses to prevent repetition
engine/context.tsContextTracker — conversation context window across turns
engine/responseSelector.tsResponseSelector — style-driven, recency-weighted response selection
persistence/fileStorage.tsAtomic file writes with proper-lockfile and structured error logging via winston

Tech Stack

TechnologyVersionPurpose
TypeScript^5.6.0Primary language — strict mode, ES2022 target
Node.js>=18.0.0Runtime — ESM modules (NodeNext)
Express.js^5.2.1Web API and static frontend serving
Vitest^4.0.17Test framework — V8 coverage
helmet^8.0.0Security headers (CSP, frameguard, referrer-policy)
ajv^8.18.0JSON schema validation for rule files
proper-lockfile^4.1.2Atomic file locking for persistence
winston^3.19.0Structured error logging

Persona System

16 personas defined as JSON files in src/personas/definitions/: classic, supportive, assistant, career, challenger, developer, dietitian, lifecoach, mindfulness, motivational, philosopher, productivity, quiz, stu, trainer, tutor.

Each persona defines:

  • rules — pattern-response pairs with optional state transitions
  • greetings — conversation openers
  • stateTransitions — topic flow definitions
  • fallbacks — emotion-specific and generic fallback responses

Rule precedence: persona-specific rules override defaults; higher-weight rules take precedence over lower-weight rules; ties use stable first-match-wins sort.

Rule System

14 JSON rule categories: emotions, family, work, dreams, health, self, greeting, goodbye, and others. Rules are loaded and validated against an AJV schema at startup. Pattern matching uses regex with capture groups; $N substitution assembles responses from captured text with safe reflection (first-person pronouns are not reflected back).

Weighted rule matching (introduced v1.3): phrase patterns default to weight 10, keyword patterns to weight 0, enabling clear priority layers without breaking existing rules.

Key Decisions

DecisionRationaleOutcome
CLI first, web secondFaster iteration on core logic before UI complexityGood
Pattern matching over AILearning project focused on rule-based systemsGood
ESM modules over CommonJSCleaner imports, modern JavaScript standardsGood
TypeScript strict modeType safety, better IDE tooling, fewer runtime errorsGood
First-match-wins rulesSimple, predictable pattern matching behaviorGood
TopicMemory class patternConsistent state tracking across conversationGood
ResponseHistory class patternPrevents repetitive responses, cycles through all optionsGood
Express.js for web APIMinimal setup, familiar patterns, good TypeScript supportGood
Default weight 0 for rulesBackward compatibility with existing rulesGood
3+ turn gap for topic returnAvoids “you just mentioned” for immediate repeatsGood
Avoid first-person reflectionPrevents subject confusion in captured groups with “I”Good

Development Status

  • Version: v1.4 (complete) — all 22 phases across 4 milestones shipped
  • Tests: 350 passing, 91% coverage
  • CI/CD: Passing — TruffleHog, Trivy, Semgrep inlined in security check
  • ESLint: 0 errors, 108 accepted warnings (console + any)
  • Deployment: Local (port 3001) / Vercel (vercel.json configured)
  • GitHub: irishrocker1125/Eliza

Lessons Learned

LessonRecommendation
TypeScript strict mode catches errors earlyAlways use for learning projects
Build core features CLI-firstWeb interface was trivial after CLI logic was solid
Pattern order mattersDocument priority carefully; first-match-wins requires ordering discipline
Reflection can flip subjectsAvoid reflecting captured groups containing first-person pronouns
Dogfooding reveals real issuesRun simulated conversations with emotional personas to catch edge cases
Generic fallbacks feel coldAdd emotion-specific fallbacks before generic ones
Compliance gaps accumulate silentlyRun compliance checklist at every milestone boundary

Technical Debt

ItemPriorityNotes
No persistent conversation historyLowBy design for v1.0, could add later
Rules hardcoded in moduleLowCould externalize to JSON for easier editing
Fire-and-forget persistence callsLowaddMessage() errors silently swallowed in server.ts
Some any types in persona collectionsLowCould tighten type definitions