From 3f90d92667994c04a23e4b69a4e5c4d9639c8184 Mon Sep 17 00:00:00 2001 From: Magicray1217 Date: Tue, 21 Apr 2026 06:54:35 +0800 Subject: [PATCH] fix(active-memory): gracefully degrade on timeout instead of failing entire reply. Fixes #66849 --- extensions/active-memory/index.test.ts | 21 ++++ extensions/active-memory/index.ts | 152 +++++++++++++------------ 2 files changed, 102 insertions(+), 71 deletions(-) diff --git a/extensions/active-memory/index.test.ts b/extensions/active-memory/index.test.ts index 82931984578..15206f02177 100644 --- a/extensions/active-memory/index.test.ts +++ b/extensions/active-memory/index.test.ts @@ -1147,6 +1147,27 @@ describe("active-memory plugin", () => { ).toBe(true); }); + it("returns undefined instead of throwing when an unexpected error escapes the recall path", async () => { + runEmbeddedPiAgent.mockRejectedValueOnce(new Error("network reset")); + hoisted.updateSessionStore.mockRejectedValueOnce(new Error("store unavailable")); + + const result = await hooks.before_prompt_build( + { prompt: "what should i eat? escape test", messages: [] }, + { + agentId: "main", + trigger: "user", + sessionKey: "agent:main:escape-test", + messageProvider: "webchat", + }, + ); + + expect(result).toBeUndefined(); + const warnLines = vi + .mocked(api.logger.warn) + .mock.calls.map((call: unknown[]) => String(call[0])); + expect(warnLines.some((line: string) => line.includes("before_prompt_build"))).toBe(true); + }); + it("honors configured timeoutMs values above the former 60 000 ms ceiling", async () => { api.pluginConfig = { agents: ["main"], diff --git a/extensions/active-memory/index.ts b/extensions/active-memory/index.ts index c3e2290b602..73f333b2d7d 100644 --- a/extensions/active-memory/index.ts +++ b/extensions/active-memory/index.ts @@ -1958,83 +1958,93 @@ export default definePluginEntry({ }); api.on("before_prompt_build", async (event, ctx) => { - const resolvedAgentId = resolveStatusUpdateAgentId(ctx); - const resolvedSessionKey = - ctx.sessionKey?.trim() || - (resolvedAgentId - ? resolveCanonicalSessionKeyFromSessionId({ - api, - agentId: resolvedAgentId, - sessionId: ctx.sessionId, - }) - : undefined); - const effectiveAgentId = - resolvedAgentId || resolveStatusUpdateAgentId({ sessionKey: resolvedSessionKey }); - if (await isSessionActiveMemoryDisabled({ api, sessionKey: resolvedSessionKey })) { - await persistPluginStatusLines({ + try { + const resolvedAgentId = resolveStatusUpdateAgentId(ctx); + const resolvedSessionKey = + ctx.sessionKey?.trim() || + (resolvedAgentId + ? resolveCanonicalSessionKeyFromSessionId({ + api, + agentId: resolvedAgentId, + sessionId: ctx.sessionId, + }) + : undefined); + const effectiveAgentId = + resolvedAgentId || resolveStatusUpdateAgentId({ sessionKey: resolvedSessionKey }); + if (await isSessionActiveMemoryDisabled({ api, sessionKey: resolvedSessionKey })) { + await persistPluginStatusLines({ + api, + agentId: effectiveAgentId, + sessionKey: resolvedSessionKey, + }); + return undefined; + } + if (!isEnabledForAgent(config, effectiveAgentId)) { + await persistPluginStatusLines({ + api, + agentId: effectiveAgentId, + sessionKey: resolvedSessionKey, + }); + return undefined; + } + if (!isEligibleInteractiveSession(ctx)) { + await persistPluginStatusLines({ + api, + agentId: effectiveAgentId, + sessionKey: resolvedSessionKey, + }); + return undefined; + } + if ( + !isAllowedChatType(config, { + ...ctx, + sessionKey: resolvedSessionKey ?? ctx.sessionKey, + mainKey: api.config.session?.mainKey, + }) + ) { + await persistPluginStatusLines({ + api, + agentId: effectiveAgentId, + sessionKey: resolvedSessionKey, + }); + return undefined; + } + const query = buildQuery({ + latestUserMessage: event.prompt, + recentTurns: extractRecentTurns(event.messages), + config, + }); + const result = await maybeResolveActiveRecall({ api, + config, agentId: effectiveAgentId, sessionKey: resolvedSessionKey, + sessionId: ctx.sessionId, + messageProvider: ctx.messageProvider, + channelId: ctx.channelId, + query, + currentModelProviderId: ctx.modelProviderId, + currentModelId: ctx.modelId, }); + if (!result.summary) { + return undefined; + } + const promptPrefix = buildPromptPrefix(result.summary); + if (!promptPrefix) { + return undefined; + } + return { + prependContext: promptPrefix, + }; + } catch (error) { + const message = toSingleLineLogValue( + error instanceof Error ? error.message : String(error), + ); + api.logger.warn?.( + `active-memory: before_prompt_build failed, skipping memory lookup: ${message}`, + ); return undefined; } - if (!isEnabledForAgent(config, effectiveAgentId)) { - await persistPluginStatusLines({ - api, - agentId: effectiveAgentId, - sessionKey: resolvedSessionKey, - }); - return undefined; - } - if (!isEligibleInteractiveSession(ctx)) { - await persistPluginStatusLines({ - api, - agentId: effectiveAgentId, - sessionKey: resolvedSessionKey, - }); - return undefined; - } - if ( - !isAllowedChatType(config, { - ...ctx, - sessionKey: resolvedSessionKey ?? ctx.sessionKey, - mainKey: api.config.session?.mainKey, - }) - ) { - await persistPluginStatusLines({ - api, - agentId: effectiveAgentId, - sessionKey: resolvedSessionKey, - }); - return undefined; - } - const query = buildQuery({ - latestUserMessage: event.prompt, - recentTurns: extractRecentTurns(event.messages), - config, - }); - const result = await maybeResolveActiveRecall({ - api, - config, - agentId: effectiveAgentId, - sessionKey: resolvedSessionKey, - sessionId: ctx.sessionId, - messageProvider: ctx.messageProvider, - channelId: ctx.channelId, - query, - currentModelProviderId: ctx.modelProviderId, - currentModelId: ctx.modelId, - }); - if (!result.summary) { - return undefined; - } - const promptPrefix = buildPromptPrefix(result.summary); - if (!promptPrefix) { - return undefined; - } - return { - prependContext: promptPrefix, - }; }); }, });