Skip to content

Commit 4c32d7c

Browse files
committed
fix: apply Issue #492 fixes onto latest upstream/master (rebase)
This rebases the following fixes from PR #516 onto upstream/master (0988a46): F2 (excludeAgents runtime reading): - Add isAgentOrSessionExcluded() helper supporting exact/wildcard/temp:* patterns - Add memoryReflection.excludeAgents to PluginConfig and openclaw.plugin.json schema - Add excludeAgents check in runMemoryReflection command hook F3 (wildcard pattern fix): - Replace config.autoRecallExcludeAgents.includes(agentId) with isAgentOrSessionExcluded() in before_prompt_build hook - Supports pi-, temp:*, and exact match patterns F5 (serialCooldownMs configurable): - Add serialCooldownMs?: number to PluginConfig.memoryReflection - Serial guard now reads cooldown from cfg.memoryReflection.serialCooldownMs - Default: 120000ms (2 min), set to 0 to disable Schema additions (openclaw.plugin.json): - memoryReflection.serialCooldownMs (integer, min: 0) - memoryReflection.excludeAgents (string array) - autoRecallExcludeAgents (string array, top-level) EF1 (backtick fix already present in upstream 0988a46)
1 parent 0988a46 commit 4c32d7c

2 files changed

Lines changed: 156 additions & 22 deletions

File tree

index.ts

Lines changed: 62 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -205,6 +205,10 @@ interface PluginConfig {
205205
thinkLevel?: ReflectionThinkLevel;
206206
errorReminderMaxEntries?: number;
207207
dedupeErrorSignals?: boolean;
208+
/** Cooldown in ms between reflection triggers for the same session. Default: 120000 (2 min). Set to 0 to disable. */
209+
serialCooldownMs?: number;
210+
/** Agent/session patterns excluded from reflection injection. Supports exact match, wildcard prefix (e.g. "pi-"), and "temp:*". */
211+
excludeAgents?: string[];
208212
};
209213
mdMirror?: { enabled?: boolean; dir?: string };
210214
workspaceBoundary?: WorkspaceBoundaryConfig;
@@ -1620,6 +1624,38 @@ const pluginVersion = getPluginVersion();
16201624
// hook/tool registration for the new API instance" regression that rwmjhb identified.
16211625
const _registeredApis = new WeakSet<OpenClawPluginApi>();
16221626

