Hidden Persistent Memory Lorebook Template

Hidden Persistent Memory Lorebook Template

5

5

Hidden Persistent Memory Template

This is a very large script. It is intended to be used as an example, so that it can be broken up into other Scripts.

Because it is an example, it has not been fully tested. The use of zero-width characters was tested, as well as the ability for them to persist in last_messages.

Much of the logic here has been taken from my previous Scripts and adapted using an LLM to make it utilize the zero-width method for data retention.

Mileage will vary based upon the model. If your audience is non-proxy users you will want to be careful about how much context you feed back to the model as you will need to ensure you have space for both the Script specific instructions AND the actual content.

A Janitor AI Scripts template that uses zero-width unicode characters for invisible state persistence between Script instances. Tracks weather, location, emotional state, inventory, schedule, and character presence without any visible markers in the chat.

Table of Contents

What This Template Does

The Hidden Persistent Memory template replaces visible flag strings (like **FLAGS:** XX:XX:XX) with invisible zero-width unicode characters embedded in the AI's output. State data passes between Script cycles without breaking user immersion.

This is the successor to the Persistent Flags Lorebook Template. Where that template uses visible hex strings that the user can see and potentially cheat, this template encodes all state information invisibly.

Key Features

  • Invisible State Tracking: Zero-width unicode characters encode data without visible markers

  • Modular Components: Each tracking system (weather, location, emotions, etc.) can be used independently

  • Scene Shift Detection: Keyword-weighted analysis detects potential location changes and asks the LLM to evaluate

  • Emotion Bitmask: 16-bit bitmask tracks 8 emotion axes at 4 intensity levels each

  • Character Presence: Tracks which characters are in the scene with arrival/departure evaluation

  • Token Budget Management: Automatically reduces detail levels (Full > Summary > Bullet) when context is tight

  • Natural Emotion Decay: Emotions gradually diminish over time without reinforcement

  • ES6 Enabled: Requires "use worker" directive for modern JavaScript support

Comparison to Persistent Flags

FeaturePersistent FlagsHidden Persistent MemoryState visibilityVisible hex stringInvisible unicodeState format**FLAGS:** 00:1A:FFZero-width charactersTracking typesSingle flag chainMultiple categoriesAnti-cheat neededYesNot applicableToken overheadModerateLowUser can edit stateYes (copy/paste)No (invisible)ModularityMonolithicComponent-based

How It Works

Zero-Width Encoding

The system maps decimal digits (0-9) to zero-width unicode characters:

DigitCharacterUnicode0Zero-Width SpaceU+200B1Zero-Width Non-JoinerU+200C2Zero-Width JoinerU+200D3Zero-Width No-Break SpaceU+FEFF4Word JoinerU+20605Function ApplicationU+20616Invisible TimesU+20627Invisible SeparatorU+20638Left-to-Right MarkU+200E9Right-to-Left MarkU+200F

State data is encoded as a decimal string, then each digit is replaced with its corresponding zero-width character. The encoded block is wrapped in header/footer markers (ZWJ ZWJ) for reliable extraction.

State String Format

The state is organized as pipe-delimited category segments:

CATEGORY_ID + DATA | CATEGORY_ID + DATA | ...

Example state string (decimal, before encoding):

0102|0205|0314820|0400010010|05015|0601010

SegmentCategoryValueMeaning0102Weather (01)Index 02Raining0205Location (02)Index 05Dungeon0314820Emotion (03)14820Emotion bitmask0400010010Inventory (04)BitfieldItems 4 and 8 owned05015Schedule (05)Day 015Day 150601010Characters (06)BitfieldCharacters 2 and 4 present

Execution Cycle

  1. Script scans the last 10 messages for encoded state blocks

  2. Decodes the most recent valid state found

  3. Parses into category segments

  4. Each active component processes its segment: checks keywords, updates state, builds context

  5. Re-encodes updated state

  6. Appends instruction for LLM to reproduce encoded string

  7. LLM copies invisible string at start and end of its response

  8. Cycle repeats

Components

Weather Tracking

Category ID: 01
Detail method: Flat (no tiering)

Tracks current weather as an index into a predefined table. Weather changes when keywords are detected in the user's message.

IndexWeatherKeywords00Clearclear sky, sunny, sunlight01Cloudycloudy, overcast, gray sky02Rainrain, raining, downpour03Stormstorm, thunder, lightning04Snowsnow, snowing, blizzard05Fogfog, mist, haze06Windywind, windy, gale07Hailhail, hailstorm08Heatwaveheatwave, scorching, sweltering09Eclipseeclipse, darkened sky, black sun

Location Tracking

Category ID: 02
Detail method: Summary (Full / Summary / Bullet)

Tracks current location with three detail levels. Includes scene shift detection using keyword weights.

When the user's message contains travel-related keywords, the system scores potential destination locations based on keyword matches. If a candidate exceeds the weight threshold, the script injects an evaluation prompt asking the LLM to determine if a scene change actually occurred.

Default locations: Tavern, Forest, Castle, Market, Dungeon

Emotional State

