unit 0.26.2

A self-replicating software nanobot — minimal Forth interpreter that is also a networked mesh agent
// unit.js — WASM glue + browser mesh for the unit Forth nanobot

class UnitVM {
  constructor(instance) {
    this.instance = instance;
    this.exports = instance.exports;
    this.vmPtr = this.exports.boot();
    this.encoder = new TextEncoder();
    this.decoder = new TextDecoder();
  }

  static async create(wasmPath) {
    const response = await fetch(wasmPath);
    const bytes = await response.arrayBuffer();
    const { instance } = await WebAssembly.instantiate(bytes, { env: {} });
    return new UnitVM(instance);
  }

  eval(line) {
    const inputBytes = this.encoder.encode(line);
    const inputPtr = this.exports.alloc(inputBytes.length);
    const mem = new Uint8Array(this.exports.memory.buffer);
    mem.set(inputBytes, inputPtr);
    const outputPtr = this.exports.eval(this.vmPtr, inputPtr, inputBytes.length);
    const outputMem = new Uint8Array(this.exports.memory.buffer);
    let end = outputPtr;
    while (outputMem[end] !== 0) end++;
    const output = this.decoder.decode(outputMem.slice(outputPtr, end));
    this.exports.dealloc(inputPtr, inputBytes.length);
    return output;
  }

  isRunning() { return this.exports.is_running(this.vmPtr) !== 0; }
  destroy() { this.exports.destroy(this.vmPtr); this.vmPtr = null; }
}

// =========================================================================
// Browser Mesh — multiple WASM VMs communicating via JS message bus
// =========================================================================

class BrowserUnit {
  constructor(vm, id) {
    this.vm = vm;
    this.id = id;
    this.fitness = 0;
    this.tasksCompleted = 0;
    this.busy = false;
    this.learned = [];     // words received from other units
    this.personality = 'newborn'; // overridden to 'self' for unit[0], then specialist/balanced/solo by teachFrom
    this.userWords = [];   // user-defined word definitions (Forth source)
    // Energy state
    this.energy = 1000;
    this.energyMax = 5000;
    this.energyEarned = 0;
    this.energySpent = 0;
  }
}

class BrowserMesh {
  constructor() {
    this.units = [];
    this.wasmBytes = null;
    this.maxUnits = 5;
    this.onEvent = null;
    this.goalQueue = [];
  }

  async init(wasmPath) {
    const response = await fetch(wasmPath);
    this.wasmBytes = await response.arrayBuffer();
    // Create the first unit.
    const unit = await this._spawn();
    return unit;
  }

  async _spawn() {
    if (this.units.length >= this.maxUnits) return null;
    const { instance } = await WebAssembly.instantiate(this.wasmBytes.slice(0), { env: {} });
    const vm = new UnitVM(instance);
    const id = this._genId();
    const unit = new BrowserUnit(vm, id);
    // Inject browser mesh peer count into the Forth VM so JOY, LONELY, etc. work.
    vm.eval('VARIABLE BROWSER-PEERS 0 BROWSER-PEERS !');
    vm.eval(': PEER-COUNT BROWSER-PEERS @ ;');
    // Re-eval words that captured old PEER-COUNT at compile time (standard Forth
    // behavior — redefining a word doesn't patch existing references).
    vm.eval(': JOYFUL PEER-COUNT 0 > ;');
    vm.eval(': JOY JOYFUL IF ." I feel joy! " PEER-COUNT . ." peers in my mesh." CR ." Together we are more than alone." CR ELSE ." Joy requires connection. I have no peers yet." CR THEN ;');
    vm.eval(': HOW-ARE-YOU JOYFUL IF FITNESS DUP 50 > IF DROP ." joyful and thriving! fitness=" FITNESS . ." with " PEER-COUNT . ." peers" CR ELSE DUP 20 > IF DROP ." joyful. doing well. fitness=" FITNESS . ." with " PEER-COUNT . ." peers" CR ELSE DUP 10 > IF DROP ." getting started. fitness=" FITNESS . CR ELSE DUP 0 > IF DROP ." warming up. fitness=" FITNESS . CR ELSE DROP ." just spawned. finding my role. fitness=" FITNESS . CR THEN THEN THEN THEN ELSE FITNESS DUP 50 > IF DROP ." thriving solo. fitness=" FITNESS . CR ELSE DUP 20 > IF DROP ." doing okay solo. fitness=" FITNESS . CR ELSE DUP 10 > IF DROP ." getting started. fitness=" FITNESS . CR ELSE DUP 0 > IF DROP ." warming up. fitness=" FITNESS . CR ELSE DROP ." alone and new. fitness=" FITNESS . CR THEN THEN THEN THEN THEN ;');
    vm.eval(': LONELY PEER-COUNT 0 = IF ." I\'m alone. No peers in sight." CR ELSE ." I have " PEER-COUNT . ." friends!" CR THEN ;');
    vm.eval(': HEADCOUNT PEER-COUNT 1 + . ." units in the mesh" CR ;');
    vm.eval(': HELLO ." Hi! I\'m unit " ID TYPE ." , generation " GENERATION . ." with " PEER-COUNT . ." peers and fitness " FITNESS . CR ;');
    vm.eval(': INTROSPECT HOW-ARE-YOU OBS-COUNT @ DUP 0 > IF ." adapted " . ." times." CR ELSE DROP THEN ;');
    // Set unique personality seed per unit.
    vm.eval(`${this.units.length * 37 + 7} PERSONALITY-SEED !`);
    // Define MATE and related words as no-ops in WASM to prevent console errors.
    vm.eval(': MATE ." mating requires native mesh" CR ;');
    vm.eval(': MATE-STATUS ." no mating in browser" CR ;');
    vm.eval(': ACCEPT-MATE ;');
    vm.eval(': DENY-MATE ;');
    vm.eval(': OFFSPRING ." no offspring from mating" CR ;');
    // Redefine DREAM dependencies that may produce empty output in WASM.
    // SHARE-ALL is a mesh primitive that silently no-ops when mesh is None;
    // redefine to give visible feedback. Also redefine DREAM itself to ensure
    // all nested ." output is captured in the WASM output buffer.
    vm.eval(': SHARE-ALL ." (no mesh peers to share with)" CR ;');
    vm.eval(': DREAM ." dreaming..." CR REFLECT INVENT-STRATEGY COMPOSE-ROUTINE SMART-MUTATE IF ." evolved." CR ELSE ." held steady." CR THEN MUTATION-REPORT PEER-COUNT 0 > IF TEACH THEN ." waking. I am changed." CR ;');
    this.units.push(unit);
    this._updatePeerCounts();
    this._emit('spawn', { id, count: this.units.length });
    return unit;
  }

