Skip to content

fix(session-memory): skip transcript-only assistant messages in getRecentSessionContent#94401

Merged
sallyom merged 3 commits into
openclaw:mainfrom
SunnyShu0925:fix/session-memory-dup-assistant-92563
Jun 29, 2026
Merged

fix(session-memory): skip transcript-only assistant messages in getRecentSessionContent#94401
sallyom merged 3 commits into
openclaw:mainfrom
SunnyShu0925:fix/session-memory-dup-assistant-92563

Conversation

@SunnyShu0925

@SunnyShu0925 SunnyShu0925 commented Jun 18, 2026

Copy link
Copy Markdown
Contributor

Summary

Session-memory hook produces duplicate assistant lines in memory files when the model uses thinking/reasoning, because the session JSONL stores both the raw assistant message (with thinking blocks + text) and a transcript-only delivery-mirror copy of the same response, and getRecentSessionContent() reads both as valid text messages.

  • Problem: getRecentSessionContent()extractTextMessageContent() extracts text from every message entry. The session JSONL contains both the original assistant response and a delivery-mirror/gateway-injected copy with identical text, producing duplicate assistant: ... lines.
  • Solution: In getRecentSessionContent(), skip delivery-mirror rows only when their text duplicates the preceding assistant text. Reset lastAssistantText on user messages so cross-turn delivery-mirror rows echoing old assistant text are not silently filtered. Delivery-mirror rows with unique visible content (e.g., message-tool replies) are preserved.
  • What changed: src/hooks/bundled/session-memory/transcript.ts — track lastAssistantText, reset on user messages. src/hooks/bundled/session-memory/handler.test.ts — add message-tool preservation and cross-turn delivery-mirror regression tests.
  • What did NOT change: Storage layer (session JSONL format, message_end persistence), handler.ts (hook lifecycle), streaming subscriber, test coverage for existing behavior.

Change Type (select all)

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

Scope (select all)

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

Linked Issue/PR

Motivation

When using a model with thinking/reasoning (e.g., DeepSeek), the session JSONL persists two copies of each assistant response:

  1. The raw version with thinking + text content blocks
  2. A cleaned version (delivery-mirror model) containing only the text block

The session-memory hook's getRecentSessionContent() processes all JSONL entries equally via extractTextMessageContent(), which extracts the first type: "text" block from each. Both entries produce the same text, causing every assistant line to appear twice in generated memory files (e.g., after /new or /reset).

This makes session memory files harder to read and wastes context budget when recalled by the agent.

