fix(active-memory): gracefully degrade on timeout instead of failing entire reply. Fixes #66849

This commit is contained in:
Magicray1217
2026-04-21 06:54:35 +08:00
committed by Peter Steinberger
parent 8a2d7f2541
commit 3f90d92667
2 changed files with 102 additions and 71 deletions

View File

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

View File

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