From 2e3b59bc58043f04233dcbfa5e280b7d240e3fa0 Mon Sep 17 00:00:00 2001 From: "clawsweeper[bot]" <274271284+clawsweeper[bot]@users.noreply.github.com> Date: Mon, 25 May 2026 14:35:43 +0000 Subject: [PATCH] fix: guard QMD session stem fallback (#86482) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Summary: - This PR changes `resolveTranscriptStemToSessionKeys` to skip empty or missing `sessionId` values during QMD slug fallback, adds regression coverage, and adds a changelog entry. - PR surface: Source +1, Tests +17, Docs +1. Total +19 across 3 files. - Reproducibility: yes. from source inspection: current main reaches `normalizeQmdSessionStem(entry.sessionId) ... ad-only review, but the source PR includes a direct after-fix resolver probe for the same mixed-store case. Automerge notes: - PR branch already contained follow-up commit before automerge: fix: guard QMD session stem fallback - PR branch already contained follow-up commit before automerge: fix(clawsweeper): address review for automerge-openclaw-openclaw-8632… Validation: - ClawSweeper review passed for head 81478b0ee6cc77f4ecf4287524424c91c3bd9bfa. - Required merge gates passed before the squash merge. Prepared head SHA: 81478b0ee6cc77f4ecf4287524424c91c3bd9bfa Review: https://github.com/openclaw/openclaw/pull/86482#issuecomment-4534348706 Co-authored-by: abnershang Co-authored-by: clawsweeper <274271284+clawsweeper[bot]@users.noreply.github.com> Co-authored-by: Abner Shang <75654486+abnershang@users.noreply.github.com> Co-authored-by: clawsweeper[bot] <274271284+clawsweeper[bot]@users.noreply.github.com> Approved-by: takhoffman Co-authored-by: takhoffman <781889+takhoffman@users.noreply.github.com> --- CHANGELOG.md | 1 + src/plugin-sdk/session-transcript-hit.test.ts | 17 +++++++++++++++++ src/plugin-sdk/session-transcript-hit.ts | 3 ++- 3 files changed, 20 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 61f1b9e08f8..7fd57cdec5a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -23,6 +23,7 @@ Docs: https://docs.openclaw.ai - Media understanding: convert HEIC and HEIF images to JPEG before image description providers run so iPhone photos work in direct and configured image-description flows. (#86037) - Agents: release embedded-attempt session locks from outer teardown so post-prompt exceptions cannot wedge later requests behind `SessionWriteLockTimeoutError`. Fixes #86014. Thanks @openperf. - Discord/OpenAI voice: rotate Realtime sessions at provider max duration without logging the expected session-expiry event as an error. +- Sessions: skip metadata-only entries during QMD-slugified session lookup so one incomplete row does not block transcript hit resolution. (#86327) Thanks @abnershang. - Agents/media: derive bundled plugin local-media trust from plugin tool metadata instead of importing the full plugin registry on subscription paths. (#84409) Thanks @samzong. - Image tool: keep config-backed custom-provider API keys usable for auto-discovered vision models, including deferred image-tool execution without env keys or auth profiles. (#85733) - Memory/local embeddings: run local GGUF embeddings in an isolated worker sidecar and degrade to configured fallback or keyword search on worker failure so native embedding crashes do not take down the Gateway. (#85348) Thanks @osolmaz. diff --git a/src/plugin-sdk/session-transcript-hit.test.ts b/src/plugin-sdk/session-transcript-hit.test.ts index 6522bf0b499..3e575950dfa 100644 --- a/src/plugin-sdk/session-transcript-hit.test.ts +++ b/src/plugin-sdk/session-transcript-hit.test.ts @@ -217,6 +217,23 @@ describe("resolveTranscriptStemToSessionKeys", () => { ).toEqual(["agent:main:s1"]); }); + it("ignores store entries without session ids during QMD-slugified fallback", () => { + const store: Record = { + "agent:main:non-session": { + updatedAt: 1, + } as SessionEntry, + "agent:main:s1": baseEntry({ sessionId: "foo_bar.v1" }), + }; + + expect( + resolveTranscriptStemToSessionKeys({ + store, + stem: "foo-bar-v1", + allowQmdSlugFallback: true, + }), + ).toEqual(["agent:main:s1"]); + }); + it("does not use QMD-slugified fallback unless requested", () => { const store: Record = { "agent:main:s1": baseEntry({ sessionId: "foo_bar.v1" }), diff --git a/src/plugin-sdk/session-transcript-hit.ts b/src/plugin-sdk/session-transcript-hit.ts index 26f0ca06540..45e6165ce26 100644 --- a/src/plugin-sdk/session-transcript-hit.ts +++ b/src/plugin-sdk/session-transcript-hit.ts @@ -151,7 +151,8 @@ export function resolveTranscriptStemToSessionKeys(params: { continue; } } - if (normalizeQmdSessionStem(entry.sessionId) === normalizedStem) { + const entrySessionId = normalizeOptionalString(entry.sessionId); + if (entrySessionId && normalizeQmdSessionStem(entrySessionId) === normalizedStem) { matches.push(sessionKey); } }