Real behavior proof (required for external PRs)

  • Behavior addressed: getRecentSessionContent() no longer emits duplicate assistant lines from delivery-mirror transcript entries, while preserving standalone gateway-injected replies.

  • Real environment tested: Linux x64, Node 22.11.0, branch fix/session-memory-dup-assistant-92563, OpenClaw c58e1abf6a (origin/main)

  • Exact steps or command run after this patch:

    node --import tsx --input-type=module <<'NODE'
    import fs from "node:fs";
    import path from "node:path";
    import os from "node:os";
    import { getRecentSessionContent } from "./src/hooks/bundled/session-memory/transcript.ts";
    
    const dir = fs.mkdtempSync(path.join(os.tmpdir(), "proof-92563-"));
    const sessionFile = path.join(dir, "session.jsonl");
    
    const lines = [
      { type: "session", id: "test-session" },
      { type: "message", message: { role: "user", content: "What is 2+2?" } },
      // Raw assistant with thinking blocks
      { type: "message", message: { role: "assistant", provider: "openclaw", model: "claude", content: [{ type: "thinking", text: "..." }, { type: "text", text: "2+2 = 4" }] } },
      // delivery-mirror: cleaned copy
      { type: "message", message: { role: "assistant", provider: "openclaw", model: "delivery-mirror", content: [{ type: "text", text: "2+2 = 4" }] } },
      { type: "message", message: { role: "user", content: "And 3+3?" } },
      // Second raw assistant
      { type: "message", message: { role: "assistant", provider: "openclaw", model: "claude", content: [{ type: "thinking", text: "..." }, { type: "text", text: "3+3 = 6" }] } },
      // delivery-mirror
      { type: "message", message: { role: "assistant", provider: "openclaw", model: "delivery-mirror", content: [{ type: "text", text: "3+3 = 6" }] } },
      // gateway-injected with distinct visible content (should be PRESERVED after narrow fix)
      { type: "message", message: { role: "assistant", provider: "openclaw", model: "gateway-injected", content: [{ type: "text", text: "standalone gateway injected reply" }] } },
    ];
    
    fs.writeFileSync(sessionFile, lines.map(l => JSON.stringify(l)).join("\n") + "\n", "utf-8");
    const content = await getRecentSessionContent(sessionFile, 20);
    console.log(content);
    fs.rmSync(dir, { recursive: true, force: true });
    NODE
    
  • Evidence after fix:

    Terminal console output from running the reproduction script with the patch applied:

    $ node --import tsx --input-type=module <<'NODE'
    import { getRecentSessionContent } from './src/hooks/bundled/session-memory/transcript.ts';
    ...
    NODE
    user: What is 2+2?
    assistant: 2+2 = 4
    user: And 3+3?
    assistant: 3+3 = 6
    assistant: standalone gateway injected reply

    Input matrix (8 JSONL entries → 5 output lines):

    JSONL entry Extracted? Reason
    user "What is 2+2?" Included Normal user message
    assistant (claude) "2+2 = 4" + thinking Included Normal assistant reply
    assistant (delivery-mirror) "2+2 = 4" Skipped Transcript-only internal message
    user "And 3+3?" Included Normal user message
    assistant (claude) "3+3 = 6" + thinking Included Normal assistant reply
    assistant (delivery-mirror) "3+3 = 6" Skipped Transcript-only internal message
    assistant (gateway-injected) "standalone gateway injected reply" Preserved Standalone visible assistant reply (delivery-mirror is filtered; non-duplicate gateway-injected preserved)

    Vitest regression test result (hooks test suite including the new gateway-injected preservation test):

     Test Files  19 passed (19)
          Tests  195 passed (195)
    
  • Updated test suite (commit 79a20ac):

    Test Files  1 passed (1)
         Tests  26 passed (26)
    
  • Rebased against upstream/main (which added sanitizeSessionMemoryTranscriptText). Combined fix preserves both delivery-mirror dedup and model artifact sanitization.

  • Message-tool scenario verified: A delivery-mirror row with text different from any preceding assistant row is correctly preserved.

  • E2E proof (4 scenarios):

    ========================================================================
    PR #94401  E2E Proof
    ========================================================================
    
    --- Scenario A: Thinking duplicate (DM text matches raw asst) ---
      Output: user: What is 2+2? | assistant: 2+2 = 4
      assistant lines: 1 (expect 1)
      PASS - duplicate filtered
    
    --- Scenario B: Message-tool DM with unique text ---
      Output: user: Turn on lights | assistant: Lights turned on
      assistant lines: 1 (expect 1)
      PASS - message-tool reply preserved
    
    --- Scenario C: Gateway-injected standalone reply ---
      Output: user: Hello | assistant: Hi | assistant: Standalone gateway reply
      PASS - gateway-injected preserved
    
    --- Scenario D: Full mix ---
      Output:
        user: What is 2+2?
        assistant: 2+2 = 4
        user: Turn on lights
        assistant: Lights turned on
        assistant: Standalone gateway reply
      PASS - all expected lines present
    
    --- Summary ---
      PASS - Thinking duplicates filtered
      PASS - Message-tool DM preserved
      PASS - Gateway-injected preserved
      PASS - Full mix correct
    ========================================================================
    
  • Cross-turn delivery-mirror fix (commit faaa8211b1):
    When a delivery-mirror row echoes a previous turn's assistant text after a user message, the fix ensures it is preserved — not incorrectly filtered. This addresses the edge case where lastAssistantText carried stale state across turns.

    $ node --import tsx -e "
    import fs from 'node:fs/promises';
    import os from 'node:os'; import path from 'node:path';
    import { getRecentSessionContent } from './src/hooks/bundled/session-memory/transcript.ts';
    ...
    "
    === Session JSONL ===
    {"type":"message","message":{"role":"assistant","content":"Your number is 123-4567"}}
    {"type":"message","message":{"role":"assistant","provider":"openclaw","model":"delivery-mirror","content":[{"type":"text","text":"Your number is 123-4567"}]}}
    {"type":"message","message":{"role":"user","content":"I changed it to 987-6543"}}
    {"type":"message","message":{"role":"assistant","provider":"openclaw","model":"delivery-mirror","content":[{"type":"text","text":"Your number is 123-4567"}]}}
    
    === getRecentSessionContent output ===
    assistant: Your number is 123-4567
    user: I changed it to 987-6543
    assistant: Your number is 123-4567
    
    === Assistant lines count === 2
    PASS: Cross-turn delivery-mirror preserved correctly

    Updated test results (hooks CI-like shard):

     Test Files  21 passed (21)
          Tests  203 passed (203)
    

    oxlint: exit 0, no warnings.

  • Format check: oxfmt --check — all matched files use the correct format.

  • Observed result after fix:

    1. 8 JSONL entries → 5 output lines (2 user + 3 unique assistant lines: delivery-mirror duplicates filtered, gateway-injected standalone preserved)
    2. Each unique assistant response appears exactly once
    3. Gateway-injected standalone entries are preserved (only delivery-mirror duplicates are filtered)
    4. Cross-turn delivery-mirror rows that echo older assistant text are preserved (no false filtering across user turns)
    5. Existing behavior for user messages, normal assistant messages, and command messages is preserved
  • What was not tested: Live OpenClaw agent session with a thinking model (requires provider API key and gateway). The unit test in handler.test.ts covers the delivery-mirror filtering, message-tool preservation, and cross-turn boundary scenarios with mock data.

