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>
This commit is contained in:
Lidang Jiang
2026-04-28 21:43:06 +08:00
committed by GitHub
parent ba17b8b728
commit 6d539db011
5 changed files with 86 additions and 6 deletions

View File

@@ -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.

View File

@@ -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);
});
});

View File

@@ -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("<active_memory_plugin>"),
});
});
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("<active_memory_plugin>"),
});
});
it("skips group sessions whose conversation id is not in allowedChatIds", async () => {
api.pluginConfig = {
agents: ["main"],

View File

@@ -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<string, { disabled?: boolean; updatedAt?: number }>;
@@ -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";
}

View File

@@ -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",