इसे छोड़कर कंटेंट पर जाएं

सत्र संग्रहण और प्रविष्टि मॉडल

यह दस्तावेज़ इस बात का प्रामाणिक स्रोत है कि coding-agent सत्रों को रनटाइम पर कैसे प्रस्तुत, स्थायी, माइग्रेट और पुनर्निर्मित किया जाता है।

इसमें शामिल है:

  • सत्र JSONL प्रारूप और संस्करण
  • प्रविष्टि वर्गीकरण और ट्री सिमैंटिक्स (id/parentId + लीफ पॉइंटर)
  • पुरानी या विकृत फ़ाइलें लोड करते समय माइग्रेशन/संगतता व्यवहार
  • संदर्भ पुनर्निर्माण (buildSessionContext)
  • स्थायित्व गारंटी, विफलता व्यवहार, ट्रंकेशन/ब्लॉब बाह्यकरण
  • संग्रहण अमूर्तन (FileSessionStorage, MemorySessionStorage) और संबंधित उपयोगिताएँ

इसमें शामिल नहीं है: /tree UI रेंडरिंग व्यवहार, सत्र डेटा को प्रभावित करने वाले सिमैंटिक्स से परे।

कार्यान्वयन फ़ाइलें

Section titled “कार्यान्वयन फ़ाइलें”

डिफ़ॉल्ट सत्र फ़ाइल स्थान:

~/.xcsh/agent/sessions/--<cwd-encoded>--/<timestamp>_<sessionId>.jsonl

<cwd-encoded> कार्यशील निर्देशिका से अग्रणी स्लैश हटाकर और /, \\, तथा : को - से बदलकर प्राप्त किया जाता है।

ब्लॉब स्टोर स्थान:

~/.xcsh/agent/blobs/<sha256>

टर्मिनल ब्रेडक्रम्ब फ़ाइलें यहाँ लिखी जाती हैं:

~/.xcsh/agent/terminal-sessions/<terminal-id>

ब्रेडक्रम्ब सामग्री दो पंक्तियाँ हैं: मूल cwd, फिर सत्र फ़ाइल पथ। continueRecent() सबसे हाल के mtime को स्कैन करने से पहले इस टर्मिनल-स्कोप्ड पॉइंटर को प्राथमिकता देता है।

सत्र फ़ाइलें JSONL हैं: प्रति पंक्ति एक JSON ऑब्जेक्ट।

  • पंक्ति 1 हमेशा सत्र हेडर (type: "session") होती है।
  • शेष पंक्तियाँ SessionEntry मान हैं।
  • रनटाइम पर प्रविष्टियाँ केवल-जोड़ने योग्य हैं; शाखा नेविगेशन मौजूदा प्रविष्टियों को बदलने के बजाय एक पॉइंटर (leafId) को स्थानांतरित करता है।
{
"type": "session",
"version": 3,
"id": "1f9d2a6b9c0d1234",
"timestamp": "2026-02-16T10:20:30.000Z",
"cwd": "/work/pi",
"title": "optional session title",
"parentSession": "optional lineage marker"
}

नोट:

  • v1 फ़ाइलों में version वैकल्पिक है; अनुपस्थिति का अर्थ v1 है।
  • parentSession एक अपारदर्शी वंशावली स्ट्रिंग है। वर्तमान कोड प्रवाह (fork, forkFrom, createBranchedSession, या स्पष्ट newSession({ parentSession })) के आधार पर या तो सत्र id या सत्र पथ लिखता है। इसे मेटाडेटा मानें, टाइप्ड विदेशी कुंजी नहीं।

प्रविष्टि आधार (SessionEntryBase)

Section titled “प्रविष्टि आधार (SessionEntryBase)”

सभी गैर-हेडर प्रविष्टियों में शामिल है:

{
"type": "...",
"id": "8-char-id",
"parentId": "previous-or-branch-parent",
"timestamp": "2026-02-16T10:20:30.000Z"
}

parentId मूल प्रविष्टि (पहला जोड़, या resetLeaf() के बाद) के लिए null हो सकता है।

प्रविष्टि वर्गीकरण

Section titled “प्रविष्टि वर्गीकरण”

