openclaw - 💡(How to fix) Fix [Feature]: Persist sessionKey in session transcript JSONL header (orphan recovery + plugin identity) [1 comments, 2 participants]

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…
GitHub stats
openclaw/openclaw#84209Fetched 2026-05-20 03:42:43
View on GitHub
Comments
1
Participants
2
Timeline
1
Reactions
1
Author
Timeline (top)
commented ×1

Persist sessionKey in the existing session JSONL header line so a .jsonl transcript can self-identify which logical conversation produced it, without depending on sessions.json. This restores the work tracked by the previously-filed but accidentally-orphaned issues #55849 and #55993 (see "Prior art" below).

Root Cause

  • #55849 "Session transcript headers should include sessionKey for recovery/audit" — opened 2026-03-27, closed 2026-04-26 by clawsweeper as a duplicate of #55993.
  • #55993 "Include sessionKey and metadata in session log file header" — opened 2026-03-27, closed 2026-04-26 by clawsweeper as a duplicate of #55849 — on the same day, in the same automated review pass. As a result both issues ended up closed pointing at each other, and the canonical tracking item effectively disappeared.
  • PR #56082 "Fix #55802 and #55849: exec obfuscation config and sessionKey in transcript header" — opened 2026-04-09, closed unmerged 2026-04-12. Greptile and Codex automated reviews both rated it "safe to merge" (5/5 confidence). No public reason is recorded for the closure; the most likely cause is the PR bundled two unrelated fixes (#55802 + #55849) and was returned for splitting. The implementation itself was clean and could be revived; for the transcript-header half, it would additionally need to cover the third header writer (transcript-append.ts:175) that the original PR missed.

Code Example

{"type":"session","version":3,"id":"<sessionId>","timestamp":"...","cwd":"..."}

---

{
  "type": "session",
  "version": 4,
  "id": "<sessionId>",
  "sessionKey": "agent:main:main",
  "agentId": "main",
  "timestamp": "...",
  "cwd": "..."
}
RAW_BUFFERClick to expand / collapse

Summary

Persist sessionKey in the existing session JSONL header line so a .jsonl transcript can self-identify which logical conversation produced it, without depending on sessions.json. This restores the work tracked by the previously-filed but accidentally-orphaned issues #55849 and #55993 (see "Prior art" below).

Problem to solve

Today, the very first line of every session transcript is a free-form type: "session" header, and on main (2026.5.10-beta.1, commit 0fcddd3974) it contains only:

{"type":"session","version":3,"id":"<sessionId>","timestamp":"...","cwd":"..."}

The semantic identity of the conversation — its sessionKey (e.g. agent:main:main, agent:main:cron:<jobId>, agent:main:explicit:<topicId>) — is not written anywhere on disk inside the JSONL itself. The only mapping from sessionId (the JSONL UUID) back to sessionKey lives in <state-dir>/agents/<agentId>/sessions/sessions.json, which is mutated in place by the gateway.

Concrete consequences:

  1. Orphaned transcripts are anonymous after /reset or compaction rotation. When a sessionKey is reset, the store entry is overwritten with a new sessionId; the old .jsonl lingers on disk with no way to determine which conversation it belonged to. The original report on #55849 cited a real instance of 705 transcript files vs. 55 store entries, i.e. ~92% of historical transcripts were unidentifiable. We see the same pattern on our test environment.

  2. External readers cannot identify a transcript without the store. Anything reading transcripts from another process, machine, snapshot, or backup (analytics, audit, dashboards, migration tools, plugins that ship transcripts off-host) has to carry sessions.json alongside the JSONL, and the store is racy — it gets rewritten by the live gateway. A self-describing JSONL would make every transcript file portable.

  3. Channel plugins are forced to maintain a parallel mapping. In the CoClaw plugin we currently keep a separate coclaw-chat-history.json whose only real job is to remember which sessionKey produced which sessionId(s) over time — so we can reconstruct the chat history for an orphan once the upstream sessions.json has forgotten it. If the JSONL header carried sessionKey, that file would not need to exist. Any plugin that needs to attribute a transcript to a logical conversation will end up reinventing the same shim.

  4. Audit / compliance scenarios. Knowing which logical session a transcript belongs to is exactly the metadata you would want at audit time (who/what/when), and it is exactly what is missing in the current header.

This is fundamental information about a .jsonl that we believe OpenClaw should preserve as a baseline platform capability, not push to every consumer.

Proposed solution

