Dokümanlar menüsü
GDPR / PII silme (purge)
Bir kişiyi tek çağrıyla silin (eraseSubject) ya da tek bir run/thread'in journal izini kalıcı silin (purgeRun/purgeThread) — journal'ın append-only felsefesinin yasal silme için tek istisnası. purgeRun özyineleme ile alt-agent/network çocuklarını da kaskad siler.
Ne işe yarar / ne zaman kullanılır#
GNL journal'ı normalde append-only'dır (bkz. deterministik replay, exactly-once araçlar) — kayıtlar silinmez, üzerine yazılmaz. Ancak bir kullanıcı GDPR kapsamında "beni unut" talebinde bulunduğunda ya da bir run'a kazayla PII (kişisel veri) girdiğinde, bu izin kalıcı olarak silinmesi gerekir. purgeRun ve purgeThread tam bu senaryo içindir: journal'ın append-only kuralına bilinçli ve tek istisna olarak, ilgili run'ın veya thread'in tüm anahtar öneki (model/tool/input/wf/proc/cfg/memory girdileri) kalıcı olarak silinir — geri alınamaz. Bir kişinin silme talebi için eraseSubject kullanın: tek çağrı, o kişiyi storage'ın tuttuğu her depodan siler. purgeRun ve purgeThread tek bir run ya da tek bir thread için yerinde duruyor.
Kurulum / import#
import { eraseSubject, purgeRun, purgeThread } from '@gnldev/durable';Ayrı bir alt-paket yolu yok — purgeRun, purgeThread ve sweepRuns doğrudan @gnldev/durable kök export'undan gelir. Kullandığınız journal'ın (SqliteStorage, PostgresStorage, InMemoryStorage) deletePrefix portunu desteklemesi gerekir — üç yerleşik adaptör (InMemory/SQLite/Postgres) de sağlar; desteklemeyen bir journal verilirse net bir hata fırlatılır. eraseSubject da aynı kök export'tan gelir; arka plan paketleri kendi silicilerini getirir (@gnldev/queue'dan jobEraser, @gnldev/scheduler'dan triggerEraser, @gnldev/events'ten eventEraser).
Bir kişiyi tek çağrıyla silmek#
Silme talebi geldiğinde eraseSubject'i çağırın. Depoların listesini değil storage'ın kendisini alır; böylece tuttuğu hiçbir depo dışarıda kalamaz: journal, memory store, vektör store ve iş kaydı (work log). Storage'ın dışında kalanlar erasers ile gelir: arka plan paketlerinin kendi kayıtları ya da sizin bir deponuz.
import { InMemoryStorage, eraseSubject, toJournal } from '@gnldev/durable';
import { jobEraser } from '@gnldev/queue';
import { triggerEraser } from '@gnldev/scheduler';
import { eventEraser } from '@gnldev/events';
const storage = new InMemoryStorage();
const report = await eraseSubject(storage, 'ayse', {
orgId: 'acme', // omit for a person outside organizations
erasers: [jobEraser(storage), triggerEraser(toJournal(storage)), eventEraser(storage.work!)],
});
// { journalRows, workRecords, memoryThreads, workingMemory, unreachedThreads, byEraser: { jobs, triggers, events } }Kişinin run'larını; kendi thread'lerini (yazdığı başkalarına ait thread'leri değil) mesajları, gözlemleri, gözlemsel memory kayıtları ve vektörleriyle birlikte; hem 0.7 hem 0.6 anahtarlarıyla working memory'sini; dokümanlarını; sahibi olduğu iş ve olay kayıtlarını siler. Siliciler de arka plan paketlerinin tuttuğu iş, tetik ve olay kayıtlarını ekler.
KÖK storage'ı verin ve kişinin organizasyonunu orgId ile belirtin; organizasyon dışındaki bir kişi için boş bırakın. Storage'ın bir organizasyon görünümü reddedilir, çünkü sahipli iş ve olay kayıtları kök iş kaydında durur.
Tuttuğunu silemeyen bir depoyu hiçbir şey silmeden önce reddeder — deletePrefix'i olmayan bir journal, deleteIdPrefix'i olmayan bir iş deposu, silemeyen bir vektör store. Yarım kalmış bir silme sessizce yaşanmaz.
Rapor aranan şeyi değil, silineni sayar. Agent'larınızın memory'si BAŞKA bir storage üzerine kurulduysa, o storage'ı memory olarak verin. Verilmezse, kişinin sahibi olduğu ama mesajlarını ulaşılan hiçbir deponun tutmadığı thread'ler unreachedThreads içinde listelenir — liste boş değilse silme her şeye ULAŞAMAMIŞ demektir, diğer sayılar ne derse desin.
eraseSubject, çalıştığı anda orada olanı siler. Kişinin o sırada süren bir turu, silme döndükten sonra da yazabilir. Önce kişinin kimlik bilgisini iptal edin, sonra subjectRuns'a sorun: hiçbir run inFlight olmayana kadar bekleyin (ya da iptal edin), silin ve bir kez daha sorun.Kişinin tamamı yerine yalnızca bir run'ı ya da bir konuşmayı silmek için aşağıdaki purgeRun ve purgeThread'i kullanın.
Adım adım kullanım#
1. Bir run'ın tüm izini (o run'a ait tüm runId:* anahtarları + memory marker'ı) kalıcı silin:
import { purgeRun, purgeThread } from '@gnldev/durable';
const removed = await purgeRun(journal, 'run-123');
// removed: the number of journal keys deleted (parent + EVERY sub-agent/network child)Silmeden önce, eğer run bütçe sayacına eklenmişse (bkz. bütçe/kota) bu maliyet sayaçtan otomatik düşülür — aksi halde purge sonrası __usage__ bayat kalır ve silinen run'ın maliyeti hayalet olarak sayılmaya devam eder. Bu düşme yalnız run gerçekten sayaca eklenmişse yapılır; hiç sayılmamış (ör. hâlâ askıda onay bekleyen) bir run'ı yanlışlıkla eksiye düşürmez.
purgeRun ÖZYİNELEMELİDİR: bir run başka run'lara dallanmış olabilir — dinamik multi-agent yönlendirmenin (runNetwork) çocukları (net:<runId>:<i>) ya da statik agent_<ad> tool çağrılarının açtığı alt-agent run'ları (agent:<toolCallId>). Silmeden ÖNCE parent'ın journal'ı taranır (tool entry'lerinden ve net: önekli anahtarlardan), bulunan her çocuk kendi çocuklarıyla birlikte (hangi seviyede olursa olsun) kaskad silinir — döngü/tekrar emniyeti dahildir. Sonuç: bir kullanıcının GDPR "beni unut" talebi, o kullanıcının PII'si alt-agent çıktılarına sızmış olsa bile hiçbir seviyede yetim iz bırakmaz.
2. Bir thread'in (BasicMemory tabanlı) journal izini silin:
await purgeThread(journal, 'thread-abc');
// deletes mem:thread-abc:*, om:thread-abc:*, xthr:thread-abc:* and the thread's owner recordpurgeThread, thread'in journal izini siler: journal-tabanlı BasicMemory anahtarları (mem:<threadId>:*), gözlemsel memory'nin om: sayaçları ve saklanmış özetleri, xthr: dedup ailesi ve thread'in sahip kaydı. Ayrı bir zengin memory store kullanıyorsanız (ör. vektör tabanlı), o store'un kendi silme API'sini (AgentMemory.deleteThread) ayrıca çağırmanız gerekir — ya da kişinin tamamını eraseSubject ile silin.3. Studio üzerinden (yazılabilir journal + operatör yetkisi gerekir) tek bir run'ı silin:
DELETE /runs/:id
→ { ok: true, deleted: <number of keys removed> }API referansı#
eraseSubject(storage, resourceId, { orgId?, erasers?, memory? }) → Promise<EraseReport>. Bir kişiyi storage'ın tuttuğu her depodan (run'lar, thread'ler, 0.6 anahtarları dahil working memory, gözlemsel memory kayıtları ve vektörleri, dokümanlar, sahipli iş ve olay kayıtları) ve silicilerin ulaştığı yerlerden siler. Rapor: { journalRows, workRecords, memoryThreads, workingMemory, unreachedThreads, byEraser }. Silemeyen bir depoyu hiçbir şey silmeden reddeder.
purgeRun(journal, runId, seen?) → Promise<number>. Bir run'ın TÜM izini (<runId>:* + memory marker'ı) kalıcı siler; ÖZYİNELEMELİDİR — alt-agent (agent:<toolCallId>) ve network (net:<runId>:<i>) çocuklarını hangi seviyede olursa olsun kaskad siler (döngü emniyetli). Silmeden önce bütçe sayacından run maliyetini düşer (best-effort).
purgeThread(journal, threadId) → Promise<number>. Bir thread'in journal izini kalıcı siler: mem:<threadId>:*, gözlemsel memory'nin om: sayaçları ve saklanmış özetleri, xthr: dedup ailesi ve thread'in sahip kaydı; silinen anahtar sayısını döner.
sweepRuns(journal, { olderThanMs, keepSuspended?, now? }) → Promise<SweepResult>. Retention politikasına göre eski run'ları toplu purgeRun ile siler (bkz. retention/TTL süpürmesi).
DELETE /runs/:id, kök seviyesi (organizasyon kapsamsız) bir yönetim ucudur — ham journal üzerinde çalışır. Bu yüzden organizasyona bağlı bir kimlik bu ucu çağıramaz (403 döner); yalnızca organizasyona bağsız bir operatör run purge işlemi yapabilir — aksi halde bağlı kimlik başka bir organizasyonun run'ını da silebilirdi. İşlem geri alınamaz ve aktör + silinen kayıt sayısıyla birlikte run.purge olarak denetim (audit) kaydına düşer.