Category ID: 03
Detail method: Ranked information (highest intensity first)

Tracks 8 emotion axes using a 16-bit bitmask (2 bits per emotion):

BitsEmotionTriggers0-1Affectionatepraise, compliment, kind, gentle2-3Frustratedreject, insult, refuse, deny4-5Anxiousdanger, threat, risk, afraid6-7Romanticflirt, kiss, hold hands, embrace8-9Playfuljoke, tease, laugh, grin10-11Dominantcommand, order, control, submit12-13Trustconfide, trust, share, honest14-15Intimacyclose, intimate, vulnerable

Intensity levels: 00=off, 01=low, 10=medium, 11=high

Natural decay: When no emotion triggers are detected, all active emotions decrease by one intensity level per cycle.

Inventory

Category ID: 04
Detail method: Summary (Full / Summary / Bullet)

Tracks item ownership as a bitfield (one digit per item: 0=unowned, 1=owned). Supports weapons, consumables, construction materials, resources, currency, and accessories. Includes kingdom-building items (castle blueprints, iron ingots, stone blocks, timber).

Default items (8 slots): Iron Sword, Healing Potion, Castle Blueprint, Iron Ingots, Stone Blocks, Timber, Gold Coins, Magic Amulet

Schedule/Time

Category ID: 05
Detail method: Ranked information (current day + triggered events)

Tracks a day counter (1-999) and triggers scheduled events at specific day thresholds.

KeywordsDay incrementnext day, next morning, wake up, dawn breaks+1days later, days pass, a week, several days+3

Default events: Day 1 (arrival), Day 7 (weekly), Day 14 (fortnight), Day 30 (monthly), Day 90 (quarterly)

Character Presence

Category ID: 06
Detail method: Summary (Full / Summary / Bullet)

Tracks which characters from a predefined list are present in the current scene. Uses the Multiple Character Template's approach for mention detection with persistent tracking.

  • Character presence stored as a bitfield (1=present, 0=absent)

  • Arrival keywords detected: arrives, enters, walks in, comes in, approaches

  • Departure keywords detected: leaves, exits, walks away, departs, heads out

  • Mentioned-but-absent characters get an evaluation prompt for the LLM

Default characters: Alex, Maya, Jordan, Sam, Riley, Casey (6 slots)

Configuration

Feature Toggles

const FEATURES = {
    CORE_ENCODING: true,         // Always keep true
    WEATHER_TRACKING: true,      // Weather component
    LOCATION_TRACKING: true,     // Location + scene shifts
    EMOTION_TRACKING: true,      // Emotion bitmask
    INVENTORY_TRACKING: true,    // Item ownership
    SCHEDULE_TRACKING: true,     // Day counter + events
    CHARACTER_TRACKING: true,    // Multi-character presence
    SCENE_SHIFT_DETECTION: true, // Location change evaluation
    EMOTION_DECAY: true,         // Natural emotion reduction
    TOKEN_MANAGEMENT: true,      // Auto detail reduction
    DEBUG_MODE: false            // Show encoded state info
};

Token Budget

const CONFIG = {
    MAX_SCENARIO_CHARS: 600,       // Total scenario addition budget
    MAX_PERSONALITY_CHARS: 400,    // Total personality addition budget
    SEARCH_DEPTH: 10,              // Messages to scan for state
    SCENE_SHIFT_THRESHOLD: 4,      // Weight needed to trigger shift eval
    EMOTION_DECAY_RATE: 1          // Steps emotions decay per cycle
};

Default State

const DEFAULT_STATE = {
    '01': '00',        // Weather: clear
    '02': '00',        // Location: first entry
    '03': '00000',     // Emotion: all off
    '04': '00000000',  // Inventory: empty
    '05': '001',       // Schedule: day 1
    '06': '000000'     // Characters: all absent
};

Quick Start

Minimal Setup (Weather Only)

  1. Copy the full template into a Script lorebook entry

  2. Disable all components except CORE_ENCODING and WEATHER_TRACKING

  3. Modify WEATHER_TABLE with your scenario's weather conditions

  4. Set the default weather index in DEFAULT_STATE

  5. Test by mentioning weather keywords in chat

Using Multiple Components

  1. Enable the components you need in FEATURES

  2. Modify each component's data table for your scenario

  3. Adjust DEFAULT_STATE to match your starting conditions

  4. Set CONFIG.MAX_SCENARIO_CHARS and CONFIG.MAX_PERSONALITY_CHARS based on your token limits

  5. Enable DEBUG_MODE: true to verify state encoding/decoding

  6. Test each component independently before combining

Component Data Tables

Customizing Weather

{ id: 'clear', keywords: ['clear sky', 'sunny'], description: 'The sky is clear and bright.' }

Customizing Locations

{
    id: 'tavern',
    keywords: ['tavern', 'bar', 'inn'],
    full: {
        scenario: ' Detailed description...',
        personality: ', personality trait'
    },
    summary: {
        scenario: ' Shorter description...',
        personality: ', shorter trait'
    },
    bullet: {
        scenario: ' Location: name. Key features.',
        personality: ', brief trait'
    }
}