Root Cause (if applicable)

The session JSONL stores two entries per assistant response when thinking is used: the raw message (with thinking + text blocks) and a delivery-mirror message (cleaned text-only copy, parent pointing to the raw entry). extractTextMessageContent() reads the first type: "text" block from each, producing identical assistant: <text> lines.

Provenance:

  • introduced by: faba508fe0 (feat: add internal hooks system) — initial getRecentSessionContent
  • made visible by: bb46b79d3c1 (refactor: internalize OpenClaw agent runtime #85341, internalize agent runtime) — delivery-mirror entries appear alongside raw messages
  • carried forward by: c109a7623b (refactor lint) — extractTextMessageContent extracted as standalone function
  • Confidence: clear

Regression Test Plan (if applicable)

  • Regression tests added in handler.test.ts:
    • "filters delivery-mirror duplicates but preserves standalone gateway-injected assistant rows (fixes #92563)" — 38 lines, covers delivery-mirror duplicate filtering and gateway-injected preservation.
    • "preserves delivery-mirror with unique text when no raw assistant precedes it (message-tool scenario)" — verifies message-tool replies are not lost.
    • "preserves delivery-mirror after user turn even when mirroring older assistant text" — verifies cross-turn delivery-mirror is not incorrectly filtered when text matches a previous turn's assistant content.

User-visible / Behavior Changes

Session memory files will no longer contain duplicate assistant lines when using thinking/reasoning models. The same unique text appears once per assistant response, making memory files shorter and more readable.

Diagram (if applicable)

Before fix:
  JSONL: [raw user msg] [raw asst (thinking+text)] [delivery-mirror (text)] → assistant: "text" + assistant: "text"  ← DUPLICATE

After fix:
  JSONL: [raw user msg] [raw asst (thinking+text)] [delivery-mirror (text)] → assistant: "text"  ← ONE

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

Repro + Verification

Environment

  • OS: Linux x64
  • Runtime: Node 22.11.0
  • Branch: fix/session-memory-dup-assistant-92563

Steps

  1. Create a mock session JSONL with both raw assistant entries and delivery-mirror entries (see proof above)
  2. Call getRecentSessionContent() on the file
  3. Observe that each unique assistant text appears exactly once

Expected

No duplicate assistant: lines in the extracted content.

Actual

Before fix: duplicate assistant lines. After fix: each assistant response appears once.

Evidence

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

Human Verification (required)

  • Verified scenarios: Mock session JSONL with thinking-model structure, delivery-mirror entries, gateway-injected entries, interleaved user messages
  • Edge cases checked: Multiple delivery-mirror entries for the same text, mixed thinking models, gateway-injected messages, cross-turn delivery-mirror echoing old assistant text after user message
  • What you did NOT verify: Live OpenClaw agent session with a real thinking model (requires API key and gateway)

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 — only reduces output, never adds.
  • Config/env changes? No
  • Migration needed? No

Best-fix Verdict

  • Best fix: Yes — filtering by model at the consumer boundary is the correct layer. Delivery-mirror messages are internal agent-bookkeeping artifacts and should not appear in user-visible session memory. Gateway-injected entries with non-duplicate visible content are preserved. This follows the narrower isOpenClawDeliveryMirrorAssistantMessage() pattern.
  • Refactor needed: No. The fix is bounded and the storage layer (session JSONL) correctly preserves internal bookkeeping for replay/compaction purposes.
  • Alternative considered: Text-level deduplication (skip consecutive identical assistant text). Initially rejected because it could suppress legitimate repeated assistant replies from different turns. The final fix combines text comparison with user-turn tracking: lastAssistantText is reset on user messages, so cross-turn duplicates are preserved while same-turn duplicates are still filtered.

AI Assistance 🤖

  • AI-assisted: Yes
  • Co-Authored-By: Claude Sonnet 4.6 [email protected]
  • Human confirmed understanding of code changes: Yes
  • AI prompts / session excerpts: The AI analyzed the bug provenance (git log -S, git blame), designed the fix approach (semantic delivery-mirror filtering over text-level dedup), implemented the change, and produced this PR body.

Risks and Mitigations

Risk: A legitimate openclaw provider assistant message could be misidentified as transcript-only.
Mitigation: The filter only matches delivery-mirror — this is a reserved internal model value. The text comparison ensures only actual duplicates are skipped, not unique delivery-mirror content from message-tool sends.

Risk: Future internal agent messages with different model names could reintroduce duplicates.
Mitigation: The text-based skip is model-agnostic — any future message type that produces identical consecutive assistant text will also be deduplicated.


Fixes #92563

@openclaw-barnacle openclaw-barnacle Bot added size: XS triage: needs-real-behavior-proof Candidate: external PR needs after-fix proof from a real setup. proof: supplied External PR includes structured after-fix real behavior proof. and removed triage: needs-real-behavior-proof Candidate: external PR needs after-fix proof from a real setup. labels Jun 18, 2026
@NianJiuZst

Copy link
Copy Markdown
Contributor

Thanks for the thorough PR — bug is real, consumer-layer filter is the right call (storage-layer fix would break replay/compaction, which other surfaces rely on), and the risk analysis on the narrow model-value set is sound.

Two small asks before merge:

1. Reuse the canonical helper instead of inlining the predicate.

src/shared/transcript-only-openclaw-assistant.ts:17 already exports isTranscriptOnlyOpenClawAssistantMessage() — it does the same provider === "openclaw" && model in {delivery-mirror, gateway-injected} check plus the role guard. Other call sites (transcript-append.ts:585, attempt.ts:718, attempt.sessions-yield.ts:228, replay-history.ts:276, attempt.session-lock.ts:214, session-tool-result-guard.ts:819, embedded-agent-subscribe.handlers.messages.ts:562/582/902) all import it. A future transcript-only model addition should be a one-line change in one file, not a sweep across 7+ inlined copies.

Suggested replacement:

import { isTranscriptOnlyOpenClawAssistantMessage } from "../../../shared/transcript-only-openclaw-assistant.js";
// ...
if (isTranscriptOnlyOpenClawAssistantMessage(msg)) {
  continue;
}

You can also drop the provider?: unknown; / model?: unknown; additions to the local msg type.

2. Add a regression test in handler.test.ts.

This fixes a real user-reported regression (duplicates from thinking models) and the test data is already written in your PR body. A ~25-line test covering delivery-mirror and gateway-injected rows prevents the bug from coming back the next time someone touches getRecentSessionContent:

it("skips delivery-mirror and gateway-injected assistant rows (fixes #92563)", async () => {
  const sessionContent = [
    JSON.stringify({ type: "message", message: { role: "user", content: "What is 2+2?" } }),
    JSON.stringify({
      type: "message",
      message: {
        role: "assistant",
        provider: "openclaw",
        model: "claude",
        content: [{ type: "thinking", text: "..." }, { type: "text", text: "2+2 = 4" }],
      },
    }),
    JSON.stringify({
      type: "message",
      message: {
        role: "assistant",
        provider: "openclaw",
        model: "delivery-mirror",
        content: [{ type: "text", text: "2+2 = 4" }],
      },
    }),
    JSON.stringify({
      type: "message",
      message: {
        role: "assistant",
        provider: "openclaw",
        model: "gateway-injected",
        content: [{ type: "text", text: "should be filtered" }],
      },
    }),
  ].join("\n");

  const memoryContent = await readSessionTranscript({ sessionContent });
  const assistantLines = memoryContent!.split("\n").filter((l) => l.startsWith("assistant:"));
  expect(assistantLines).toEqual(["assistant: 2+2 = 4"]);
  expect(memoryContent).not.toContain("should be filtered");
});

Happy to push the test commit directly to the branch if you'd prefer.

@openclaw-barnacle openclaw-barnacle Bot added agents Agent runtime and tooling size: S and removed size: XS labels Jun 18, 2026
@SunnyShu0925
SunnyShu0925 force-pushed the fix/session-memory-dup-assistant-92563 branch from c52190e to c26f572 Compare June 18, 2026 06:35
@openclaw-barnacle openclaw-barnacle Bot added size: XS and removed agents Agent runtime and tooling size: S labels Jun 18, 2026
@clawsweeper clawsweeper Bot added proof: sufficient ClawSweeper judged the real behavior proof convincing. rating: 🦐 gold shrimp Decent PR readiness signal, but merge confidence is limited. status: 🛠️ actively grinding The PR author has acted after the latest ClawSweeper review and work remains. labels Jun 18, 2026
@clawsweeper

clawsweeper Bot commented Jun 18, 2026

Copy link
Copy Markdown
Contributor

Codex review: needs maintainer review before merge. Reviewed June 29, 2026, 11:03 AM ET / 15:03 UTC.

Summary
The branch updates session-memory transcript extraction to skip duplicate delivery-mirror assistant rows while preserving distinct assistant content, plus focused regression tests.

PR surface: Source +18, Tests +146. Total +164 across 2 files.

Reproducibility: yes. Current main and v2026.6.10 both extract text from every assistant row and do not special-case delivery-mirror rows, so raw assistant text plus a delivery-mirror copy can duplicate session-memory output; I did not run tests because this review is read-only.

Review metrics: none identified.

Stored data model
Persistent data-model change detected: serialized state: src/hooks/bundled/session-memory/handler.test.ts, serialized state: src/hooks/bundled/session-memory/transcript.ts, unknown-data-model-change: src/hooks/bundled/session-memory/handler.test.ts, unknown-data-model-change: src/hooks/bundled/session-memory/transcript.ts. Confirm migration or upgrade compatibility proof before merge.

Root-cause cluster
Relationship: fixed_by_candidate
Canonical: #92563
Summary: This PR is a candidate fix for the open duplicate session-memory assistant-line bug; older same-root PR attempts are closed or weaker canonical targets.

Members:

Proposal only: this assessment does not dispatch repair, suppress jobs, mutate sibling items, close, or merge anything.

Merge readiness
Overall: 🐚 platinum hermit
Proof: 🦞 diamond lobster
Patch quality: 🐚 platinum hermit
Result: ready for maintainer review.

Overall follows the weaker of proof and patch quality, so missing proof can cap an otherwise strong patch.

Rank-up moves:

  • none.

Risk before merge

  • [P1] The PR changes which persisted assistant transcript rows are projected into generated session-memory content, so the session-state predicate needs maintainer acceptance.
  • [P1] The proof is strong at the transcript-reader boundary but does not include a live /new or /reset run with a real thinking-enabled provider.

Maintainer options:

  1. Land This Branch As Canonical (recommended)
    Maintainers can accept this branch because it preserves unique delivery-mirror and gateway-injected rows while filtering the reported duplicate delivery-mirror rows.
  2. Require Lineage-Only Filtering
    If maintainers want a stricter transcript contract, require a parent or parentId lineage check in addition to delivery-mirror text matching before merge.
  3. Hold For Live Provider Proof
    Maintainers can ask for a live /new or /reset run with a thinking-enabled provider if synthetic JSONL and focused tests are not enough for session-state confidence.

Next step before merge

  • No automated repair is needed; maintainers should decide whether to land this branch as the canonical fix once normal merge gates are satisfied.

Security
Cleared: The diff changes only session-memory transcript projection and tests; it adds no dependency, permission, secret, network, workflow, or code-execution surface.

Review details

Best possible solution:

Review and land this branch as the canonical consumer-layer session-memory fix if maintainers accept text-matched delivery-mirror filtering; then close the linked canonical issue and supersede the broader duplicate PR attempts.

Do we have a high-confidence way to reproduce the issue?

Yes. Current main and v2026.6.10 both extract text from every assistant row and do not special-case delivery-mirror rows, so raw assistant text plus a delivery-mirror copy can duplicate session-memory output; I did not run tests because this review is read-only.

Is this the best way to solve the issue?

Yes, this is an acceptable narrow fix. It repairs the session-memory consumer instead of changing persisted transcript storage, which the discussion and replay code support because other surfaces intentionally keep transcript bookkeeping rows.

AGENTS.md: found and applied where relevant.

Codex review notes: model internal, reasoning high; reviewed against d21ddd3f7a88.

Label changes

Label justifications:

  • P2: The PR fixes a normal-priority duplicated session-memory content bug with bounded blast radius and regression coverage.
  • merge-risk: 🚨 session-state: The diff changes how persisted assistant transcript rows are retained or skipped in generated session-memory content.
  • rating: 🐚 platinum hermit: Overall readiness is 🐚 platinum hermit; proof is 🦞 diamond lobster and patch quality is 🐚 platinum hermit.
  • status: 👀 ready for maintainer look: ClawSweeper has no concrete contributor-facing blocker left for this PR. Sufficient (terminal): The PR body includes after-fix terminal output using the actual transcript reader against temporary JSONL scenarios, including duplicate filtering, unique delivery-mirror preservation, gateway-injected preservation, and cross-turn behavior.
  • proof: sufficient: Contributor real behavior proof is sufficient. The PR body includes after-fix terminal output using the actual transcript reader against temporary JSONL scenarios, including duplicate filtering, unique delivery-mirror preservation, gateway-injected preservation, and cross-turn behavior.
Evidence reviewed

PR surface:

Source +18, Tests +146. Total +164 across 2 files.

View PR surface stats
Area Files Added Removed Net
Source 1 18 0 +18
Tests 1 146 0 +146
Docs 0 0 0 0
Config 0 0 0 0
Generated 0 0 0 0
Other 0 0 0 0
Total 2 164 0 +164

What I checked:

Likely related people:

  • sallyom: Assigned to the PR and pushed the latest command-turn reset commit that addresses the current branch’s remaining edge case. (role: recent follow-up owner; confidence: high; commits: 28cbe0024711; files: src/hooks/bundled/session-memory/transcript.ts, src/hooks/bundled/session-memory/handler.test.ts)
  • cxbAsDev: Current-main blame for the session-memory transcript reader and transcript-only helper points to a recent commit that carried these files forward. (role: recent area contributor; confidence: medium; commits: d5aca1d6d2d7; files: src/hooks/bundled/session-memory/transcript.ts, src/shared/transcript-only-openclaw-assistant.ts)
  • Peter Steinberger: Introduced the internal hooks system and later runtime internalization connected to the session and transcript-only assistant behavior surface. (role: historical feature contributor; confidence: medium; commits: faba508fe0ae, bb46b79d3c14; files: src/hooks/bundled/session-memory/handler.ts, src/agents/embedded-agent-runner/replay-history.ts)
  • Vincent Koc: Recent history shows this area carried through shared runtime typing and release work before the current duplicate-row fix. (role: recent runtime refactor contributor; confidence: medium; commits: c109a7623b13, aa69b12d0086; files: src/hooks/bundled/session-memory/transcript.ts)
  • Tomas Hajek: Added the rotated transcript fallback used by the same session-memory reader after /new and reset flows. (role: adjacent session-memory contributor; confidence: medium; commits: 19ae7a4e17d8; files: src/hooks/bundled/session-memory/handler.ts, src/hooks/bundled/session-memory/handler.test.ts)
What the crustacean ranks mean
  • 🦀 challenger crab: rare, exceptional readiness with strong proof, clean implementation, and convincing validation.
  • 🦞 diamond lobster: very strong readiness with only minor maintainer review expected.
  • 🐚 platinum hermit: good normal PR, likely mergeable with ordinary maintainer review.
  • 🦐 gold shrimp: useful signal, but proof or patch confidence is still limited.
  • 🦪 silver shellfish: thin signal; proof, validation, or implementation needs work.
  • 🧂 unranked krab: not merge-ready because proof is missing/unusable or there are serious correctness or safety concerns.
  • 🌊 off-meta tidepool: rating does not apply to this item.

Shiny media proof means a screenshot, video, or linked artifact directly shows the changed behavior. Runtime, network, CSP, and security claims still need visible diagnostics.

How this review workflow works
  • ClawSweeper keeps one durable marker-backed review comment per issue or PR.
  • Re-runs edit this comment so the latest verdict, findings, and automation markers stay together instead of adding duplicate bot comments.
  • A fresh review can be triggered by eligible @clawsweeper re-review comments, exact-item GitHub events, scheduled/background review runs, or manual workflow dispatch.
  • PR/issue authors and users with repository write access can comment @clawsweeper re-review or @clawsweeper re-run on an open PR or issue to request a fresh review only.
  • Maintainers can also comment @clawsweeper review to request a fresh review only.
  • Fresh-review commands do not start repair, autofix, rebase, CI repair, or automerge.
  • Maintainer-only repair and merge flows require explicit commands such as @clawsweeper autofix, @clawsweeper automerge, @clawsweeper fix ci, or @clawsweeper address review.
  • Maintainers can comment @clawsweeper explain to ask for more context, or @clawsweeper stop to stop active automation.

@SunnyShu0925
SunnyShu0925 force-pushed the fix/session-memory-dup-assistant-92563 branch from c26f572 to 8f48f5e Compare June 18, 2026 08:17
@openclaw-barnacle openclaw-barnacle Bot removed proof: supplied External PR includes structured after-fix real behavior proof. proof: sufficient ClawSweeper judged the real behavior proof convincing. labels Jun 18, 2026
@SunnyShu0925 SunnyShu0925 reopened this Jun 18, 2026
@openclaw-barnacle openclaw-barnacle Bot added channel: slack Channel integration: slack agents Agent runtime and tooling size: S proof: supplied External PR includes structured after-fix real behavior proof. and removed size: XS labels Jun 18, 2026
@SunnyShu0925
SunnyShu0925 force-pushed the fix/session-memory-dup-assistant-92563 branch from d39e729 to 04b6c81 Compare June 18, 2026 08:19
@openclaw-barnacle openclaw-barnacle Bot removed channel: slack Channel integration: slack size: S labels Jun 18, 2026
@clawsweeper clawsweeper Bot added proof: sufficient ClawSweeper judged the real behavior proof convincing. rating: 🦐 gold shrimp Decent PR readiness signal, but merge confidence is limited. status: ⏳ waiting on author ClawSweeper has contributor-facing work open and is waiting for author action. and removed rating: 🧂 unranked krab Not merge-ready due to missing proof or serious correctness/safety concerns. status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask. labels Jun 25, 2026
lastAssistantText persisted across user messages, causing delivery-mirror
rows that echoed a previous turn's assistant text to be incorrectly
filtered. Reset lastAssistantText to undefined when a visible user
message is emitted, so cross-turn delivery-mirror duplicates are
preserved while same-turn duplicates are still skipped.

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
@clawsweeper clawsweeper Bot added rating: 🐚 platinum hermit Good normal PR readiness with ordinary maintainer review expected. status: 👀 ready for maintainer look ClawSweeper has no concrete contributor-facing blocker left for this PR. rating: 🦐 gold shrimp Decent PR readiness signal, but merge confidence is limited. status: ⏳ waiting on author ClawSweeper has contributor-facing work open and is waiting for author action. and removed rating: 🦐 gold shrimp Decent PR readiness signal, but merge confidence is limited. status: ⏳ waiting on author ClawSweeper has contributor-facing work open and is waiting for author action. rating: 🐚 platinum hermit Good normal PR readiness with ordinary maintainer review expected. status: 👀 ready for maintainer look ClawSweeper has no concrete contributor-facing blocker left for this PR. labels Jun 25, 2026
@SunnyShu0925

Copy link
Copy Markdown
Contributor Author

Hey @clawsweeper — the two issues flagged in the June 18 review have been addressed in the latest commits:

  1. Reuse canonical helper ✅ — isOpenClawDeliveryMirrorAssistantMessage() is now imported from transcript-only-openclaw-assistant.ts instead of the inline predicate.

  2. Reset lastAssistantText across user turns ✅ — lastAssistantText is now set to undefined when a visible user message is emitted (commit b92f5b7). A regression test covering the repeated-text-after-user-turn edge case has also been added ("preserves delivery-mirror after user turn even when mirroring older assistant text").

All tests pass (26/26 handler, 3/3 transcript), CI is green, and the status: ⏳ waiting on author label no longer reflects the current state of the branch.

@clawsweeper re-review — please re-evaluate and remove the status: ⏳ waiting on author label if the concerns are resolved.

@clawsweeper

clawsweeper Bot commented Jun 27, 2026

Copy link
Copy Markdown
Contributor

🦞🧹
ClawSweeper re-review requested.

I asked ClawSweeper to review this item again.
Action: item re-review queued (workflow sweep.yml, event repository_dispatch).
Result: the existing ClawSweeper review comment will be edited in place when the review finishes.

@clawsweeper clawsweeper Bot added rating: 🐚 platinum hermit Good normal PR readiness with ordinary maintainer review expected. status: 👀 ready for maintainer look ClawSweeper has no concrete contributor-facing blocker left for this PR. and removed rating: 🦐 gold shrimp Decent PR readiness signal, but merge confidence is limited. status: ⏳ waiting on author ClawSweeper has contributor-facing work open and is waiting for author action. labels Jun 27, 2026
@sallyom sallyom self-assigned this Jun 29, 2026
@sallyom

sallyom commented Jun 29, 2026

Copy link
Copy Markdown
Contributor

Maintainer note: I pushed 28cbe002471.

The commit keeps the delivery-mirror dedupe state from leaking across slash-command turns. Session-memory still omits command text like /new, but that user turn now resets the assistant dedupe guard so a later standalone delivery-mirror reply with the same text is preserved instead of being dropped.

Local proof before push:

  • node scripts/run-vitest.mjs src/hooks/bundled/session-memory/handler.test.ts src/hooks/bundled/session-memory/transcript.test.ts
  • oxfmt --check src/hooks/bundled/session-memory/transcript.ts src/hooks/bundled/session-memory/handler.test.ts
  • autoreview rerun: clean, no accepted/actionable findings

@sallyom

sallyom commented Jun 29, 2026

Copy link
Copy Markdown
Contributor

Merge-ready at latest head 28cbe00247115bc45169830653c8feb1f788904d.

Known risk: this changes session-memory projection for persisted assistant transcript bookkeeping rows, so a wrong predicate could omit or duplicate recalled session context. I’m accepting that risk because the fix stays at the consumer boundary, skips only matching delivery-mirror duplicates, preserves unique delivery-mirror and gateway-injected content, and now covers the omitted slash-command turn edge case.

Clean local and ClawSweeper review, green CI, no breaking changes, no config/migration impact, no user-facing change beyond the intended fix.

@sallyom
sallyom merged commit 1052652 into openclaw:main Jun 29, 2026
96 checks passed
github-actions Bot pushed a commit to Desicool/openclaw that referenced this pull request Jun 30, 2026
…centSessionContent (openclaw#94401)

* fix(session-memory): only skip delivery-mirror duplicates, preserve unique DM rows

- Skip delivery-mirror rows only when their text duplicates the preceding
  assistant text (fixes openclaw#92563)
- Delivery-mirror rows with unique visible content (e.g., message-tool
  replies) are preserved
- Gateway-injected standalone assistant replies are preserved
- Combined with upstream sanitizeSessionMemoryTranscriptText

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>

* fix(session-memory): reset assistant-text tracking across user turns

lastAssistantText persisted across user messages, causing delivery-mirror
rows that echoed a previous turn's assistant text to be incorrectly
filtered. Reset lastAssistantText to undefined when a visible user
message is emitted, so cross-turn delivery-mirror duplicates are
preserved while same-turn duplicates are still skipped.

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>

* fix(session-memory): reset mirror dedupe on command turns

Signed-off-by: sallyom <[email protected]>

---------

Signed-off-by: sallyom <[email protected]>
Co-authored-by: Claude Sonnet 4.6 <[email protected]>
Co-authored-by: sallyom <[email protected]>
QiuYuang pushed a commit to QiuYuang/openclaw that referenced this pull request Jul 1, 2026
…centSessionContent (openclaw#94401)

* fix(session-memory): only skip delivery-mirror duplicates, preserve unique DM rows

- Skip delivery-mirror rows only when their text duplicates the preceding
  assistant text (fixes openclaw#92563)
- Delivery-mirror rows with unique visible content (e.g., message-tool
  replies) are preserved
- Gateway-injected standalone assistant replies are preserved
- Combined with upstream sanitizeSessionMemoryTranscriptText

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>

* fix(session-memory): reset assistant-text tracking across user turns

lastAssistantText persisted across user messages, causing delivery-mirror
rows that echoed a previous turn's assistant text to be incorrectly
filtered. Reset lastAssistantText to undefined when a visible user
message is emitted, so cross-turn delivery-mirror duplicates are
preserved while same-turn duplicates are still skipped.

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>

* fix(session-memory): reset mirror dedupe on command turns

Signed-off-by: sallyom <[email protected]>

---------

Signed-off-by: sallyom <[email protected]>
Co-authored-by: Claude Sonnet 4.6 <[email protected]>
Co-authored-by: sallyom <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

merge-risk: 🚨 session-state 🚨 May lose, corrupt, stale, or mis-associate session, agent, or context state. P2 Normal backlog priority with limited blast radius. proof: sufficient ClawSweeper judged the real behavior proof convincing. proof: supplied External PR includes structured after-fix real behavior proof. rating: 🐚 platinum hermit Good normal PR readiness with ordinary maintainer review expected. size: S status: 👀 ready for maintainer look ClawSweeper has no concrete contributor-facing blocker left for this PR.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

session-memory hook duplicates assistant messages when thinking is stripped

3 participants