fix(ai-context): sync manual edits to committed tracker data

Fixes critical issue where manual edits (add location, add item, change
stats, etc.) were invisible to AI in next generation, causing edits to be
immediately overwritten.

Root Cause:
- Manual edits updated extensionSettings and lastGeneratedData
- AI prompt builder used committedTrackerData (NOT extensionSettings)
- Manual edits were never synced to committedTrackerData
- Result: AI didn't see manual changes, overwrote them

Solution - Sync to Both Data Stores:

All manual edit points now update BOTH:
1. lastGeneratedData (for display)
2. committedTrackerData (for AI context)

Files Modified:

1. **src/systems/interaction/inventoryActions.js**
   - updateLastGeneratedDataInventory() now sets committedTrackerData.userStats
   - Affects: add/remove items, add/remove locations

2. **src/systems/rendering/userStats.js**
   - All 3 edit handlers now set committedTrackerData.userStats
   - Affects: stat values (health, etc.), mood emoji, conditions
   - Also fixed: now uses buildInventorySummary() for proper v2 format

3. **src/systems/rendering/infoBox.js**
   - updateInfoBoxField() now sets committedTrackerData.infoBox
   - Affects: date, weather, temperature, time, location

4. **src/systems/rendering/thoughts.js**
   - updateCharacterField() now sets committedTrackerData.characterThoughts
   - Affects: character emoji, name, traits, thoughts, relationship

Impact - Manual Edits Now Persist:

Before:
- Add location "Home" → Next generation → Location gone 
- Add item "Sword" → Next generation → Item gone 
- Change health to 25% → AI ignores it 

After:
- Add location "Home" → Next generation → Location persists ✓
- Add item "Sword" → Next generation → Item included ✓
- Change health to 25% → AI acknowledges low health ✓

Works in Both Modes:
- Together mode: AI sees manual edits in injected prompt ✓
- Separate mode: AI sees manual edits in context ✓

User Experience:
- "I edited it, so it should stay" - now works as expected
- AI builds on manual changes instead of overwriting them
- Minimal overhead (just string copies)