1627+
function isAgentOrSessionExcluded(
1628+
agentId: string,
1629+
sessionKey: string | undefined,
1630+
patterns: string[],
1631+
): boolean {
1632+
if (!Array.isArray(patterns) || patterns.length === 0) return false;
1633+
1634+
const cleanAgentId = agentId.trim();
1635+
const isInternal = typeof sessionKey === "string" &&
1636+
sessionKey.trim().startsWith("temp:memory-reflection");
1637+
1638+
for (const pattern of patterns) {
1639+
const p = typeof pattern === "string" ? pattern.trim() : "";
1640+
if (!p) continue;
1641+
1642+
if (p === "temp:*") {
1643+
if (isInternal) return true;
1644+
continue;
1645+
}
1646+
1647+
if (p.endsWith("-")) {
1648+
// Wildcard prefix match: "pi-" matches "pi-agent"
1649+
const prefix = p.slice(0, -1);
1650+
if (cleanAgentId.startsWith(prefix)) return true;
1651+
} else if (p === cleanAgentId) {
1652+
return true;
1653+
}
1654+
}
1655+
1656+
return false;
1657+
}
1658+
16231659
const memoryLanceDBProPlugin = {
16241660
id: "memory-lancedb-pro",
16251661
name: "Memory (LanceDB Pro)",
@@ -2222,15 +2258,15 @@ const memoryLanceDBProPlugin = {
22222258
const AUTO_RECALL_TIMEOUT_MS = parsePositiveInt(config.autoRecallTimeoutMs) ?? 5_000; // configurable; default raised from 3s to 5s for remote embedding APIs behind proxies
22232259
api.on("before_prompt_build", async (event: any, ctx: any) => {
22242260
// Per-agent exclusion: skip auto-recall for agents in the exclusion list.
2225-
const agentId = resolveHookAgentId(ctx?.agentId, (event as any).sessionKey);
2261+
const sessionKey = (event as any).sessionKey as string | undefined;
2262+
const agentId = resolveHookAgentId(ctx?.agentId, sessionKey);
22262263
if (
22272264
Array.isArray(config.autoRecallExcludeAgents) &&
22282265
config.autoRecallExcludeAgents.length > 0 &&
2229-
agentId !== undefined &&
2230-
config.autoRecallExcludeAgents.includes(agentId)
2266+
isAgentOrSessionExcluded(agentId, sessionKey, config.autoRecallExcludeAgents)
22312267
) {
22322268
api.logger.debug?.(
2233-
`memory-lancedb-pro: auto-recall skipped for excluded agent '${agentId}'`,
2269+
`memory-lancedb-pro: auto-recall skipped for excluded agent '${agentId}' (sessionKey=${sessionKey ?? "(none)"})`,
22342270
);
22352271
return;
22362272
}
@@ -3199,22 +3235,28 @@ const memoryLanceDBProPlugin = {
31993235
api.logger.info(`memory-reflection: skipping re-entrant call for sessionKey=${sessionKey}; already running (global guard)`);
32003236
return;
32013237
}
3238+
// Parse context before guards so cfg is available for serialCooldownMs
3239+
const context = (event.context || {}) as Record<string, unknown>;
3240+
const cfg = context.cfg;
32023241
// Serial loop guard: skip if a reflection for this sessionKey completed recently
32033242
if (sessionKey) {
32043243
const serialGuard = getSerialGuardMap();
32053244
const lastRun = serialGuard.get(sessionKey);
3206-
if (lastRun && (Date.now() - lastRun) < SERIAL_GUARD_COOLDOWN_MS) {
3207-
api.logger.info(`memory-reflection: skipping serial re-trigger for sessionKey=${sessionKey}; last run ${(Date.now() - lastRun) / 1000}s ago (cooldown=${SERIAL_GUARD_COOLDOWN_MS / 1000}s)`);
3208-
return;
3245+
if (lastRun) {
3246+
const cooldownMs = typeof (cfg?.memoryReflection as Record<string, unknown> | undefined)?.serialCooldownMs === "number"
3247+
? (cfg!.memoryReflection as Record<string, unknown>).serialCooldownMs as number
3248+
: 120_000;
3249+
if ((Date.now() - lastRun) < cooldownMs) {
3250+
api.logger.info(`memory-reflection: command hook skipped (cooldown ${((Date.now() - lastRun) / 1000).toFixed(0)}s/${(cooldownMs / 1000).toFixed(0)}s, sessionKey=${sessionKey})`);
3251+
return;
3252+
}
32093253
}
32103254
}
32113255
if (sessionKey) globalLock.set(sessionKey, true);
32123256
let reflectionRan = false;
32133257
try {
32143258
pruneReflectionSessionState();
32153259
const action = String(event?.action || "unknown");
3216-
const context = (event.context || {}) as Record<string, unknown>;
3217-
const cfg = context.cfg;
32183260
const workspaceDir = resolveWorkspaceDirFromContext(context);
32193261
if (!cfg) {
32203262
api.logger.warn(`memory-reflection: command:${action} missing cfg in hook context; skip reflection`);
@@ -3225,6 +3267,17 @@ const memoryLanceDBProPlugin = {
32253267
const currentSessionId = typeof sessionEntry.sessionId === "string" ? sessionEntry.sessionId : "unknown";
32263268
let currentSessionFile = typeof sessionEntry.sessionFile === "string" ? sessionEntry.sessionFile : undefined;
32273269
const sourceAgentId = parseAgentIdFromSessionKey(sessionKey) || "main";
3270+
// Exclude agents/sessions listed in memoryReflection.excludeAgents (supports wildcards)
3271+
const excludePatterns = (cfg as Record<string, unknown> | undefined)?.memoryReflection
3272+
? ((cfg as Record<string, unknown>).memoryReflection as Record<string, unknown>)?.excludeAgents as string[] | undefined
3273+
: undefined;
3274+
if (excludePatterns && isAgentOrSessionExcluded(sourceAgentId, sessionKey, excludePatterns)) {
3275+
api.logger.debug?.(
3276+
`memory-reflection: command hook skipped (excluded agent=${sourceAgentId}, sessionKey=${sessionKey ?? "(none)"})`,
3277+
);
3278+
return;
3279+
}
3280+
32283281
const commandSource = typeof context.commandSource === "string" ? context.commandSource : "";
32293282
api.logger.info(
32303283
`memory-reflection: command:${action} hook start; sessionKey=${sessionKey || "(none)"}; source=${commandSource || "(unknown)"}; sessionId=${currentSessionId}; sessionFile=${currentSessionFile || "(none)"}`

openclaw.plugin.json

Lines changed: 94 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,9 @@
44
"description": "Enhanced LanceDB-backed long-term memory with hybrid retrieval, multi-scope isolation, long-context chunking, and management CLI",
55
"version": "1.1.0-beta.10",
66
"kind": "memory",
7-
"skills": ["./skills"],
7+
"skills": [
8+
"./skills"
9+
],
810
"configSchema": {
911
"type": "object",
1012
"additionalProperties": false,
@@ -161,7 +163,12 @@
161163
},
162164
"recallMode": {
163165
"type": "string",
164-
"enum": ["full", "summary", "adaptive", "off"],
166+
"enum": [
167+
"full",
168+
"summary",
169+
"adaptive",
170+
"off"
171+
],
165172
"default": "full",
166173
"description": "Auto-recall depth mode. 'full': inject with configured per-item budget. 'summary': L0 abstracts only (compact). 'adaptive': analyze query intent to auto-select category and depth. 'off': disable auto-recall injection."
167174
},
@@ -260,23 +267,78 @@
260267
"type": "object",
261268
"additionalProperties": false,
262269
"properties": {
263-
"utility": { "type": "number", "minimum": 0, "maximum": 1, "default": 0.1 },
264-
"confidence": { "type": "number", "minimum": 0, "maximum": 1, "default": 0.1 },
265-
"novelty": { "type": "number", "minimum": 0, "maximum": 1, "default": 0.1 },
266-
"recency": { "type": "number", "minimum": 0, "maximum": 1, "default": 0.1 },
267-
"typePrior": { "type": "number", "minimum": 0, "maximum": 1, "default": 0.6 }
270+
"utility": {
271+
"type": "number",
272+
"minimum": 0,
273+
"maximum": 1,
274+
"default": 0.1
275+
},
276+
"confidence": {
277+
"type": "number",
278+
"minimum": 0,
279+
"maximum": 1,
280+
"default": 0.1
281+
},
282+
"novelty": {
283+
"type": "number",
284+
"minimum": 0,
285+
"maximum": 1,
286+
"default": 0.1
287+
},
288+
"recency": {
289+
"type": "number",
290+
"minimum": 0,
291+
"maximum": 1,
292+
"default": 0.1
293+
},
294+
"typePrior": {
295+
"type": "number",
296+
"minimum": 0,
297+
"maximum": 1,
298+
"default": 0.6
299+
}
268300
}
269301
},
270302
"typePriors": {
271303
"type": "object",
272304
"additionalProperties": false,
273305
"properties": {
274-
"profile": { "type": "number", "minimum": 0, "maximum": 1, "default": 0.95 },
275-
"preferences": { "type": "number", "minimum": 0, "maximum": 1, "default": 0.9 },
276-
"entities": { "type": "number", "minimum": 0, "maximum": 1, "default": 0.75 },
277-
"events": { "type": "number", "minimum": 0, "maximum": 1, "default": 0.45 },
278-
"cases": { "type": "number", "minimum": 0, "maximum": 1, "default": 0.8 },
279-
"patterns": { "type": "number", "minimum": 0, "maximum": 1, "default": 0.85 }
306+
"profile": {
307+
"type": "number",
308+
"minimum": 0,
309+
"maximum": 1,
310+
"default": 0.95
311+
},
312+
"preferences": {
313+
"type": "number",
314+
"minimum": 0,
315+
"maximum": 1,
316+
"default": 0.9
317+
},
318+
"entities": {
319+
"type": "number",
320+
"minimum": 0,
321+
"maximum": 1,
322+
"default": 0.75
323+
},
324+
"events": {
325+
"type": "number",
326+
"minimum": 0,
327+
"maximum": 1,
328+
"default": 0.45
329+
},
330+
"cases": {
331+
"type": "number",
332+
"minimum": 0,
333+
"maximum": 1,
334+
"default": 0.8
335+
},
336+
"patterns": {
337+
"type": "number",
338+
"minimum": 0,
339+
"maximum": 1,
340+
"default": 0.85
341+
}
280342
}
281343
}
282344
}
@@ -650,6 +712,18 @@
650712
"dedupeErrorSignals": {
651713
"type": "boolean",
652714
"default": true
715+
},
716+
"serialCooldownMs": {
717+
"type": "integer",
718+
"minimum": 0,
719+
"description": "Cooldown in ms between reflection triggers for the same session. Default: 120000 (2 min). Set to 0 to disable."
720+
},
721+
"excludeAgents": {
722+
"type": "array",
723+
"items": {
724+
"type": "string"
725+
},
726+
"description": "Agent/session patterns excluded from reflection injection. Supports exact match, wildcard prefix (e.g. pi-), and temp:*."
653727
}
654728
}
655729
},
@@ -853,6 +927,13 @@
853927
"description": "Maximum number of auto-capture extractions allowed per hour"
854928
}
855929
}
930+
},
931+
"autoRecallExcludeAgents": {
932+
"type": "array",
933+
"items": {
934+
"type": "string"
935+
},
936+
"description": "Agent/session patterns excluded from auto-recall and reflection injection. Supports exact match, wildcard prefix (e.g. pi-), and temp:*."
856937
}
857938
}
858939
},

0 commit comments

Comments
 (0)