Give the assistant a standing memory file

A small operator-written file handed to the model at the start of every
conversation — who you are, how this install is set up, what has already been
decided. Injected ahead of the retrieved passages and marked operator-authored
so it outranks anything they contradict, and never cited as a source.

Deliberately not indexed and deliberately under DATA_DIR: it changes
constantly, vector similarity is the wrong way to retrieve things you were
told to remember, and gitignoring it keeps personal notes out of a pushed
repository. The character cap is a context budget — this text costs its share
of 16k on every single turn.
This commit is contained in:
Gmer4Lfe
2026-08-03 17:11:27 -04:00
parent 7625e923e5
commit d0b3588f6c
5 changed files with 181 additions and 0 deletions
+61
View File
@@ -200,6 +200,7 @@ if (is_dir('/var/log/varaverk')) {
<option value="doc">Design notes</option>
</select>
<label class="vv-ai-toggle"><input type="checkbox" id="vv-ai-think" checked> reasoning</label>
<button class="vv-ai-btn ghost" id="vv-ai-mem" type="button">Memory</button>
<button class="vv-ai-btn ghost" id="vv-ai-clear" type="button">Clear</button>
<span class="vv-ai-hint">Ctrl+Enter to send · 3-turn history · build <?=$_vv_ai_build?>
<span id="vv-ai-live" style="color:#e57">· JS NOT RUNNING</span></span>
@@ -208,6 +209,22 @@ if (is_dir('/var/log/varaverk')) {
</div>
</div>
<div id="vv-ai-memwrap" style="display:none;border:1px solid #262626;border-radius:6px;
background:#0e0e0e;padding:10px;">
<div style="display:flex;align-items:center;gap:8px;margin-bottom:6px;">
<span style="font-size:9px;letter-spacing:.08em;text-transform:uppercase;color:#4a4a4a;">
Standing memory — given to the assistant at the start of every conversation</span>
<span id="vv-ai-mem-count" style="margin-left:auto;font-size:10px;color:#3a3a3a;font-family:monospace;"></span>
</div>
<textarea id="vv-ai-mem-text" class="vv-ai-input" rows="10" spellcheck="false"
placeholder="Who you are, how this install is set up, decisions already made, things it should stop asking.&#10;&#10;e.g.&#10;- HOST2 (unRAID-Jayred36) is being rebuilt and is offline. Do not suggest syncing to it.&#10;- RSYNC_ENABLED is deliberately false until HOST2 is onboarded.&#10;- I verify everything before trusting it. Show your sources."></textarea>
<div style="display:flex;gap:8px;align-items:center;margin-top:7px;">
<span id="vv-ai-mem-status" style="font-size:10px;color:#4a4a4a;"></span>
<button class="vv-ai-btn ghost" style="margin-left:auto" id="vv-ai-mem-cancel" type="button">Close</button>
<button class="vv-ai-btn" id="vv-ai-mem-save" type="button">Save</button>
</div>
</div>
<div id="vv-ai-view" onclick="if(event.target===this)vvAiCloseView()">
<div class="vv-ai-view-box">
<div class="vv-ai-view-h">
@@ -530,6 +547,50 @@ if (is_dir('/var/log/varaverk')) {
if (s && s.path) vvAiOpen(s.path);
};
// ── Memory panel ────────────────────────────────────────────────────────
// Live character count against the cap, because the budget is the whole point: this text is
// prepended to every single turn and competes with retrieval for a 16k context.
let memMax = 4000;
function memCount() {
const n = $('vv-ai-mem-text').value.length;
const el = $('vv-ai-mem-count');
el.textContent = n + ' / ' + memMax;
el.style.color = n > memMax ? '#e57' : (n > memMax * 0.8 ? '#ffb74d' : '#3a3a3a');
$('vv-ai-mem-save').disabled = n > memMax;
}
function memOpen() {
const w = $('vv-ai-memwrap');
if (w.style.display !== 'none') { w.style.display = 'none'; return; }
$('vv-ai-mem-status').textContent = 'loading…';
w.style.display = '';
fetch(API + '?action=memory_get').then(r => r.json()).then(d => {
if (!d.ok) { $('vv-ai-mem-status').textContent = d.error || 'failed to load'; return; }
memMax = d.max || 4000;
$('vv-ai-mem-text').value = d.memory || '';
$('vv-ai-mem-status').textContent = d.exists ? d.path : 'not created yet — ' + d.path;
memCount();
}).catch(e => { $('vv-ai-mem-status').textContent = 'failed to load: ' + e; });
}
function memSave() {
$('vv-ai-mem-save').disabled = true;
$('vv-ai-mem-status').textContent = 'saving…';
fetch(API, { method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8' },
body: new URLSearchParams({ action: 'memory_set', memory: $('vv-ai-mem-text').value }) })
.then(r => r.json()).then(d => {
$('vv-ai-mem-save').disabled = false;
$('vv-ai-mem-status').textContent = d.ok
? 'saved — applies from your next message'
: (d.error || 'save failed');
})
.catch(e => { $('vv-ai-mem-save').disabled = false;
$('vv-ai-mem-status').textContent = 'save failed: ' + e; });
}
$('vv-ai-mem').addEventListener('click', memOpen);
$('vv-ai-mem-cancel').addEventListener('click', () => { $('vv-ai-memwrap').style.display = 'none'; });
$('vv-ai-mem-save').addEventListener('click', memSave);
$('vv-ai-mem-text').addEventListener('input', memCount);
// ── Wiring ──────────────────────────────────────────────────────────────
$('vv-ai-send').addEventListener('click', send);
$('vv-ai-input').addEventListener('keydown', e => {