Customizing Emotions

// Emotion axes
{ name: 'affectionate', triggers: ['praise', 'compliment', 'kind'] }

// Emotion personality templates affectionate: { low: { personality: ', slightly warm in demeanor' }, medium: { personality: ', noticeably affectionate' }, high: { personality: ', deeply affectionate' } }

Customizing Inventory

{
id: 'iron_sword',
keywords: ['sword', 'iron sword', 'blade'],
category: 'weapon',
full: { scenario: '...', personality: '...' },
summary: { scenario: '...', personality: '...' },
bullet: { scenario: '...', personality: '...' }
}

Update DEFAULT_STATE[CATEGORY.INVENTORY] to match the number of items (one digit per item).

Customizing Characters

{
id: 'alex',
name: 'Alex',
aliases: ['alexander', 'alec'],
departureKeywords: ['leaves', 'exits', 'walks away'],
arrivalKeywords: ['arrives', 'enters', 'walks in'],
full: {
scenario: "Alex was mentioned...",
personality: "Alex personality traits...",
example_dialogs: "<BEGIN 'Alex' EXAMPLE DIALOGS>...<END>"
},
summary: { scenario: "...", personality: "...", example_dialogs: "" },
bullet: { scenario: "...", personality: "...", example_dialogs: "" }
}

Scene Shift Detection

Keyword Weight Categories

CategoryWeightExample KeywordsTravel3walk, go to, head to, leave, arriveIndoor2step inside, walk in, open the doorOutdoor2step outside, go outside, fresh airRest1sit down, settle, stay atDistance2across, through the, beyond

Each candidate location also gets +2 weight when its own keywords are matched.

Tuning the Threshold

  • Lower threshold (2-3): More sensitive, may trigger false positives

  • Default (4): Balanced detection

  • Higher threshold (6-8): Only detects explicit travel language

Shift Evaluation

When a potential shift is detected, the script does NOT immediately change the location. Instead, it injects an instruction asking the LLM to evaluate whether the scene change actually occurred based on narrative context.

Copy/Paste Isolation

Each component section is clearly marked with // === COMPONENT: NAME === comments. To isolate a component:

  1. Always include the three CORE sections (encoding, extraction, injection)

  2. Include the desired COMPONENT section

  3. Include the corresponding output section in OUTPUT ASSEMBLY

  4. Remove entries for unused components from DEFAULT_STATE

  5. Remove unused component processing from buildStateString()

  6. Remove unused component output from the OUTPUT ASSEMBLY section

Troubleshooting

State Not Persisting

  • Enable DEBUG_MODE: true to see the decoded state

  • Check that the LLM is reproducing the encoded string

  • Verify SEARCH_DEPTH is sufficient (default 10 messages)

  • Check that the state string format is valid (decimal digits and pipe delimiters only)

LLM Not Reproducing State

  • Keep the [PERSISTENT MEMORY] instruction block short and clear

  • Ensure the instruction appears at the end of scenario context

  • Avoid overloading the LLM with too many simultaneous instructions

Too Much Context Being Injected

  • Enable TOKEN_MANAGEMENT and reduce MAX_SCENARIO_CHARS / MAX_PERSONALITY_CHARS

  • Components will automatically drop from Full to Summary to Bullet

  • Consider disabling components you don't need

  • Shorten the text in your data table entries

Emotions Not Changing

  • Check that trigger keywords are in the user's message (lowercase matching)

  • Remember emotions are encoded as a single decimal number

  • Each axis can only go up to 3 (binary 11)

  • Emotions decay naturally each cycle when no triggers fire

Location Not Shifting

  • Lower SCENE_SHIFT_THRESHOLD to make detection more sensitive

  • Ensure location keywords match how users describe places

  • Scene shifts require the LLM to confirm - the script only suggests

Characters Not Appearing

  • Verify the character's name or aliases appear in the user's message

  • Check if arrival keywords are present (arrives, enters, walks in)

  • Characters mentioned without arrival keywords get an evaluation prompt

  • Check DEFAULT_STATE - characters start absent by default

Known Limitations

  1. Platform portability: Copying AI text to other platforms may strip zero-width characters

  2. Model compliance: LLMs occasionally fail to reproduce exact zero-width strings; the script falls back to defaults gracefully

  3. No manual state editing: Users cannot see or modify state like they could with visible hex flags

  4. Debugging difficulty: Invisible state requires DEBUG_MODE to troubleshoot

  5. Character limits: Each component adds encoding overhead; very complex scenarios should limit active components

Adding Custom Components

  1. Define a new CATEGORY_ID (2-digit string, e.g., '07')

  2. Add a default value to DEFAULT_STATE

  3. Create a data table and processing section under a // === COMPONENT: NAME === comment

  4. Add state encoding in buildStateString()

  5. Add output logic in the OUTPUT ASSEMBLY section

  6. Add a feature toggle in FEATURES


Template: Hidden_Persistent_Memory_Template.js
Author: Tydorius on JanitorAI
Support: Ko-fi

proxy allowed

Published chats

0

comments

Leave a comment or feedback for the creator ❤️