From 6d539db011e43dfb21712290ec24eb3e9ab53b4b Mon Sep 17 00:00:00 2001 From: Lidang Jiang <119769478+Lidang-Jiang@users.noreply.github.com> Date: Tue, 28 Apr 2026 21:43:06 +0800 Subject: [PATCH] fix: support explicit active-memory chat types (openclaw#66285) Verified: - pnpm install --frozen-lockfile - pnpm test extensions/active-memory/config.test.ts extensions/active-memory/index.test.ts - pnpm exec oxfmt --check --threads=1 CHANGELOG.md extensions/active-memory/index.ts extensions/active-memory/index.test.ts extensions/active-memory/config.test.ts extensions/active-memory/openclaw.plugin.json - git diff --check Co-authored-by: Lidang-Jiang <119769478+Lidang-Jiang@users.noreply.github.com> Co-authored-by: Tak Hoffman <781889+Takhoffman@users.noreply.github.com> --- CHANGELOG.md | 1 + extensions/active-memory/config.test.ts | 28 +++++++++++ extensions/active-memory/index.test.ts | 48 +++++++++++++++++++ extensions/active-memory/index.ts | 11 +++-- extensions/active-memory/openclaw.plugin.json | 4 +- 5 files changed, 86 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a10b6c517b9..12875a1b100 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ Docs: https://docs.openclaw.ai ### Fixes +- Active Memory: allow `allowedChatTypes` to include explicit portal/webchat sessions and classify `agent:...:explicit:...` session keys before opaque session ids can shadow the chat type. Fixes #65775. (#66285) Thanks @Lidang-Jiang. - fix(device-pairing): validate callerScopes against resolved token scopes on repair [AI]. (#72925) Thanks @pgondhi987. - Active Memory docs: document the `cacheTtlMs` 1000-120000 ms range and 15000 ms default so setup snippets do not lead users past the schema limit. Fixes #65708. (#65737) Thanks @WuKongAI-CMU. - fix(agents): canonicalize provider aliases in byProvider tool policy lookup [AI]. (#72917) Thanks @pgondhi987. diff --git a/extensions/active-memory/config.test.ts b/extensions/active-memory/config.test.ts index bbba7a9d81b..b50f3b29620 100644 --- a/extensions/active-memory/config.test.ts +++ b/extensions/active-memory/config.test.ts @@ -36,6 +36,20 @@ describe("active-memory manifest config schema", () => { expect(result.ok).toBe(true); }); + it("accepts explicit in allowedChatTypes", () => { + const result = validateJsonSchemaValue({ + schema: manifest.configSchema, + cacheKey: "active-memory.manifest.allowed-chat-types.explicit", + value: { + enabled: true, + agents: ["main"], + allowedChatTypes: ["direct", "explicit"], + }, + }); + + expect(result.ok).toBe(true); + }); + it("rejects timeoutMs values above the runtime ceiling", () => { const result = validateJsonSchemaValue({ schema: manifest.configSchema, @@ -49,4 +63,18 @@ describe("active-memory manifest config schema", () => { expect(result.ok).toBe(false); }); + + it("rejects unknown allowedChatTypes values", () => { + const result = validateJsonSchemaValue({ + schema: manifest.configSchema, + cacheKey: "active-memory.manifest.allowed-chat-types.invalid", + value: { + enabled: true, + agents: ["main"], + allowedChatTypes: ["direct", "portal"], + }, + }); + + expect(result.ok).toBe(false); + }); }); diff --git a/extensions/active-memory/index.test.ts b/extensions/active-memory/index.test.ts index ce0bb2350de..4b80f1301c2 100644 --- a/extensions/active-memory/index.test.ts +++ b/extensions/active-memory/index.test.ts @@ -548,6 +548,54 @@ describe("active-memory plugin", () => { }); }); + it("runs for explicit sessions when explicit chat types are explicitly allowed", async () => { + api.pluginConfig = { + agents: ["main"], + allowedChatTypes: ["explicit"], + }; + await plugin.register(api as unknown as OpenClawPluginApi); + + const result = await hooks.before_prompt_build( + { prompt: "what should i work on next?", messages: [] }, + { + agentId: "main", + trigger: "user", + sessionKey: "agent:main:explicit:portal-123", + messageProvider: "webchat", + channelId: "webchat", + }, + ); + + expect(runEmbeddedPiAgent).toHaveBeenCalledTimes(1); + expect(result).toEqual({ + prependContext: expect.stringContaining(""), + }); + }); + + it("keeps explicit session classification when the opaque session id contains chat-type tokens", async () => { + api.pluginConfig = { + agents: ["main"], + allowedChatTypes: ["explicit"], + }; + await plugin.register(api as unknown as OpenClawPluginApi); + + const result = await hooks.before_prompt_build( + { prompt: "what should i work on next?", messages: [] }, + { + agentId: "main", + trigger: "user", + sessionKey: "agent:main:explicit:portal-123:group:shadow", + messageProvider: "webchat", + channelId: "webchat", + }, + ); + + expect(runEmbeddedPiAgent).toHaveBeenCalledTimes(1); + expect(result).toEqual({ + prependContext: expect.stringContaining(""), + }); + }); + it("skips group sessions whose conversation id is not in allowedChatIds", async () => { api.pluginConfig = { agents: ["main"], diff --git a/extensions/active-memory/index.ts b/extensions/active-memory/index.ts index fd893c3cbdd..caa1cc4e04b 100644 --- a/extensions/active-memory/index.ts +++ b/extensions/active-memory/index.ts @@ -68,7 +68,7 @@ type ActiveRecallPluginConfig = { model?: string; modelFallback?: string; modelFallbackPolicy?: "default-remote" | "resolved-only"; - allowedChatTypes?: Array<"direct" | "group" | "channel">; + allowedChatTypes?: Array<"direct" | "group" | "channel" | "explicit">; allowedChatIds?: string[]; deniedChatIds?: string[]; thinking?: ActiveMemoryThinkingLevel; @@ -105,7 +105,7 @@ type ResolvedActiveRecallPluginConfig = { model?: string; modelFallback?: string; modelFallbackPolicy: "default-remote" | "resolved-only"; - allowedChatTypes: Array<"direct" | "group" | "channel">; + allowedChatTypes: Array<"direct" | "group" | "channel" | "explicit">; allowedChatIds: string[]; deniedChatIds: string[]; thinking: ActiveMemoryThinkingLevel; @@ -176,7 +176,7 @@ type CachedActiveRecallResult = { result: ActiveRecallResult; }; -type ActiveMemoryChatType = "direct" | "group" | "channel"; +type ActiveMemoryChatType = "direct" | "group" | "channel" | "explicit"; type ActiveMemoryToggleStore = { sessions?: Record; @@ -643,7 +643,7 @@ function normalizePluginConfig(pluginConfig: unknown): ResolvedActiveRecallPlugi const allowedChatTypes = Array.isArray(raw.allowedChatTypes) ? raw.allowedChatTypes.filter( (value): value is ActiveMemoryChatType => - value === "direct" || value === "group" || value === "channel", + value === "direct" || value === "group" || value === "channel" || value === "explicit", ) : []; return { @@ -913,6 +913,9 @@ function resolveChatType(ctx: { }): ActiveMemoryChatType | undefined { const sessionKey = ctx.sessionKey?.trim().toLowerCase(); if (sessionKey) { + if (sessionKey.startsWith("agent:") && sessionKey.split(":")[2] === "explicit") { + return "explicit"; + } if (sessionKey.includes(":group:")) { return "group"; } diff --git a/extensions/active-memory/openclaw.plugin.json b/extensions/active-memory/openclaw.plugin.json index 22cf64af206..bf5bbb4fea5 100644 --- a/extensions/active-memory/openclaw.plugin.json +++ b/extensions/active-memory/openclaw.plugin.json @@ -24,7 +24,7 @@ "type": "array", "items": { "type": "string", - "enum": ["direct", "group", "channel"] + "enum": ["direct", "group", "channel", "explicit"] } }, "allowedChatIds": { @@ -101,7 +101,7 @@ }, "allowedChatTypes": { "label": "Allowed Chat Types", - "help": "Choose which session types may run Active Memory. Defaults to direct-message style sessions only." + "help": "Choose which session types may run Active Memory. Defaults to direct-message style sessions only, but explicit portal/webchat sessions can also be enabled." }, "allowedChatIds": { "label": "Allowed Chat IDs",