SessionEntry इनका संघ है:

  • message
  • thinking_level_change
  • model_change
  • compaction
  • branch_summary
  • custom
  • custom_message
  • label
  • ttsr_injection
  • session_init
  • mode_change

सीधे एक AgentMessage संग्रहीत करता है।

{
"type": "message",
"id": "a1b2c3d4",
"parentId": null,
"timestamp": "2026-02-16T10:21:00.000Z",
"message": {
"role": "assistant",
"provider": "anthropic",
"model": "claude-sonnet-4-5",
"content": [{ "type": "text", "text": "Done." }],
"usage": { "input": 100, "output": 20, "cacheRead": 0, "cacheWrite": 0, "cost": { "input": 0, "output": 0, "cacheRead": 0, "cacheWrite": 0, "total": 0 } },
"timestamp": 1760000000000
}
}
{
"type": "model_change",
"id": "b1c2d3e4",
"parentId": "a1b2c3d4",
"timestamp": "2026-02-16T10:21:30.000Z",
"model": "openai/gpt-4o",
"role": "default"
}

role वैकल्पिक है; संदर्भ पुनर्निर्माण में अनुपस्थित को default माना जाता है।

{
"type": "thinking_level_change",
"id": "c1d2e3f4",
"parentId": "b1c2d3e4",
"timestamp": "2026-02-16T10:22:00.000Z",
"thinkingLevel": "high"
}
{
"type": "compaction",
"id": "d1e2f3a4",
"parentId": "c1d2e3f4",
"timestamp": "2026-02-16T10:23:00.000Z",
"summary": "Conversation summary",
"shortSummary": "Short recap",
"firstKeptEntryId": "a1b2c3d4",
"tokensBefore": 42000,
"details": { "readFiles": ["src/a.ts"] },
"preserveData": { "hookState": true },
"fromExtension": false
}
{
"type": "branch_summary",
"id": "e1f2a3b4",
"parentId": "a1b2c3d4",
"timestamp": "2026-02-16T10:24:00.000Z",
"fromId": "a1b2c3d4",
"summary": "Summary of abandoned path",
"details": { "note": "optional" },
"fromExtension": true
}

यदि मूल से शाखा बना रहे हैं (branchFromId === null), तो fromId शाब्दिक स्ट्रिंग "root" होता है।

एक्सटेंशन स्थिति स्थायित्व; buildSessionContext द्वारा अनदेखा किया जाता है।

{
"type": "custom",
"id": "f1a2b3c4",
"parentId": "e1f2a3b4",
"timestamp": "2026-02-16T10:25:00.000Z",
"customType": "my-extension",
"data": { "state": 1 }
}

एक्सटेंशन-प्रदत्त संदेश जो LLM संदर्भ में भाग लेता है।

{
"type": "custom_message",
"id": "a2b3c4d5",
"parentId": "f1a2b3c4",
"timestamp": "2026-02-16T10:26:00.000Z",
"customType": "my-extension",
"content": "Injected context",
"display": true,
"details": { "debug": false }
}
{
"type": "label",
"id": "b2c3d4e5",
"parentId": "a2b3c4d5",
"timestamp": "2026-02-16T10:27:00.000Z",
"targetId": "a1b2c3d4",
"label": "checkpoint"
}

label: undefined targetId के लिए एक लेबल साफ़ करता है।

{
"type": "ttsr_injection",
"id": "c2d3e4f5",
"parentId": "b2c3d4e5",
"timestamp": "2026-02-16T10:28:00.000Z",
"injectedRules": ["ruleA", "ruleB"]
}
{
"type": "session_init",
"id": "d2e3f4a5",
"parentId": "c2d3e4f5",
"timestamp": "2026-02-16T10:29:00.000Z",
"systemPrompt": "...",
"task": "...",
"tools": ["read", "edit"],
"outputSchema": { "type": "object" }
}
{
"type": "mode_change",
"id": "e2f3a4b5",
"parentId": "d2e3f4a5",
"timestamp": "2026-02-16T10:30:00.000Z",
"mode": "plan",
"data": { "planFile": "/tmp/plan.md" }
}

संस्करण और माइग्रेशन

Section titled “संस्करण और माइग्रेशन”