Extend the existing session header line (it is already a free-form JSON object, no schema version bump strictly required, though bumping version is a clean signal). Concretely, add one required and one recommended field:

{
  "type": "session",
  "version": 4,
  "id": "<sessionId>",
  "sessionKey": "agent:main:main",
  "agentId": "main",
  "timestamp": "...",
  "cwd": "..."
}
  • sessionKey is the primary ask. It is already in scope at every header-creation site.
  • agentId is a useful companion (cheap, derivable from sessionKey, but saves consumers from re-parsing).

This is backward compatible: readers that don't know the new fields ignore them. The header line is already heterogeneous across versions, so adding optional fields fits the existing pattern.

Three header-writing sites currently produce the persisted header, all of which already have sessionKey available (or trivially resolvable) in surrounding scope:

FileFunctionStatus in PR #56082
src/config/sessions/transcript.ts:35ensureSessionHeaderWas updated
src/agents/pi-embedded-helpers/bootstrap.ts:244ensureSessionHeader (exported)Was updated
src/config/sessions/transcript-append.ts:175ensureTranscriptHeaderWas missed — also needs to receive sessionKey to stay symmetric

Reset / compaction paths that rotate transcripts (e.g. src/gateway/session-reset-service.ts:637) need the same treatment so the rotated successor file is identified, not just the original.

Impact

  • Orphan recovery. A grep -l '"sessionKey":"agent:main:main"' over the sessions directory becomes a one-liner. Today this requires content sniffing or recovering sessions.json snapshots.
  • Plugin simplification. Channel plugins (CoClaw, and by induction every other channel plugin that wants to expose chat history beyond the live session) can drop bespoke sessionKey ↔ sessionId mapping files entirely.
  • Audit / dashboard / cleanup tooling. Anything that wants to classify transcripts by kind (main / cron / subagent / topic / explicit) can do so from the header alone, without depending on the gateway being up or sessions.json being current. (kind and channel are obviously derivable from the parsed sessionKey; we are intentionally not asking for them as separate fields here — sessionKey alone is sufficient.)
  • Portability. Transcripts shipped across hosts (debug dumps, support bundles) become self-identifying.
  • Pi-embedded path. Subagent and embedded-runner transcripts identify their parent conversation, which is currently only inferable by re-traversing the runtime tree.

Prior art / why this issue is being re-filed

This feature has been requested twice before and once nearly implemented, but the artifacts were accidentally orphaned by automation. Reopening as a fresh request to make the history visible and avoid the same outcome:

  • #55849 "Session transcript headers should include sessionKey for recovery/audit" — opened 2026-03-27, closed 2026-04-26 by clawsweeper as a duplicate of #55993.
  • #55993 "Include sessionKey and metadata in session log file header" — opened 2026-03-27, closed 2026-04-26 by clawsweeper as a duplicate of #55849 — on the same day, in the same automated review pass. As a result both issues ended up closed pointing at each other, and the canonical tracking item effectively disappeared.
  • PR #56082 "Fix #55802 and #55849: exec obfuscation config and sessionKey in transcript header" — opened 2026-04-09, closed unmerged 2026-04-12. Greptile and Codex automated reviews both rated it "safe to merge" (5/5 confidence). No public reason is recorded for the closure; the most likely cause is the PR bundled two unrelated fixes (#55802 + #55849) and was returned for splitting. The implementation itself was clean and could be revived; for the transcript-header half, it would additionally need to cover the third header writer (transcript-append.ts:175) that the original PR missed.

Net state on current main: feature was requested → triaged-as-duplicate → re-triaged-as-duplicate → implementation submitted → closed unmerged → no remaining tracking item. Hence this filing.

Additional information

Environment of the report:

  • OpenClaw 2026.5.7 (installed) / 2026.5.10-beta.1 (repo main, commit 0fcddd3974).
  • Linux (WSL2). Header writers verified by source inspection at the cited lines.
  • Reproduction is trivial: open any <state-dir>/agents/<agentId>/sessions/<uuid>.jsonl, observe the header line.

We're happy to test a PR against the CoClaw plugin's chat-history layer to verify that removing our coclaw-chat-history.json shim works end-to-end against the new header.


Reported by the CoClaw team. This issue was discovered while developing @coclaw/openclaw-coclaw, a CoClaw channel plugin for OpenClaw.

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING

openclaw - 💡(How to fix) Fix [Feature]: Persist sessionKey in session transcript JSONL header (orphan recovery + plugin identity) [1 comments, 2 participants]