mirror of
https://github.com/moltbot/moltbot.git
synced 2026-04-20 13:13:06 +00:00
Fix active-memory recall runs when mx-claw is enabled (#65049)
* fix(active-memory): preserve parent channel context for recall runs * fix(active-memory): keep recall runs on the resolved channel * fix(active-memory): prefer resolved recall channel over wrapper hints * fix(active-memory): trust explicit recall channel hints * fix(active-memory): rank recall channel fallbacks by trust
This commit is contained in:
@@ -471,7 +471,6 @@ describe("active-memory plugin", () => {
|
||||
expect(runEmbeddedPiAgent.mock.calls.at(-1)?.[0]).toMatchObject({
|
||||
provider: "github-copilot",
|
||||
model: "gpt-5.4-mini",
|
||||
messageChannel: "webchat",
|
||||
messageProvider: "webchat",
|
||||
sessionKey: expect.stringMatching(/^agent:main:main:active-memory:[a-f0-9]{12}$/),
|
||||
config: {
|
||||
@@ -1155,7 +1154,7 @@ describe("active-memory plugin", () => {
|
||||
);
|
||||
expect(runEmbeddedPiAgent.mock.calls.at(-1)?.[0]).toMatchObject({
|
||||
messageChannel: "telegram",
|
||||
messageProvider: "webchat",
|
||||
messageProvider: "telegram",
|
||||
});
|
||||
expect(hoisted.sessionStore["agent:main:telegram:direct:12345"]?.pluginDebugEntries).toEqual([
|
||||
{
|
||||
@@ -1192,6 +1191,82 @@ describe("active-memory plugin", () => {
|
||||
});
|
||||
});
|
||||
|
||||
it("prefers the resolved session channel over a wrapper channel hint", async () => {
|
||||
hoisted.sessionStore["agent:main:telegram:direct:12345"] = {
|
||||
sessionId: "session-a",
|
||||
updatedAt: 25,
|
||||
channel: "telegram",
|
||||
};
|
||||
|
||||
await hooks.before_prompt_build(
|
||||
{ prompt: "what wings should i order? wrapper channel hint", messages: [] },
|
||||
{
|
||||
agentId: "main",
|
||||
trigger: "user",
|
||||
sessionKey: "agent:main:telegram:direct:12345",
|
||||
messageProvider: "webchat",
|
||||
channelId: "webchat",
|
||||
},
|
||||
);
|
||||
|
||||
expect(runEmbeddedPiAgent.mock.calls.at(-1)?.[0]).toMatchObject({
|
||||
messageChannel: "telegram",
|
||||
messageProvider: "telegram",
|
||||
});
|
||||
});
|
||||
|
||||
it("preserves an explicit real channel hint over a stale stored wrapper channel", async () => {
|
||||
hoisted.sessionStore["agent:main:telegram:direct:12345"] = {
|
||||
sessionId: "session-a",
|
||||
updatedAt: 25,
|
||||
origin: {
|
||||
provider: "webchat",
|
||||
},
|
||||
};
|
||||
|
||||
await hooks.before_prompt_build(
|
||||
{ prompt: "what wings should i order? explicit channel hint", messages: [] },
|
||||
{
|
||||
agentId: "main",
|
||||
trigger: "user",
|
||||
sessionKey: "agent:main:telegram:direct:12345",
|
||||
messageProvider: "webchat",
|
||||
channelId: "telegram",
|
||||
},
|
||||
);
|
||||
|
||||
expect(runEmbeddedPiAgent.mock.calls.at(-1)?.[0]).toMatchObject({
|
||||
messageChannel: "telegram",
|
||||
messageProvider: "telegram",
|
||||
});
|
||||
});
|
||||
|
||||
it("preserves a direct explicit channel when weak legacy fallback disagrees", async () => {
|
||||
hoisted.sessionStore["agent:main:telegram:direct:12345"] = {
|
||||
sessionId: "session-a",
|
||||
updatedAt: 25,
|
||||
origin: {
|
||||
provider: "webchat",
|
||||
},
|
||||
};
|
||||
|
||||
await hooks.before_prompt_build(
|
||||
{ prompt: "what wings should i order? direct explicit channel", messages: [] },
|
||||
{
|
||||
agentId: "main",
|
||||
trigger: "user",
|
||||
sessionKey: "agent:main:telegram:direct:12345",
|
||||
messageProvider: "telegram",
|
||||
channelId: "telegram",
|
||||
},
|
||||
);
|
||||
|
||||
expect(runEmbeddedPiAgent.mock.calls.at(-1)?.[0]).toMatchObject({
|
||||
messageChannel: "telegram",
|
||||
messageProvider: "telegram",
|
||||
});
|
||||
});
|
||||
|
||||
it("clears stale status on skipped non-interactive turns even when agentId is missing", async () => {
|
||||
const sessionKey = "noncanonical-session";
|
||||
hoisted.sessionStore[sessionKey] = {
|
||||
|
||||
@@ -369,6 +369,28 @@ function resolveRecallRunChannelContext(params: {
|
||||
} {
|
||||
const explicitChannel = normalizeOptionalString(params.channelId);
|
||||
const explicitProvider = normalizeOptionalString(params.messageProvider);
|
||||
const trustedExplicitChannel =
|
||||
explicitChannel && explicitChannel !== explicitProvider ? explicitChannel : undefined;
|
||||
const resolveReturnValue = (params: {
|
||||
resolvedChannel?: string;
|
||||
resolvedChannelStrength?: "strong" | "weak";
|
||||
}) => {
|
||||
const trustedResolvedChannel =
|
||||
params.resolvedChannelStrength === "strong" ? params.resolvedChannel : undefined;
|
||||
return {
|
||||
messageChannel:
|
||||
trustedExplicitChannel ??
|
||||
trustedResolvedChannel ??
|
||||
explicitChannel ??
|
||||
params.resolvedChannel,
|
||||
messageProvider:
|
||||
trustedExplicitChannel ??
|
||||
trustedResolvedChannel ??
|
||||
explicitProvider ??
|
||||
explicitChannel ??
|
||||
params.resolvedChannel,
|
||||
};
|
||||
};
|
||||
const resolvedSessionKey =
|
||||
normalizeOptionalString(params.sessionKey) ??
|
||||
resolveCanonicalSessionKeyFromSessionId({
|
||||
@@ -377,10 +399,7 @@ function resolveRecallRunChannelContext(params: {
|
||||
sessionId: params.sessionId,
|
||||
});
|
||||
if (!resolvedSessionKey) {
|
||||
return {
|
||||
messageChannel: explicitChannel ?? explicitProvider,
|
||||
messageProvider: explicitProvider ?? explicitChannel,
|
||||
};
|
||||
return resolveReturnValue({});
|
||||
}
|
||||
|
||||
try {
|
||||
@@ -395,19 +414,20 @@ function resolveRecallRunChannelContext(params: {
|
||||
store,
|
||||
sessionKey: resolvedSessionKey,
|
||||
}).existing;
|
||||
const entryChannel =
|
||||
const strongEntryChannel =
|
||||
normalizeOptionalString(sessionEntry?.lastChannel) ??
|
||||
normalizeOptionalString(sessionEntry?.channel) ??
|
||||
normalizeOptionalString(sessionEntry?.origin?.provider);
|
||||
return {
|
||||
messageChannel: explicitChannel ?? entryChannel ?? explicitProvider,
|
||||
messageProvider: explicitProvider ?? explicitChannel ?? entryChannel,
|
||||
};
|
||||
normalizeOptionalString(sessionEntry?.channel);
|
||||
const weakEntryChannel = normalizeOptionalString(sessionEntry?.origin?.provider);
|
||||
return resolveReturnValue({
|
||||
resolvedChannel: strongEntryChannel ?? weakEntryChannel,
|
||||
resolvedChannelStrength: strongEntryChannel
|
||||
? "strong"
|
||||
: weakEntryChannel
|
||||
? "weak"
|
||||
: undefined,
|
||||
});
|
||||
} catch {
|
||||
return {
|
||||
messageChannel: explicitChannel ?? explicitProvider,
|
||||
messageProvider: explicitProvider ?? explicitChannel,
|
||||
};
|
||||
return resolveReturnValue({});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import type { OpenClawConfig } from "openclaw/plugin-sdk/config-runtime";
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { msteamsPlugin } from "./channel.js";
|
||||
import { msTeamsApprovalAuth } from "./approval-auth.js";
|
||||
import { msteamsPlugin } from "./channel.js";
|
||||
|
||||
function createConfiguredMSTeamsCfg(): OpenClawConfig {
|
||||
return {
|
||||
|
||||
Reference in New Issue
Block a user