वर्तमान सत्र संस्करण: 3

तब लागू होता है जब हेडर version अनुपस्थित हो या < 2:

  • प्रत्येक गैर-हेडर प्रविष्टि में id और parentId जोड़ता है।
  • फ़ाइल क्रम का उपयोग करके एक रैखिक पैरेंट श्रृंखला पुनर्निर्मित करता है।
  • जब मौजूद हो तो संघनन फ़ील्ड firstKeptEntryIndex -> firstKeptEntryId माइग्रेट करता है।
  • हेडर version = 2 सेट करता है।

तब लागू होता है जब हेडर version < 3:

  • message प्रविष्टियों के लिए: विरासत message.role === "hookMessage" को "custom" में पुनर्लिखित करता है।
  • हेडर version = 3 सेट करता है।

माइग्रेशन ट्रिगर और स्थायित्व

Section titled “माइग्रेशन ट्रिगर और स्थायित्व”
  • माइग्रेशन सत्र लोड (setSessionFile) के दौरान चलते हैं।
  • यदि कोई माइग्रेशन चला, तो पूरी फ़ाइल तुरंत डिस्क पर पुनर्लिखित की जाती है।
  • माइग्रेशन पहले इन-मेमोरी प्रविष्टियों को बदलता है, फिर पुनर्लिखित JSONL को स्थायी करता है।

लोड और संगतता व्यवहार

Section titled “लोड और संगतता व्यवहार”

loadEntriesFromFile(path) व्यवहार:

  • अनुपस्थित फ़ाइल (ENOENT) -> [] लौटाता है।
  • गैर-पार्स करने योग्य पंक्तियों को उदार JSONL पार्सर (parseJsonlLenient) द्वारा संभाला जाता है।
  • यदि पहली पार्स की गई प्रविष्टि एक मान्य सत्र हेडर नहीं है (type !== "session" या स्ट्रिंग id अनुपस्थित) -> [] लौटाता है।

SessionManager.setSessionFile() व्यवहार:

  • लोडर से [] को खाली/अस्तित्वहीन सत्र माना जाता है और उस पथ पर एक नई आरंभीकृत सत्र फ़ाइल से बदल दिया जाता है।
  • मान्य फ़ाइलें लोड की जाती हैं, आवश्यकता होने पर माइग्रेट की जाती हैं, ब्लॉब रेफ़ हल किए जाते हैं, फिर अनुक्रमित किए जाते हैं।

ट्री और लीफ सिमैंटिक्स

Section titled “ट्री और लीफ सिमैंटिक्स”

अंतर्निहित मॉडल केवल-जोड़ने योग्य ट्री + परिवर्तनशील लीफ पॉइंटर है:

  • प्रत्येक जोड़ने की विधि ठीक एक नई प्रविष्टि बनाती है जिसका parentId वर्तमान leafId होता है।
  • नई प्रविष्टि नया leafId बन जाती है।
  • branch(entryId) केवल leafId को स्थानांतरित करता है; मौजूदा प्रविष्टियाँ अपरिवर्तित रहती हैं।
  • resetLeaf() leafId = null सेट करता है; अगला जोड़ एक नई मूल प्रविष्टि (parentId: null) बनाता है।
  • branchWithSummary() लीफ को शाखा लक्ष्य पर सेट करता है और एक branch_summary प्रविष्टि जोड़ता है।

getEntries() सभी गैर-हेडर प्रविष्टियों को सम्मिलन क्रम में लौटाता है। सामान्य संचालन में मौजूदा प्रविष्टियाँ हटाई नहीं जातीं; पुनर्लेखन प्रतिनिधित्व को अपडेट करते हुए तार्किक इतिहास को संरक्षित करते हैं (माइग्रेशन, स्थानांतरण, लक्षित पुनर्लेखन सहायक)।

संदर्भ पुनर्निर्माण (buildSessionContext)

Section titled “संदर्भ पुनर्निर्माण (buildSessionContext)”

buildSessionContext(entries, leafId, byId?) यह निर्धारित करता है कि मॉडल को क्या भेजा जाता है।

