Skip to content

fix(memory): export archived qmd session transcripts#57446

Merged
vincentkoc merged 3 commits into
mainfrom
fix/qmd-archived-session-export
Mar 30, 2026
Merged

fix(memory): export archived qmd session transcripts#57446
vincentkoc merged 3 commits into
mainfrom
fix/qmd-archived-session-export

Conversation

@vincentkoc

Copy link
Copy Markdown
Member

Summary

  • Problem: QMD session export only listed *.jsonl transcripts, so reset and deleted transcripts dropped out of QMD recall immediately after session resets.
  • Why it matters: on setups with daily resets, most historical session memory became silently unsearchable even though the transcript files still existed on disk.
  • What changed: listSessionFilesForAgent() now uses the existing usage-counted transcript helper, so live plus .jsonl.reset.* and .jsonl.deleted.* files are exported while backups and unrelated files stay excluded.
  • What did NOT change (scope boundary): this does not change session retention policy, nested archive traversal, or QMD indexing behavior beyond which transcript files are surfaced to export.

Change Type (select all)

  • Bug fix
  • Feature
  • Refactor required for the fix
  • Docs
  • Security hardening
  • Chore/infra

Scope (select all touched areas)

  • Gateway / orchestration
  • Skills / tool execution
  • Auth / tokens
  • Memory / storage
  • Integrations
  • API / contracts
  • UI / DX
  • CI/CD / infra

Linked Issue/PR

Root Cause / Regression History (if applicable)

  • Root cause: packages/memory-host-sdk/src/host/session-files.ts filtered strictly on name.endsWith(".jsonl"), which excluded the .jsonl.reset.* and .jsonl.deleted.* transcript shapes produced by session reset/delete flows.
  • Missing detection / guardrail: there was no direct regression test for listSessionFilesForAgent() covering archived transcript variants.
  • Prior context (git blame, prior PR, issue, or refactor if known): fix(qmd): include archived session transcripts in file listing #30273 and fix(memory): include archived session transcripts in QMD export #43947 both diagnosed the same stale suffix-only filter on the older path.
  • Why this regressed now: current main still carried the narrow filename filter after the memory host refactor.
  • If unknown, what was ruled out: N/A.

Regression Test Plan (if applicable)

  • Coverage level that should have caught this:
    • Unit test
    • Seam / integration test
    • End-to-end test
    • Existing coverage already sufficient
  • Target test or file: packages/memory-host-sdk/src/host/session-files.test.ts
  • Scenario the test should lock in: list live, reset, and deleted transcripts from the agent sessions root while excluding backups and non-transcript files.
  • Why this is the smallest reliable guardrail: the bug is purely in filename selection before QMD export, so a filesystem-level unit test on listSessionFilesForAgent() is enough.
  • Existing test that already covers this (if any): none.
  • If no new test is added, why not: N/A.

User-visible / Behavior Changes

  • QMD session-backed memory_search can now recall reset and deleted transcript history again instead of only active session files.

Diagram (if applicable)

Before:
[session reset] -> [file renamed to .jsonl.reset.*] -> [QMD export ignores it] -> [memory_search loses old session]

After:
[session reset] -> [file renamed to .jsonl.reset.*] -> [QMD export keeps it] -> [memory_search can still recall it]

Security Impact (required)

  • New permissions/capabilities? (No)
  • Secrets/tokens handling changed? (No)
  • New/changed network calls? (No)
  • Command/tool execution surface changed? (No)
  • Data access scope changed? (No)
  • If any Yes, explain risk + mitigation:

Repro + Verification

Environment

  • OS: macOS
  • Runtime/container: Node 22 + pnpm
  • Model/provider: N/A
  • Integration/channel (if any): N/A
  • Relevant config (redacted): memory.backend=qmd, memory.qmd.sessions.enabled=true

Steps

  1. Put active, .jsonl.reset.*, and .jsonl.deleted.* transcript files in the agent sessions root.
  2. Call listSessionFilesForAgent() through the QMD session export path.
  3. Verify archived reset/deleted transcripts are still listed.

Expected

  • Live, reset, and deleted transcript files remain in the QMD export source set.

Actual

  • Before this fix, only bare *.jsonl transcripts were included.

Evidence

  • Failing test/log before + passing after
  • Trace/log snippets
  • Screenshot/recording
  • Perf numbers (if relevant)

