mirror of
https://github.com/openclaw/openclaw.git
synced 2026-07-21 10:16:44 +00:00
* fix(dreaming): drop heartbeat assistant responses from dream corpus The dreaming ingestion pipeline filters heartbeat user messages (containing [OpenClaw heartbeat poll]) via sanitizeSessionText, but the paired assistant response is only dropped when it is the exact string HEARTBEAT_OK. Local models frequently respond with natural-language acknowledgments (e.g. "Heartbeat received. Main is active.") which pass through the sanitizer unchanged and enter the dream corpus as low-confidence (0.58) memory snippets. Fix: track heartbeat user message drops in buildSessionEntry and skip the immediately following assistant response. This cross-message coupling is safe because the heartbeat prompt pattern is injected by the runtime and cannot be spoofed by user input, unlike [cron:...] or System (untrusted): ... patterns (see PR #70737). Refs: #103720 * test(dreaming): add heartbeat assistant response filter test * fix(dreaming): use provenance-based heartbeat detection instead of text matching Replace text-based heartbeat detection (isGeneratedHeartbeatPromptMessage) with provenance-based authentication. The heartbeat turn now carries provenance: { kind: "heartbeat" } when persisted to the transcript, and buildSessionEntry checks for this provenance instead of matching user-spoofable text content. Changes: - src/sessions/input-provenance.ts: Add "heartbeat" to INPUT_PROVENANCE_KIND_VALUES - src/auto-reply/reply/get-reply-run.ts: Attach heartbeat provenance to user turn input when isHeartbeat is true - packages/memory-host-sdk/src/host/session-files.ts: Check message.provenance.kind === "heartbeat" instead of text matching - packages/memory-host-sdk/src/host/session-files.test.ts: Update heartbeat test to include provenance; add lookalike regression test This addresses the ClawSweeper review finding [P1] about user-spoofable text matching. The cross-message coupling is now authenticated by runtime provenance, not user-typed content. Refs: #103720 * fix(dreaming): add targeted heartbeat-derived corpus repair * fix(dreaming): fix TS return type for clearScopedLegacySessionIngestionJson catch * fix(dreaming): match chunked SQLite seen-state keys by stored scope * fix(dreaming): repair SQLite-backed session transcript lookup in doctor The findHeartbeatContaminatedCorpusLines function could not read SQLite-backed session transcripts. When a corpus ref had no .jsonl extension (SQLite logical path format), it appended .jsonl, read an empty file, and silently skipped — leaving pre-fix heartbeat contamination intact for SQLite users. Fix: - Add loadTranscriptLinesFromSqlite helper that reads events via the canonical loadTranscriptEventsSync API - Serialize all events to preserve original event positions for corpus #L<n> line-number references - Fall back to SQLite reader when filesystem read fails on a non-.jsonl corpus ref path Test: add SQLite-backed session regression test that seeds a session, writes corpus refs without .jsonl extension, and verifies both audit detection and targeted repair. * fix(dreaming): address autoreview P1 and P2 for checkpoint clearing and early return P1: Remove clearScopedSessionIngestionState call after heartbeat line removal. Clearing the cursor causes the next ingestion to re-process the transcript from the beginning, duplicating normal corpus lines that were deliberately retained. P2: Remove early return after heartbeat cleanup so the self-ingestion narrative content check and archiveDiary request still run. * fix(dreaming): resolve corpus ref session path without duplicate agent ID The session path in corpus refs is already in the format 'sessions/main/abc.jsonl' (includes agent subdirectory). The previous code prepended source.agentId again, creating a path like 'agents/main/sessions/main/abc.jsonl' — which silently returned empty in production workspaces. Fix: use path.basename to extract just the filename from the session path, then construct the transcript path correctly. * refactor(dreaming): split repair utils into separate file to satisfy LOC ratchet The dreaming-repair.ts file grew from 337 to 831 lines, exceeding the 500-line LOC ratchet limit. Split helper utilities into a new dreaming-repair-utils.ts file: - dreaming-repair.ts (365 lines): imports, types, public API functions (auditDreamingArtifacts, repairDreamingArtifacts) - dreaming-repair-utils.ts (498 lines): all helper functions, constants, and internal types * fix(ts): add missing type imports for return types in dreaming-repair.ts * fix(deadcode): remove unused exports from dreaming-repair-utils.ts * fix: resolve merge conflict marker in get-reply-run.ts * fix: remove unnecessary export from INPUT_PROVENANCE_KIND_VALUES * fix(dreaming): converge targeted repair path with wholesale archive+clear When heartbeat contamination is found, archive the entire session-corpus directory and clear all SQLite checkpoints instead of doing targeted line-by-line rewrite. This ensures the cleaned corpus gets re-ingested under the new provenance-based forward filter. * fix(dreaming): authenticate heartbeat transcript turns Co-authored-by: Erick Kinnee <1707617+ekinnee@users.noreply.github.com> --------- Co-authored-by: Erick Kinnee <erick@ekinnee.dev> Co-authored-by: Erick Kinnee <ekinnee@gmail.com> Co-authored-by: Peter Steinberger <steipete@gmail.com>