एल्गोरिदम:

  1. लीफ निर्धारित करें:
    • leafId === null -> खाली संदर्भ लौटाएँ।
    • स्पष्ट leafId -> यदि मिले तो उस प्रविष्टि का उपयोग करें।
    • अन्यथा अंतिम प्रविष्टि पर फ़ॉलबैक।
  2. लीफ से मूल तक parentId श्रृंखला पर चलें और मूल->लीफ पथ के लिए उलटें।
  3. पथ पर रनटाइम स्थिति प्राप्त करें:
    • नवीनतम thinking_level_change से thinkingLevel (डिफ़ॉल्ट "off")
    • model_change प्रविष्टियों से मॉडल मैप (role ?? "default")
    • यदि कोई स्पष्ट मॉडल परिवर्तन नहीं है तो सहायक संदेश provider/model से फ़ॉलबैक models.default
    • सभी ttsr_injection प्रविष्टियों से डिडुप्लिकेटेड injectedTtsrRules
    • नवीनतम mode_change से mode/modeData (डिफ़ॉल्ट मोड "none")
  4. संदेश सूची बनाएँ:
    • message प्रविष्टियाँ सीधे पास होती हैं
    • custom_message प्रविष्टियाँ createCustomMessage के माध्यम से custom AgentMessages बनती हैं
    • branch_summary प्रविष्टियाँ createBranchSummaryMessage के माध्यम से branchSummary AgentMessages बनती हैं
    • यदि पथ पर compaction मौजूद है:
      • पहले संघनन सारांश प्रकाशित करें (createCompactionSummaryMessage)
      • संघनन सीमा तक firstKeptEntryId से शुरू होने वाली पथ प्रविष्टियाँ प्रकाशित करें
      • संघनन सीमा के बाद की प्रविष्टियाँ प्रकाशित करें

custom और session_init प्रविष्टियाँ सीधे मॉडल संदर्भ में इंजेक्ट नहीं करतीं।

स्थायित्व गारंटी और विफलता मॉडल

Section titled “स्थायित्व गारंटी और विफलता मॉडल”

स्थायी बनाम इन-मेमोरी

Section titled “स्थायी बनाम इन-मेमोरी”
  • SessionManager.create/open/continueRecent/forkFrom -> स्थायी मोड (persist = true)।
  • SessionManager.inMemory -> गैर-स्थायी मोड (persist = false) MemorySessionStorage के साथ।