Human Verification (required)

  • Verified scenarios: pnpm test -- packages/memory-host-sdk/src/host/session-files.test.ts; pnpm build
  • Edge cases checked: .jsonl.reset.* and .jsonl.deleted.* are included; .jsonl.bak.*, sessions.json, non-JSONL files, and nested files remain excluded.
  • What you did not verify: live end-to-end QMD export on a daily-reset deployment.

Review Conversations

  • I replied to or resolved every bot review conversation I addressed in this PR.
  • I left unresolved only the conversations that still need reviewer or maintainer judgment.

Compatibility / Migration

  • Backward compatible? (Yes)
  • Config/env changes? (No)
  • Migration needed? (No)
  • If yes, exact upgrade steps:

Risks and Mitigations

  • Risk: including additional archived transcripts can increase QMD session-export volume for agents with long reset history.
    • Mitigation: the change reuses the existing usage-counted transcript helper, so it still excludes backups and unrelated artifacts while aligning export scope with current retention-managed transcript history.

@greptile-apps

greptile-apps Bot commented Mar 30, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR fixes a silent memory regression where QMD session export only surfaced bare *.jsonl transcripts, causing any session that had been reset or deleted (producing .jsonl.reset.* / .jsonl.deleted.* files) to fall out of memory_search recall immediately after the reset. The fix is a focused one-line change in listSessionFilesForAgent() that replaces the narrow .endsWith(".jsonl") guard with the existing isUsageCountedSessionTranscriptFileName helper. A new unit test is added that directly verifies all the boundary conditions.

Confidence Score: 5/5

Safe to merge — the fix is minimal and well-tested, with no behavioral changes beyond expanding the set of transcript files surfaced to QMD export.

The only finding is a P2 style issue (new test placed in the wrong describe block); there are no logic errors, correctness concerns, or data-integrity risks. The core fix correctly reuses the existing isUsageCountedSessionTranscriptFileName helper and the new test validates all the important boundary cases.

No files require special attention.

Important Files Changed

Filename Overview
packages/memory-host-sdk/src/host/session-files.ts Core one-line fix: replaces the narrow .endsWith('.jsonl') filter with isUsageCountedSessionTranscriptFileName, correctly expanding export to include .reset.* and .deleted.* transcript variants while still excluding .bak.* files and unrelated artifacts.
packages/memory-host-sdk/src/host/session-files.test.ts Adds a well-structured regression test covering the new inclusion of reset/deleted transcripts and the continued exclusion of backups, non-JSONL files, and nested files. Minor style issue: the new test is placed inside describe('buildSessionEntry') rather than its own describe('listSessionFilesForAgent') block.
CHANGELOG.md Changelog entry added for the fix under the correct Memory/QMD category; no issues.
Prompt To Fix All With AI
This is a comment left during a code review.
Path: packages/memory-host-sdk/src/host/session-files.test.ts
Line: 7

Comment:
**New test is nested under the wrong `describe` block**

The new `"includes reset and deleted transcripts in session file listing"` test (lines 26–50) exercises `listSessionFilesForAgent`, but it lives inside `describe("buildSessionEntry", ...)`. This makes the test report misleading (failures or results show up under `buildSessionEntry`) and obscures what is actually being tested.

Consider wrapping the new test in its own block:

```ts
describe("listSessionFilesForAgent", () => {
  // move the new test here
});
```

How can I resolve this? If you propose a fix, please make it concise.

Reviews (1): Last reviewed commit: "fix(memory): export archived qmd session..." | Re-trigger Greptile

Comment thread packages/memory-host-sdk/src/host/session-files.test.ts Outdated
@vincentkoc vincentkoc merged commit b7d59f7 into main Mar 30, 2026
8 checks passed
@vincentkoc vincentkoc deleted the fix/qmd-archived-session-export branch March 30, 2026 03:50
builderjarvis added a commit to saucesteals/openclaw that referenced this pull request Mar 30, 2026
@aisle-research-bot

aisle-research-bot Bot commented Mar 30, 2026

Copy link
Copy Markdown

🔒 Aisle Security Analysis

We found 1 potential security issue(s) in this PR:

# Severity Title
1 🟡 Medium Deleted/reset session transcripts included in QMD export and indexing (data retention/privacy regression)
Vulnerabilities

1. 🟡 Deleted/reset session transcripts included in QMD export and indexing (data retention/privacy regression)

Property Value
Severity Medium
CWE CWE-359
Location packages/memory-host-sdk/src/host/session-files.ts:26-30

Description