Fixes: Manual inventory/stats edits being overwritten by AI generation
This commit is contained in:
Lucas 'Paperboy' Rose-Winters
2025-10-20 08:05:08 +11:00
parent 7b320b8d0b
commit f09c42ec6e
4 changed files with 47 additions and 15 deletions
+11 -5
View File
@@ -3,7 +3,7 @@
* Handles all user interactions with the inventory v2 system * Handles all user interactions with the inventory v2 system
*/ */
import { extensionSettings, lastGeneratedData } from '../../core/state.js'; import { extensionSettings, lastGeneratedData, committedTrackerData } from '../../core/state.js';
import { saveSettings, saveChatData, updateMessageSwipeData } from '../../core/persistence.js'; import { saveSettings, saveChatData, updateMessageSwipeData } from '../../core/persistence.js';
import { buildInventorySummary } from '../generation/promptBuilder.js'; import { buildInventorySummary } from '../generation/promptBuilder.js';
import { renderInventory } from '../rendering/inventory.js'; import { renderInventory } from '../rendering/inventory.js';
@@ -37,15 +37,16 @@ let openForms = {
}; };
/** /**
* Updates lastGeneratedData.userStats to include current inventory in text format. * Updates lastGeneratedData.userStats AND committedTrackerData.userStats to include
* This ensures the AI context stays synced with manual edits. * current inventory in text format.
* This ensures manual edits are immediately visible to AI in next generation.
*/ */
function updateLastGeneratedDataInventory() { function updateLastGeneratedDataInventory() {
const stats = extensionSettings.userStats; const stats = extensionSettings.userStats;
const inventorySummary = buildInventorySummary(stats.inventory); const inventorySummary = buildInventorySummary(stats.inventory);
// Rebuild the lastGeneratedData.userStats text format // Rebuild the userStats text format
lastGeneratedData.userStats = const statsText =
`Health: ${stats.health}%\n` + `Health: ${stats.health}%\n` +
`Satiety: ${stats.satiety}%\n` + `Satiety: ${stats.satiety}%\n` +
`Energy: ${stats.energy}%\n` + `Energy: ${stats.energy}%\n` +
@@ -53,6 +54,11 @@ function updateLastGeneratedDataInventory() {
`Arousal: ${stats.arousal}%\n` + `Arousal: ${stats.arousal}%\n` +
`${stats.mood}: ${stats.conditions}\n` + `${stats.mood}: ${stats.conditions}\n` +
`${inventorySummary}`; `${inventorySummary}`;
// Update BOTH lastGeneratedData AND committedTrackerData
// This makes manual edits immediately visible to AI
lastGeneratedData.userStats = statsText;
committedTrackerData.userStats = statsText;
} }
/** /**
+5
View File
@@ -7,6 +7,7 @@ import { getContext } from '../../../../../../extensions.js';
import { import {
extensionSettings, extensionSettings,
lastGeneratedData, lastGeneratedData,
committedTrackerData,
$infoBoxContainer $infoBoxContainer
} from '../../core/state.js'; } from '../../core/state.js';
import { saveChatData } from '../../core/persistence.js'; import { saveChatData } from '../../core/persistence.js';
@@ -429,6 +430,10 @@ export function updateInfoBoxField(field, value) {
lastGeneratedData.infoBox = updatedLines.join('\n'); lastGeneratedData.infoBox = updatedLines.join('\n');
// Update BOTH lastGeneratedData AND committedTrackerData
// This makes manual edits immediately visible to AI
committedTrackerData.infoBox = updatedLines.join('\n');
// Update the message's swipe data // Update the message's swipe data
const chat = getContext().chat; const chat = getContext().chat;
if (chat && chat.length > 0) { if (chat && chat.length > 0) {
+5
View File
@@ -9,6 +9,7 @@ import { selected_group, getGroupMembers } from '../../../../../../group-chats.j
import { import {
extensionSettings, extensionSettings,
lastGeneratedData, lastGeneratedData,
committedTrackerData,
$thoughtsContainer, $thoughtsContainer,
FALLBACK_AVATAR_DATA_URI FALLBACK_AVATAR_DATA_URI
} from '../../core/state.js'; } from '../../core/state.js';
@@ -320,6 +321,10 @@ export function updateCharacterField(characterName, field, value) {
lastGeneratedData.characterThoughts = updatedLines.join('\n'); lastGeneratedData.characterThoughts = updatedLines.join('\n');
// console.log('[RPG Companion] 💾 Updated lastGeneratedData.characterThoughts:', lastGeneratedData.characterThoughts); // console.log('[RPG Companion] 💾 Updated lastGeneratedData.characterThoughts:', lastGeneratedData.characterThoughts);
// Update BOTH lastGeneratedData AND committedTrackerData
// This makes manual edits immediately visible to AI
committedTrackerData.characterThoughts = updatedLines.join('\n');
// Also update the last assistant message's swipe data // Also update the last assistant message's swipe data
const chat = getContext().chat; const chat = getContext().chat;
if (chat && chat.length > 0) { if (chat && chat.length > 0) {
+26 -10
View File
@@ -8,6 +8,7 @@ import { user_avatar } from '../../../../../../../script.js';
import { import {
extensionSettings, extensionSettings,
lastGeneratedData, lastGeneratedData,
committedTrackerData,
$userStatsContainer, $userStatsContainer,
FALLBACK_AVATAR_DATA_URI FALLBACK_AVATAR_DATA_URI
} from '../../core/state.js'; } from '../../core/state.js';
@@ -17,6 +18,7 @@ import {
updateMessageSwipeData updateMessageSwipeData
} from '../../core/persistence.js'; } from '../../core/persistence.js';
import { getSafeThumbnailUrl } from '../../utils/avatars.js'; import { getSafeThumbnailUrl } from '../../utils/avatars.js';
import { buildInventorySummary } from '../generation/promptBuilder.js';
/** /**
* Renders the user stats panel with health bars, mood, inventory, and classic stats. * Renders the user stats panel with health bars, mood, inventory, and classic stats.
@@ -178,13 +180,15 @@ export function renderUserStats() {
// Update the setting // Update the setting
extensionSettings.userStats[field] = value; extensionSettings.userStats[field] = value;
// Also update lastGeneratedData to keep it in sync // Rebuild userStats text with proper inventory format
if (!lastGeneratedData.userStats) { const stats = extensionSettings.userStats;
lastGeneratedData.userStats = ''; const inventorySummary = buildInventorySummary(stats.inventory);
} const statsText = `Health: ${stats.health}%\nSatiety: ${stats.satiety}%\nEnergy: ${stats.energy}%\nHygiene: ${stats.hygiene}%\nArousal: ${stats.arousal}%\n${stats.mood}: ${stats.conditions}\n${inventorySummary}`;
// Regenerate the userStats text with updated value
const statsText = `Health: ${extensionSettings.userStats.health}%\nSatiety: ${extensionSettings.userStats.satiety}%\nEnergy: ${extensionSettings.userStats.energy}%\nHygiene: ${extensionSettings.userStats.hygiene}%\nArousal: ${extensionSettings.userStats.arousal}%\n${extensionSettings.userStats.mood}: ${extensionSettings.userStats.conditions}\nInventory: ${extensionSettings.userStats.inventory}`; // Update BOTH lastGeneratedData AND committedTrackerData
// This makes manual edits immediately visible to AI
lastGeneratedData.userStats = statsText; lastGeneratedData.userStats = statsText;
committedTrackerData.userStats = statsText;
saveSettings(); saveSettings();
saveChatData(); saveChatData();
@@ -199,9 +203,15 @@ export function renderUserStats() {
const value = $(this).text().trim(); const value = $(this).text().trim();
extensionSettings.userStats.mood = value || '😐'; extensionSettings.userStats.mood = value || '😐';
// Update lastGeneratedData // Rebuild userStats text with proper inventory format
const statsText = `Health: ${extensionSettings.userStats.health}%\nSatiety: ${extensionSettings.userStats.satiety}%\nEnergy: ${extensionSettings.userStats.energy}%\nHygiene: ${extensionSettings.userStats.hygiene}%\nArousal: ${extensionSettings.userStats.arousal}%\n${extensionSettings.userStats.mood}: ${extensionSettings.userStats.conditions}\nInventory: ${extensionSettings.userStats.inventory}`; const stats = extensionSettings.userStats;
const inventorySummary = buildInventorySummary(stats.inventory);
const statsText = `Health: ${stats.health}%\nSatiety: ${stats.satiety}%\nEnergy: ${stats.energy}%\nHygiene: ${stats.hygiene}%\nArousal: ${stats.arousal}%\n${stats.mood}: ${stats.conditions}\n${inventorySummary}`;
// Update BOTH lastGeneratedData AND committedTrackerData
// This makes manual edits immediately visible to AI
lastGeneratedData.userStats = statsText; lastGeneratedData.userStats = statsText;
committedTrackerData.userStats = statsText;
saveSettings(); saveSettings();
saveChatData(); saveChatData();
@@ -212,9 +222,15 @@ export function renderUserStats() {
const value = $(this).text().trim(); const value = $(this).text().trim();
extensionSettings.userStats.conditions = value || 'None'; extensionSettings.userStats.conditions = value || 'None';
// Update lastGeneratedData // Rebuild userStats text with proper inventory format
const statsText = `Health: ${extensionSettings.userStats.health}%\nSatiety: ${extensionSettings.userStats.satiety}%\nEnergy: ${extensionSettings.userStats.energy}%\nHygiene: ${extensionSettings.userStats.hygiene}%\nArousal: ${extensionSettings.userStats.arousal}%\n${extensionSettings.userStats.mood}: ${extensionSettings.userStats.conditions}\nInventory: ${extensionSettings.userStats.inventory}`; const stats = extensionSettings.userStats;
const inventorySummary = buildInventorySummary(stats.inventory);
const statsText = `Health: ${stats.health}%\nSatiety: ${stats.satiety}%\nEnergy: ${stats.energy}%\nHygiene: ${stats.hygiene}%\nArousal: ${stats.arousal}%\n${stats.mood}: ${stats.conditions}\n${inventorySummary}`;
// Update BOTH lastGeneratedData AND committedTrackerData
// This makes manual edits immediately visible to AI
lastGeneratedData.userStats = statsText; lastGeneratedData.userStats = statsText;
committedTrackerData.userStats = statsText;
saveSettings(); saveSettings();
saveChatData(); saveChatData();