From dff437a1cbe7b5753799fc4466fe2a175a2b9460 Mon Sep 17 00:00:00 2001 From: hcl Date: Tue, 5 May 2026 02:37:05 +0800 Subject: [PATCH] fix(active-memory): skip colon-containing session-store channels to prevent crash with QQ c2c agent IDs (#77402) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Summary: - The PR filters colon-containing store-derived Active Memory channel values before embedded recall resolution, adds a QQ c2c regression test, and records the user-facing changelog entry. - Reproducibility: yes. Source inspection on current main shows a stored colon-containing `lastChannel` or `ch ... come the strong embedded recall channel, and the downstream bundled-plugin directory validator rejects `:`. Automerge notes: - PR branch already contained follow-up commit before automerge: fixup! fix(active-memory): add changelog contributor credit (clawswee… - PR branch already contained follow-up commit before automerge: fix(active-memory): skip colon-containing session-store channels Validation: - ClawSweeper review passed for head 4bf00dd6acfc95d861779ec1fdd1ae36cab93797. - Required merge gates passed before the squash merge. Prepared head SHA: 4bf00dd6acfc95d861779ec1fdd1ae36cab93797 Review: https://github.com/openclaw/openclaw/pull/77402#issuecomment-4372618783 Co-authored-by: HCL Co-authored-by: clawsweeper <274271284+clawsweeper[bot]@users.noreply.github.com> --- CHANGELOG.md | 1 + extensions/active-memory/index.test.ts | 27 ++++++++++++++++++++++++++ extensions/active-memory/index.ts | 10 +++++++++- 3 files changed, 37 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8a149cb9fb5..7a363cf4da0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ Docs: https://docs.openclaw.ai ### Changes +- Plugins/active-memory: skip session-store channel entries that contain `:` when resolving the recall subagent's channel, so QQ c2c agent IDs (e.g. `c2c:10D4F7C2…`) and other scoped conversation IDs do not reach bundled-plugin `dirName` validation and crash the recall run. The same guard already applied to explicit `channelId` params (#76704); this extends it to store-derived channels. (#77396) Thanks @hclsys. - Models/auth: add `openclaw models auth list [--provider ] [--json]` so users can inspect saved per-agent auth profiles without dumping secrets or hitting the old “too many arguments” path. Thanks @vincentkoc. - Control UI/header: show the active agent name in dashboard breadcrumbs without adding the current session key, keeping non-chat views oriented without crowding the topbar. - Control UI/cron: make the New Job sidebar collapsible so the jobs list can reclaim space while keeping the form one click away. Thanks @BunsDev. diff --git a/extensions/active-memory/index.test.ts b/extensions/active-memory/index.test.ts index 9d695573c09..695c15bde84 100644 --- a/extensions/active-memory/index.test.ts +++ b/extensions/active-memory/index.test.ts @@ -2753,6 +2753,33 @@ describe("active-memory plugin", () => { }); }); + it("skips colon-containing session-store channels for embedded recall (#77396)", async () => { + hoisted.sessionStore["agent:main:qqbot:direct:12345"] = { + sessionId: "session-a", + updatedAt: 25, + channel: "c2c:10D4F7C2", + origin: { + provider: "qqbot", + }, + }; + + await hooks.before_prompt_build( + { prompt: "what wings should i order? scoped stored channel", messages: [] }, + { + agentId: "main", + trigger: "user", + sessionKey: "agent:main:qqbot:direct:12345", + messageProvider: "qqbot", + channelId: "qqbot", + }, + ); + + expect(runEmbeddedPiAgent.mock.calls.at(-1)?.[0]).toMatchObject({ + messageChannel: "qqbot", + messageProvider: "qqbot", + }); + }); + it("preserves an explicit real channel hint over a stale stored wrapper channel", async () => { hoisted.sessionStore["agent:main:telegram:direct:12345"] = { sessionId: "session-a", diff --git a/extensions/active-memory/index.ts b/extensions/active-memory/index.ts index a7d4e195953..314c3df6490 100644 --- a/extensions/active-memory/index.ts +++ b/extensions/active-memory/index.ts @@ -560,9 +560,17 @@ function resolveRecallRunChannelContext(params: { store, sessionKey: resolvedSessionKey, }).existing; - const strongEntryChannel = + const rawStrongEntryChannel = normalizeOptionalString(sessionEntry?.lastChannel) ?? normalizeOptionalString(sessionEntry?.channel); + // Channel IDs containing ":" are scoped conversation IDs (e.g. QQ c2c + // "c2c:10D4F7C2..."), not runnable channel names. The same guard that + // applies to explicit channelId (#76704) must also apply to channels + // read from the session store (#77396). + const strongEntryChannel = + rawStrongEntryChannel && !rawStrongEntryChannel.includes(":") + ? rawStrongEntryChannel + : undefined; const weakEntryChannel = normalizeOptionalString(sessionEntry?.origin?.provider); return resolveReturnValue({ resolvedChannel: strongEntryChannel ?? weakEntryChannel,