लेखन एक आंतरिक प्रॉमिस श्रृंखला (#persistChain) और NdjsonFileWriter के माध्यम से क्रमबद्ध होते हैं।

  • append* इन-मेमोरी स्थिति को तुरंत अपडेट करता है।
  • कम से कम एक सहायक संदेश मौजूद होने तक स्थायित्व स्थगित रहता है।
    • पहले सहायक से पहले: प्रविष्टियाँ मेमोरी में रखी जाती हैं; कोई फ़ाइल जोड़ नहीं होता।
    • जब पहला सहायक मौजूद हो: पूर्ण इन-मेमोरी सत्र फ़ाइल में फ़्लश किया जाता है।
    • उसके बाद: नई प्रविष्टियाँ क्रमिक रूप से जोड़ी जाती हैं।

कोड में तर्काधार: ऐसे सत्रों को स्थायी करने से बचें जिन्होंने कभी सहायक प्रतिक्रिया उत्पन्न नहीं की।

स्थायित्व संचालन

Section titled “स्थायित्व संचालन”
  • flush() राइटर को फ़्लश करता है और fsync() कॉल करता है।
  • परमाणु पूर्ण पुनर्लेखन (#rewriteFile) अस्थायी फ़ाइल में लिखते हैं, flush+fsync करते हैं, बंद करते हैं, फिर लक्ष्य पर नाम बदलते हैं।
  • माइग्रेशन, setSessionName, rewriteEntries, स्थानांतरण संचालन, और टूल-कॉल आर्ग पुनर्लेखन के लिए उपयोग किया जाता है।
  • स्थायित्व त्रुटियाँ लैच की जाती हैं (#persistError) और बाद के संचालनों पर पुनः फेंकी जाती हैं।
  • पहली त्रुटि सत्र फ़ाइल संदर्भ के साथ एक बार लॉग की जाती है।
  • राइटर बंद करना सर्वोत्तम-प्रयास है लेकिन पहली सार्थक त्रुटि को प्रसारित करता है।

डेटा आकार नियंत्रण और ब्लॉब बाह्यकरण

Section titled “डेटा आकार नियंत्रण और ब्लॉब बाह्यकरण”

प्रविष्टियों को स्थायी करने से पहले:

  • बड़ी स्ट्रिंग्स को MAX_PERSIST_CHARS (500,000 वर्ण) तक सूचना के साथ ट्रंकेट किया जाता है:
    • "[Session persistence truncated large content]"
  • क्षणिक फ़ील्ड partialJson और jsonlEvents हटा दिए जाते हैं।
  • यदि ऑब्जेक्ट में content और lineCount दोनों हैं, तो ट्रंकेशन के बाद लाइन काउंट पुनर्गणना किया जाता है।
  • content सरणियों में base64 लंबाई >= 1024 वाले इमेज ब्लॉक ब्लॉब रेफ़ में बाह्यकृत किए जाते हैं:
    • blob:sha256:<hash> के रूप में संग्रहीत
    • कच्चे बाइट ब्लॉब स्टोर (BlobStore.put) में लिखे जाते हैं

लोड पर, message/custom_message इमेज ब्लॉक के लिए ब्लॉब रेफ़ वापस base64 में हल किए जाते हैं।

संग्रहण अमूर्तन

Section titled “संग्रहण अमूर्तन”

SessionStorage इंटरफ़ेस SessionManager द्वारा उपयोग किए जाने वाले सभी फ़ाइलसिस्टम संचालन प्रदान करता है:

  • तुल्यकालिक: ensureDirSync, existsSync, writeTextSync, statSync, listFilesSync
  • अतुल्यकालिक: exists, readText, readTextPrefix, writeText, rename, unlink, openWriter

कार्यान्वयन:

  • FileSessionStorage: वास्तविक फ़ाइलसिस्टम (Bun + node fs)
  • MemorySessionStorage: परीक्षणों/गैर-स्थायी सत्रों के लिए मैप-समर्थित इन-मेमोरी कार्यान्वयन

SessionStorageWriter writeLine, flush, fsync, close, getError प्रकट करता है।

सत्र खोज उपयोगिताएँ

Section titled “सत्र खोज उपयोगिताएँ”

session-manager.ts में परिभाषित:

  • getRecentSessions(sessionDir, limit) -> UI/सत्र चयनकर्ता के लिए हल्का मेटाडेटा
  • findMostRecentSession(sessionDir) -> mtime द्वारा नवीनतम
  • list(cwd, sessionDir?) -> एक प्रोजेक्ट दायरे में सत्र
  • listAll() -> ~/.xcsh/agent/sessions के अंतर्गत सभी प्रोजेक्ट दायरों में सत्र

मेटाडेटा निष्कर्षण जहाँ संभव हो केवल एक उपसर्ग (readTextPrefix(..., 4096)) पढ़ता है।

संबंधित लेकिन भिन्न: प्रॉम्प्ट इतिहास संग्रहण

Section titled “संबंधित लेकिन भिन्न: प्रॉम्प्ट इतिहास संग्रहण”

HistoryStorage (history-storage.ts) प्रॉम्प्ट स्मरण/खोज के लिए एक अलग SQLite उपप्रणाली है, सत्र पुनर्चालन के लिए नहीं।

  • DB: ~/.xcsh/agent/history.db
  • तालिका: history(id, prompt, created_at, cwd)
  • FTS5 अनुक्रमणिका: ट्रिगर-अनुरक्षित सिंक के साथ history_fts
  • इन-मेमोरी अंतिम-प्रॉम्प्ट कैश का उपयोग करके लगातार समान प्रॉम्प्ट्स को डिडुप्लिकेट करता है
  • अतुल्यकालिक सम्मिलन (setImmediate) ताकि प्रॉम्प्ट कैप्चर टर्न निष्पादन को अवरुद्ध न करे

वार्तालाप ग्राफ/स्थिति पुनर्चालन के लिए सत्र फ़ाइलों का उपयोग करें; प्रॉम्प्ट इतिहास UX के लिए HistoryStorage का उपयोग करें।