Minne och synkronisering
Minnet är en inbyggd arkitekturplugin i oHarness (memory), inte en MCP-server. Den registrerar minnesverktygen, indexet i systemprompten, återkallning per meddelande, valfri extrahering, sidan Memory och en synkronisering i bakgrunden, allt via samma PluginHost som modell-, verktygs- och granskningsplugins. Sidtillägget hålls litet: registrerade deskriptorer plus renderare som värden äger (för närvarande memory), inte godtycklig frontend-JavaScript eller ett iframe-SDK.
Var anteckningarna finns
En anteckning per Markdown-fil, med JSON-frontmatter:
---
{"id":"pricing-borderless-3fa","summary":"Pricing cards use fills, not borders","type":"feedback","tags":["design"],"createdAt":1789000000000,"updatedAt":1789000000000}
---
Cards on the pricing page are separated by fill and whitespace.
**Why:** the outlines were the loudest thing on a page of numbers.
**How to apply:** keep the border declared but transparent. See [[pricing-layout-a91]].
| Omfång | Katalog | Innehåller |
|---|---|---|
project | <project>/MEMORY/ | Beslut, begränsningar och referenser som flyttas och exporteras med projektet |
user | ~/.oharness/memory/user/ | Vem användaren är och hur hen vill arbeta, i alla projekt |
Varje registrerat projekt har .oharness/project.json. Dess stabila id håller sessioner på hemsidan, synktillstånd och återkallningsstatistik kopplade när mappen byter namn eller exporteras. Ett Git-repository använder från början sin rot-commit som det id:t; andra kataloger får ett slumpmässigt projekt-id. OHARNESS_HOME flyttar användaranteckningar och datorlokalt tillstånd, men flyttar aldrig projektets MEMORY/-katalog.
memory.dir (eller den äldre memoryDir) i konfigurationen skriver över projektkatalogen, relativt projektroten. När användarkatalogen inte kan skapas är projektanteckningarna fortfarande tillgängliga i <project>/MEMORY; bara anteckningar på användarnivå och minnestillstånd på hemsidan är otillgängliga, och en varning säger det.
Typer
| Typ | Standardomfång | Vad den registrerar |
|---|---|---|
user | användare | Användarens roll, expertis och arbetspreferenser |
feedback | projekt | En rättelse, eller ett arbetssätt som användaren bekräftat |
project | projekt | Ett beslut och skälet till det, en begränsning, pågående arbete som inte går att härleda ur koden |
reference | projekt | Var extern information finns |
Bara user-anteckningar hamnar som standard i användaromfånget. Feedback hamnar som standard i projektet, eftersom de flesta rättelser handlar om hur man arbetar med en viss kodbas; en som tydligt gäller överallt läggs medvetet i användaromfånget.
Anteckningar som skrevs innan typer fanns har ingen och fortsätter att fungera. type ingår i en antecknings innehållshash bara när den finns, så befintliga anteckningar behåller de hashar som deras synkbaslinjer redan har.
Skriva anteckningar
Modellen skriver med remember, läser med memory och tar bort med forget. remember från minnespluginen går igenom utan att fråga i alla behörighetslägen utom plan, som avvisar den som vilken annan skrivning som helst. En ask- eller deny-regel som nämner remember gäller fortfarande, forget frågar fortfarande, och ett verktyg från en annan plugin som heter remember får inget sådant undantag. Taggar trimmas, delas vid kommatecken och mellanslag, och dedupliceras innan anteckningen sparas. Systempromptens Memory-avsnitt säger när en anteckning är värd att skriva och när den inte är det. remember returnerar de befintliga anteckningar som är mest relaterade till det som just skrevs, flaggar en trolig dubblett och listar länkar som pekar på anteckningar som ännu inte finns. Allt som ser ut som en inloggningsuppgift avvisas. Anteckningar uppmanas att ha taggar för andra ord som någon kan använda: synonymer, och termen på det andra språk som används, eftersom återkallning matchar ord och inte betydelse.
Anteckningar länkar till varandra i brödtexten som [[note-id]]. Länkar härleds ur brödtexterna varje gång anteckningar läses in; inget om dem lagras separat, och synkprotokollet vet inte att de finns. En länk löses först upp i projektomfånget, sedan i användaromfånget. En länk till en anteckning som saknas behålls: den namnger oftast en anteckning som någon tänkt skriva.
Automatisk extrahering
Avstängd som standard, eftersom varje omgång är ett modellanrop på användarens egna krediter. Med memory.autoExtract.onStop lämnar slutet av en körning över konversationen sedan förra omgången till den lilla modellen, när minst minUserTurns riktiga användarmeddelanden har samlats sedan dess. Med onCompaction (eller onStop) lämnas den del av sträckan som komprimeringen ska till att ta bort, och som ingen omgång ännu har läst, över först, hur kort den än är, eftersom det är sista chansen att läsa den. En återupptagen session börjar räkna från där den återupptogs i stället för att extrahera hela sin historik igen. Det extraheras aldrig från subagenter.
Extraheraren får se de befintliga anteckningar som är mest relaterade till konversationen. De få mest relevanta kommer med hela sin brödtext, inom en storleksbudget, och bara de får uppdateras; resten listas med sammanfattning för länkning. En uppdatering ersätter brödtexten, så den ersatta versionen säkerhetskopieras först till ~/.oharness/memory-backups/. Dess svar behandlas som opålitligt: uppdateringar av id:n som den inte fick se tas bort, länkar till id:n som den inte fick se förlorar sina hakparenteser, inloggningsuppgifter maskeras, och nya anteckningar i samma omgång hänvisar till varandra som [[new:N]] tills de har id:n. Ett repositorys konfiguration kan stänga av extrahering; bara användarens egen konfiguration kan slå på den.
Extraheringen blir klar efter att körningen som utlöste den har avslutats, så de anteckningar den sparade meddelas i början av nästa meddelande: ”saved N memories from this conversation”, i alla gränssnitt.
Återkallning
Två vägar lägger anteckningar framför modellen:
- Indexet. I början av varje körning, en rad per anteckning, senast uppdaterad först, upp till
memory.indexCharstecken. Resten räknas, men går inte förlorade; verktygetmemorysöker i alla. - Återkallning per meddelande. Varje användarmeddelande poängsätts mot varje anteckning, och de bästa träffarna bifogas till meddelandet inuti
<recalled-memory>, med de anteckningar de länkar till listade med sammanfattning. Gränssnitten visar ”recalled N memories”. Inget bifogas när inget når över tröskeln, vilket är det vanliga fallet. Finns inga anteckningar alls ersätts indexet av två rader som säger när en ska skrivas, i stället för den fullständiga vägledningen.
Poängsättningen är lexikal och körs lokalt: ord stammas, CJK-text delas upp i överlappande par, och träffar i sammanfattningen och taggarna väger tyngre än träffar i brödtexten. En anteckning måste matcha i sin sammanfattning eller sina taggar för att bifogas överhuvudtaget. En anteckning som har bifogats en gång bifogas inte igen så länge ett meddelande som bär den finns kvar i transkriptet, även efter att sessionen återupptagits eller appen startats om; id:na läses tillbaka från meddelandenas metadata. Komprimering frigör de anteckningar vars meddelanden den sammanfattade bort. Bifogade anteckningar ligger i ett eget block i användarmeddelandet, registrerat i meddelandets metadata, så att transkript, sessionstitlar och webbhistoriken visar användarens egna ord. Subagenter återkallar inte.
Att återkallningen misslyckas hindrar aldrig att ett meddelande skickas.
Gränser
| Inställning | Standard | Betydelse |
|---|---|---|
memory.maxNotes | 5000 | Per omfång. Över gränsen läses de filer som ändrats längst tillbaka inte in, och en varning säger hur många; nya anteckningar avvisas tills några tagits bort |
memory.maxNoteBytes | 131072 | Brödtextens storlek; synkservern tillämpar också sin egen gräns |
memory.indexChars | 4000 | Tecken med sammanfattningar i systemprompten |
memory.recall.enabled | true | Återkallning per meddelande |
memory.recall.perTurn | 3 | Anteckningar som bifogas ett meddelande |
memory.recall.budgetChars | 2000 | Tecken som de anteckningarna får ta |
memory.recall.minScore | 1.2 | Tröskel för att bifoga |
memory.autoExtract.onCompaction / onStop | false | Automatisk extrahering |
memory.autoExtract.minUserTurns | 3 | Kortare konversationer hoppas över |
Hur ofta varje anteckning har återkallats sparas per dator i ~/.oharness/memory-usage/, utanför anteckningarna, så att återkallning aldrig orsakar en synkuppladdning. Det påverkar rankningen något och synkroniseras aldrig.
Molnsynkronisering
- Anteckningar fungerar alltid lokalt, utloggad och offline.
- Inloggning slår på automatisk synkronisering som standard. Synkroniseringen går åt båda hållen: lokala ändringar laddas upp, och ändringar som gjorts på andra enheter kommer ner av sig själva.
- Sidan Memory erbjuder 关闭云同步 och 立即同步. Reglaget gäller båda omfången. Avstängt består över omstarter och ny inloggning, och blockerar automatiska och manuella molnförfrågningar tills det uttryckligen slås på igen. Lokala verktyg och redigering fortsätter att fungera.
- Att stänga av synkroniseringen avbryter pågående förfrågningar. Data som Cloud redan har tagit emot kan inte dras tillbaka; att stänga av synkroniseringen tar inte bort kopior i molnet.
- Varje logiskt projekt-/användaromfång är låst till det första konto och den Cloud-origin som synkroniserade det. Byter du konto eller origin pausas synkroniseringen. Sidan erbjuder ingen osäker omtilldelning med ett klick.
Sidan underhåller ~/.oharness/plugins.json (OHARNESS_HOME skriver över detta), under runtimePlugins[id=memory].config.syncTargets[sha256(stableScopeKey)]:
{ "userId": "verified-account-label", "origin": "https://your-cloud.example", "enabled": false }
Lägg inte R2-nycklar eller Supabase-konfiguration i den här filen. Cloud-URL:en ligger kvar i cloud.json; R2-uppgifter finns bara i synkdriftsättningens miljö. För att stänga av hela pluginen (sidan, verktyg, prompter, återkallning och bakgrundsarbete), lägg till { "id": "memory", "enabled": false } i runtimePlugins; det träder i kraft nästa gång Harness skapas. Projektregistrering skapar fortfarande de vanliga tomma katalogerna MEMORY/ och ARTIFACTS/. För den som bäddar in är harness.memory projektlagret och harness.memoryUserService användarlagret; båda saknas när pluginen är avstängd. AgentOptions.memory är fortfarande ett inbäddnings-API på lägre nivå och får återkallning utan en pluginvärd.
När den synkroniserar
En koordinator per process schemalägger varje anteckningskatalog, oavsett hur många projekt en lokal webbserver eller skrivbordsappen har öppna. Varje katalog har en synktjänst i processen, så användarens egna anteckningar synkroniseras en gång i stället för en gång per öppet projekt.
- En lokal skrivning laddas upp ungefär tre sekunder senare; en serie skrivningar blir en omgång.
- Annars kontrolleras en katalog varje minut, och glesas ut till var femte minut så länge inget ändras på någon sida.
- Dina egna anteckningar kontrolleras alltid. Ett projekts anteckningar kontrolleras bara medan projektet används (ett meddelande skickat i det, eller sidan Memory öppen) och i trettio minuter efteråt.
- Processen håller sig till ungefär tjugo förfrågningar i minuten, vilket lämnar utrymme under synktjänstens gräns per konto för andra enheter.
- En
429pausar varje katalog fram till tiden i dessRetry-After, och sidan säger att synkroniseringen skedde för ofta och kommer att försöka igen, i stället för att skylla på konfigurationen.
立即同步 körs alltid direkt.
Vad sidan visar
Reglaget, ”senast synkroniserad” och fel gäller båda omfången: synkroniseringen räknas som på bara när den är på för båda, tiden som visas är den äldre av de två, och varje fel säger om det gäller projektanteckningar eller dina egna. Sidan visar också vad den senaste hämtningen förde ner (tillagt, ändrat, borttaget) och, på en gratisplan, planens gränser.
Städa anteckningar
Sidans lista 待整理 är en granskning som körs på den här datorn, utan modell:
| Fynd | Betydelse | Vad du kan göra |
|---|---|---|
| Samma ämne | Två till sex anteckningar i ett omfång vars sammanfattningar och taggar till stor del överlappar | Slå ihop, eller avfärda |
| Trasig länk | En [[id]] som inte namnger någon anteckning | Öppna anteckningen, eller avfärda |
| Fil saknas | En projektanteckning namnger en relativ sökväg som inte finns i projektet | Öppna, eller avfärda |
| Oanvänd | Varken återkallad eller redigerad på memory.consolidate.unusedDays (90) | Öppna, eller avfärda |
| För lång | En brödtext på över 8 000 tecken | Öppna, eller avfärda |
Ett avfärdande sparas lokalt mot anteckningarnas nuvarande versioner, så om du redigerar en av dem kommer fyndet tillbaka.
Sammanslagning öppnar en editor med ett utkast: den lilla modellens (生成合并建议) eller alla original sammanfogade (手动合并). När du tillämpar det behålls den äldsta anteckningens id, de andra tas bort, varje [[deleted]]-länk i det omfånget pekas om till den behållna anteckningen, och varje anteckning som ändras säkerhetskopieras. En sammanslagning som planerats utifrån anteckningar som har ändrats sedan dess avvisas.
Med molnsynkronisering på synkroniseras omfånget först och sammanslagningen genomförs i molnet före den här datorn (commitFirst), så att en redigering från en annan enhet däremellan avvisar sammanslagningen i stället för att bli en konflikt. Den sammanslagna brödtexten måste rymmas inom synktjänstens gräns per anteckning. Med synkroniseringen avstängd är sammanslagningen en vanlig lokal redigering.
Sammanslagning i bakgrunden är som standard på bara för betalda planer ("auto": "subscribers"): den körs när synktjänstens senaste svar sa att kontot, eller något team det tillhör, har en betald plan, och förblir av för gratiskonton och när planen är okänd (molnsynkronisering av, utloggad). true eller false skriver över det:
{ "memory": { "consolidate": { "auto": false, "cooldownHours": 24, "maxMerges": 3 } } }
När den är på startar en omgång två minuter efter start, högst en gång per vilotid, och slår ihop upp till maxMerges grupper av anteckningar med samma ämne med den lilla modellen, via samma väg som sidan. Den agerar aldrig på de andra fynden. En projektkonfiguration kan stänga av den men inte slå på den.
Säkerhetskopior och återställning
En kopia skrivs till ~/.oharness/memory-backups/ innan en anteckning ersätts eller tas bort utan att någon har valt den versionen: borttagning på sidan, forget, att ta molnets sida i en konflikt, en automatisk hämtning som ändrar eller tar bort en anteckning, en omskrivning från extraheraren, och en återställning som ersätter en nyare version. Sidan Memory listar de femtio senaste med när och varför, och återställer vilken som helst av dem till katalogen den kom från; anteckningen synkroniseras sedan som vilken annan lokal ändring som helst. Säkerhetskopior som skrevs innan skäl registrerades listas och kan också återställas.
Lagring och avstämning
Synkservern exponerar autentiserade /v1/memory/identity, /snapshot och /commit. Den lagrar inga databasrader. R2 innehåller oföränderliga Markdown-revisioner plus ett JSON-manifest som ersätts atomärt:
users/<server-verified-user-id>/projects/<project-id>/memory/
users/<server-verified-user-id>/memory/ user scope
manifest.json
objects/<note-id>/<revision-id>.md
Implementationer som använder ett annat identitets- eller lagringssystem får göra det; de exakta förfrågningsformerna, hashalgoritmen, CAS-beteendet och felkoderna specificeras i Synkprotokoll.
Den lokala baslinjen (~/.oharness/memory-sync/) möjliggör trevägsavstämning: oförändrade anteckningar hoppas över och ensidiga ändringar kopieras. Bredvid den behåller klienten en kopia av varje anteckning som den såg ut vid senaste synkroniseringen, så en anteckning som ändrats på båda sidor slås ihop i stället för att bara flaggas:
- sammanfattningen och typen kommer från den sida som ändrade dem;
- taggar kombineras som mängder, där det som någon sida lagt till behålls och det som någon sida tagit bort tas bort;
- brödtexter slås ihop radvis, och redigeringar som ligger isär kombineras;
- en redigering mot en borttagning behåller redigeringen, eftersom att ta bort igen är ett klick och en förlorad redigering är borta.
Båda versionerna säkerhetskopieras före en sammanslagning. Det som inte går att kombinera (två olika sammanfattningar eller typer, redigeringar på samma eller närliggande rader, en sammanslagning som är för stor för planen, eller en anteckning utan kopia av sin senast synkroniserade version) blir en konflikt. En konflikt håller bara tillbaka sin egen anteckning: allt annat laddas fortfarande upp och ner. Sidan Memory visar den senast synkroniserade versionen, den här datorns och molnets, och erbjuder att behålla den här datorns, ta molnets, behålla båda (molnets blir en ny anteckning bredvid) eller slå ihop för hand; det som läggs åt sidan säkerhetskopieras, oavsett vilken sida det kom från. Det finns ingen överskrivning där sista skrivningen vinner. Borttagningar är gravstenar, och manifestskrivningar använder ETag compare-and-swap.
Batchändringar som ingen granskar anteckning för anteckning (konsolidering) skrivs först i molnet: varje inblandad anteckning måste vara helt synkroniserad, commiten bär den manifestversion som kontrollerades, och först när tjänsten accepterar den skrivs batchen lokalt. En ändring från en annan enhet däremellan avvisas av tjänsten i stället för att bli en konflikt.
Förfrågningar listar hasharna för de anteckningar som den här datorn har (have); servern varken läser eller returnerar de brödtexterna, så en synkronisering där lite har ändrats överför lite. Commits delas upp enligt den ändringskvot som servern meddelar. Gravstenar som är äldre än serverns fönster tas bort från manifestet; en dator som kommer tillbaka efter det och hittar en anteckning som den senast såg synkroniserad, oförändrad, inte längre listad, tar bort den, medan en anteckning som den redigerat under tiden blir en konflikt. Två antecknings-id:n som bara skiljer sig i versaler och gemener avvisas, eftersom filsystemen i macOS och Windows inte kan rymma båda.
Synkroniseringen försöker igen vid tillfälliga fel efter en vilotid på 60 sekunder; manuell synkronisering kan försöka igen direkt. Konflikter väntar på användaren. Sidan säkerhetskopierar borttagna anteckningar, och förlorande lokala versioner i konflikter, under ~/.oharness/memory-backups/; forget gör detsamma. Direkta redigeringar i filsystemet stöds fortfarande, men en samtidig extern skrivning under en sammanslagning skyddas inte av det processlokala skrivlåset; håll en körande process per katalog.
R2 är privat och använder TLS, men detta är inte end-to-end-kryptering: Cloud kan läsa anteckningarnas innehåll. Bara anteckningar synkroniseras, inte sessionsloggar, filer eller inloggningsuppgifter.
Kvoter och planer
Servern äger sina kvoter och meddelar dem i varje svar; klienterna har ingen kopia. Standardvärden: 5000 aktiva anteckningar per projekt och per användare, 500 ändringar per commit, 128 KiB per brödtext, 16 MiB brödtexter per svar, 90 dagar per gravsten. En sänkt kvot blockerar bara tillväxt, så en användare som ligger över den kan alltid ta bort. Se Kör din egen synkserver för variablerna.
Gratiskonton har dessutom snävare gränser: ett antal anteckningar per projekt och per användare, och en storlek per anteckning, definierade i synktjänstens src/memory-entitlements.json. Betalda konton har ingen plangräns, bara kvoten ovan. Planen läses från ent-claimet som Cloud skriver in i åtkomsttoken: en betald plan som användaren själv har, eller som något team hen tillhör har, räknas som betald. Bara molnsynkroniseringen begränsas; anteckningar på datorn gör det inte, och ett gratiskonto över gränsen behåller varje lokal anteckning och slutar ladda upp nya, med ett meddelande som säger varför.
Ta bort ett konto
Tar du bort ett konto på webbplatsen raderas allt det har synkroniserat: sessionsflöden och minne i båda omfången. Gatewayen anropar synktjänstens POST /v1/admin/erase med en nyckel som bara den har (OHARNESS_SYNC_ADMIN_KEY på båda), innan själva kontot tas bort, och en gång till efteråt. Synktjänsten skriver först en markör för kontot, och avvisar från och med då dess flödes- och minnesförfrågningar, så att en enhet som fortfarande är inloggad inte kan ladda upp datan igen. Anteckningar och sessioner på dina egna datorer påverkas inte.
Att ta bort ett team gör detsamma för teamets delade sessionshistorik: gatewayen raderar feed/team-<id>/ (synktjänstens {team}-radering) efter att ha sagt upp teamets prenumeration och innan dess rader tas bort, och sedan en gång till. Synktjänsten avvisar teamets flöden från och med då, även för en token som fortfarande listar det. Team har inget minne.
Uppgradera
Uppgradera synkservern före klienterna. En ny klient faller tillbaka på det gamla protokollet mot en gammal server för projektanteckningar som saknar typ. Den kan inte synkronisera anteckningar på användarnivå, eller anteckningar med en typ, till en gammal server. En gammal klient som tar emot en typad anteckning från molnet slutar synkronisera med ett kontrollsummefel tills den uppgraderas.
Verifiering och driftsättning
Kör npm run test:memory, npm run test:sync-memory, npm run test:plugins, npm run typecheck och npm run build. script/memory-ui-demo.ts startar ett isolerat, utloggat gränssnitt på 4319 med en temporär hemkatalog; det läser aldrig användarens inloggningsuppgifter. Saknad R2-konfiguration ger ett synligt synkfel utan att lokalt minne slutar fungera. Starta om den lokala utvecklingsservern efter ändringar i källkoden.