listSessionFilesForAgent() now includes archived transcript artifacts with .reset.<timestamp> and .deleted.<timestamp> suffixes via isUsageCountedSessionTranscriptFileName().

This introduces a privacy/data-retention risk because content a user expected to be removed after a “reset” or “delete” can be:

  • Exported to the QMD session export directory (Markdown files)
  • Indexed into the memory database used by memory_search (and potentially surfaced later)

Data flow / impact:

  • Source: transcript artifacts on disk, including *.jsonl.deleted.* and *.jsonl.reset.*
  • Collection: listSessionFilesForAgent() now returns those paths
  • Sinks:
    • extensions/memory-core/src/memory/qmd-manager.ts uses these files in exportSessions() to write *.md exports
    • extensions/memory-core/src/memory/manager-sync-ops.ts uses these files in syncSessionFiles() to index them into the memory DB

Vulnerable code:

// packages/memory-host-sdk/src/host/session-files.ts
.filter((name) => isUsageCountedSessionTranscriptFileName(name))

If .deleted indicates user-initiated deletion for privacy/compliance, retaining/searching/exporting those transcripts can violate user expectations and data retention policies (e.g., “right to be forgotten”).

Recommendation

Treat billing/usage accounting and search/export eligibility as separate concerns.

  • Keep isUsageCountedSessionTranscriptFileName() for cost/usage tracking only.
  • Introduce a stricter predicate for QMD export/indexing, e.g. exclude .deleted by default (and potentially .reset, depending on semantics), or gate inclusion behind an explicit configuration/consent flag.

Example:

import {
  isPrimarySessionTranscriptFileName,// or create isIndexableSessionTranscriptFileName
} from "../../../../src/config/sessions/artifacts.js";

export async function listSessionFilesForAgent(agentId: string): Promise<string[]> {
  const dir = resolveSessionTranscriptsDirForAgent(agentId);
  const entries = await fs.readdir(dir, { withFileTypes: true });
  return entries
    .filter((e) => e.isFile())
    .map((e) => e.name)// Only index/export primary transcripts by default
    .filter((name) => isPrimarySessionTranscriptFileName(name))
    .map((name) => path.join(dir, name));
}

If reset transcripts are intended to remain searchable, document that clearly and ensure .deleted is only used for soft delete (not user privacy delete). If .deleted is a hard delete signal, implement secure deletion / purge and ensure indexing/export pipelines honor it.


Analyzed PR: #57446 at commit c5ab031

Last updated on: 2026-03-30T09:56:03Z

Alix-007 pushed a commit to Alix-007/openclaw that referenced this pull request Mar 30, 2026
* fix(memory): export archived qmd session transcripts

* test(memory): separate qmd session listing describe
alexjiang1 pushed a commit to alexjiang1/openclaw that referenced this pull request Mar 31, 2026
* fix(memory): export archived qmd session transcripts

* test(memory): separate qmd session listing describe
pgondhi987 pushed a commit to pgondhi987/openclaw that referenced this pull request Mar 31, 2026
* fix(memory): export archived qmd session transcripts

* test(memory): separate qmd session listing describe
pgondhi987 pushed a commit to pgondhi987/openclaw that referenced this pull request Mar 31, 2026
* fix(memory): export archived qmd session transcripts

* test(memory): separate qmd session listing describe
builderjarvis added a commit to saucesteals/openclaw that referenced this pull request Apr 1, 2026
lovewanwan pushed a commit to lovewanwan/openclaw that referenced this pull request Apr 28, 2026
* fix(memory): export archived qmd session transcripts

* test(memory): separate qmd session listing describe
Tardisyuan pushed a commit to Tardisyuan/openclaw that referenced this pull request Apr 30, 2026
* fix(memory): export archived qmd session transcripts

* test(memory): separate qmd session listing describe
ogt-redknie pushed a commit to ogt-redknie/OPENX that referenced this pull request May 2, 2026
* fix(memory): export archived qmd session transcripts

* test(memory): separate qmd session listing describe
github-actions Bot pushed a commit to Desicool/openclaw that referenced this pull request May 9, 2026
* fix(memory): export archived qmd session transcripts

* test(memory): separate qmd session listing describe
github-actions Bot pushed a commit to Desicool/openclaw that referenced this pull request May 24, 2026
* fix(memory): export archived qmd session transcripts

* test(memory): separate qmd session listing describe
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

maintainer Maintainer-authored PR size: S

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: QMD session exporter ignores reset/deleted session transcripts (.jsonl.reset.*, .jsonl.deleted.*)

1 participant