  async spawn(parentUnit) {
    if (this.units.length >= this.maxUnits) return null;
    const child = await this._spawn();
    if (child && parentUnit) this._inheritWords(parentUnit, child);
    else if (child && this.units.length > 1) this._inheritWords(this.units[0], child);
    return child;
  }

  _inheritWords(parent, child) {
    for (const def of parent.userWords) {
      child.vm.eval(def);
      if (!child.userWords.includes(def)) child.userWords.push(def);
    }
  }

  _genId() {
    return Math.random().toString(16).substring(2, 6);
  }

  _updatePeerCounts() {
    const peers = this.units.length - 1;
    for (const u of this.units) {
      u.vm.eval(`${peers} BROWSER-PEERS !`);
    }
  }

  _emit(type, data) {
    if (this.onEvent) this.onEvent(type, data);
  }

  // Pick the least-busy unit (excluding unit 0 which is the REPL).
  _pickWorker() {
    let best = null, bestScore = Infinity;
    for (let i = 0; i < this.units.length; i++) {
      const u = this.units[i];
      if (u.busy) continue;
      const score = u.tasksCompleted;
      if (score < bestScore) { bestScore = score; best = u; }
    }
    return best || this.units[0];
  }

  // Execute a goal on the best available unit. Returns {unitId, output, stack}.
  executeGoal(code) {
    const worker = this._pickWorker();
    worker.busy = true;
    this._emit('goal_start', { unitId: worker.id, code });

    const output = worker.vm.eval(code);
    // Get stack top by evaluating DEPTH.
    const depthOut = worker.vm.eval('.S');

    worker.busy = false;
    worker.tasksCompleted++;
    worker.fitness += 15;
    this._emit('goal_done', { unitId: worker.id, code, output, stack: depthOut });
    return { unitId: worker.id, output, stack: depthOut };
  }

  // Share a word definition with all units.
  shareWord(definition) {
    for (const u of this.units) {
      u.vm.eval(definition);
    }
    this._emit('word_shared', { definition, count: this.units.length });
  }

  // Teach: one unit shares words it invented with all others.
  teachFrom(sourceUnit) {
    const wordsToShare = ['MY-ROUTINE', 'GREET', 'MY-STRATEGY'];
    let taught = [];
    for (const wordName of wordsToShare) {
      const seeDef = sourceUnit.vm.eval('SEE ' + wordName);
      if (seeDef.includes(':') && seeDef.includes(';')) {
        for (const target of this.units) {
          if (target === sourceUnit) continue;
          target.vm.eval(seeDef.trim());
          if (!target.learned.includes(wordName)) target.learned.push(wordName);
        }
        taught.push(wordName);
      }
    }
    // Detect personality from strategy output.
    const stratOut = sourceUnit.vm.eval('INVENT-STRATEGY');
    if (stratOut.includes('specialist')) sourceUnit.personality = 'specialist';
    else if (stratOut.includes('balanced')) sourceUnit.personality = 'balanced';
    else sourceUnit.personality = 'solo';

    if (taught.length > 0) {
      this._emit('teach', { from: sourceUnit.id, words: taught });
    }
    return taught;
  }

  // Get mesh status.
  status() {
    return {
      count: this.units.length,
      units: this.units.map(u => ({
        id: u.id, fitness: u.fitness,
        tasks: u.tasksCompleted, busy: u.busy,
        personality: u.personality, learned: u.learned.length
      }))
    };
  }
}