mirror of
https://github.com/moltbot/moltbot.git
synced 2026-04-20 21:23:23 +00:00
fix(memory-core): limit runtime dreaming cron reconcile to heartbeats (#63938)
Merged via squash.
Prepared head SHA: 845c1e2763
Co-authored-by: mbelinky <132747814+mbelinky@users.noreply.github.com>
Co-authored-by: mbelinky <132747814+mbelinky@users.noreply.github.com>
Reviewed-by: @mbelinky
This commit is contained in:
@@ -42,6 +42,7 @@ Docs: https://docs.openclaw.ai
|
||||
- Gateway/session reset: emit the typed `before_reset` hook for gateway `/new` and `/reset`, preserving reset-hook behavior even when the previous transcript has already been archived. (#53872) thanks @VACInc
|
||||
- Plugins/commands: pass the active host `sessionKey` into plugin command contexts, and include `sessionId` when it is already available from the active session entry, so bundled and third-party commands can resolve the current conversation reliably. (#59044) Thanks @jalehman.
|
||||
- Agents/auth: honor `models.providers.*.authHeader` for pi embedded runner model requests by injecting `Authorization: Bearer <apiKey>` when requested. (#54390) Thanks @lndyzwdxhs.
|
||||
- Dreaming/cron: stop runtime cron reconciliation on ordinary user turns and only recover managed dreaming cron state during heartbeat-triggered dreaming checks, so unrelated chat traffic does not silently recreate removed jobs. (#63938) Thanks @mbelinky.
|
||||
- UI/compaction: keep the compaction indicator in a retry-pending state until the run actually finishes, so the UI does not show `Context compacted` before compaction actually finishes. (#55132) Thanks @mpz4life.
|
||||
- Cron/tool schemas: keep cron tool schemas strict-model-friendly while still preserving `failureAlert=false`, nullable `agentId`/`sessionKey`, and flattened add/update recovery for the newly exposed cron job fields. (#55043) Thanks @brunolorente.
|
||||
- BlueBubbles/config: accept `enrichGroupParticipantsFromContacts` in the core strict config schema so gateways no longer fail validation or startup when the BlueBubbles plugin writes that field. (#56889) Thanks @zqchris.
|
||||
|
||||
@@ -810,7 +810,10 @@ describe("gateway startup reconciliation", () => {
|
||||
} as OpenClawConfig;
|
||||
|
||||
const beforeAgentReply = getBeforeAgentReplyHandler(onMock);
|
||||
await beforeAgentReply({ cleanedBody: "hello" }, { trigger: "user", workspaceDir: "." });
|
||||
await beforeAgentReply(
|
||||
{ cleanedBody: constants.DREAMING_SYSTEM_EVENT_TEXT },
|
||||
{ trigger: "heartbeat", workspaceDir: "." },
|
||||
);
|
||||
|
||||
expect(harness.addCalls).toHaveLength(1);
|
||||
expect(harness.addCalls[0]?.schedule).toMatchObject({
|
||||
@@ -898,7 +901,10 @@ describe("gateway startup reconciliation", () => {
|
||||
} as OpenClawConfig;
|
||||
|
||||
const beforeAgentReply = getBeforeAgentReplyHandler(onMock);
|
||||
await beforeAgentReply({ cleanedBody: "hello" }, { trigger: "user", workspaceDir: "." });
|
||||
await beforeAgentReply(
|
||||
{ cleanedBody: constants.DREAMING_SYSTEM_EVENT_TEXT },
|
||||
{ trigger: "heartbeat", workspaceDir: "." },
|
||||
);
|
||||
|
||||
expect(startupHarness.updateCalls).toHaveLength(0);
|
||||
expect(reloadedHarness.updateCalls).toHaveLength(1);
|
||||
@@ -962,7 +968,10 @@ describe("gateway startup reconciliation", () => {
|
||||
expect(harness.jobs).toHaveLength(0);
|
||||
|
||||
const beforeAgentReply = getBeforeAgentReplyHandler(onMock);
|
||||
await beforeAgentReply({ cleanedBody: "hello" }, { trigger: "user", workspaceDir: "." });
|
||||
await beforeAgentReply(
|
||||
{ cleanedBody: constants.DREAMING_SYSTEM_EVENT_TEXT },
|
||||
{ trigger: "heartbeat", workspaceDir: "." },
|
||||
);
|
||||
|
||||
expect(harness.addCalls).toHaveLength(2);
|
||||
expect(harness.addCalls[1]?.schedule).toMatchObject({
|
||||
@@ -975,7 +984,61 @@ describe("gateway startup reconciliation", () => {
|
||||
}
|
||||
});
|
||||
|
||||
it("does not reconcile managed cron on every repeated runtime reply", async () => {
|
||||
it("does not reconcile managed cron on non-heartbeat runtime replies", async () => {
|
||||
clearInternalHooks();
|
||||
const logger = createLogger();
|
||||
const harness = createCronHarness();
|
||||
const onMock = vi.fn();
|
||||
const api = {
|
||||
config: {
|
||||
plugins: {
|
||||
entries: {
|
||||
"memory-core": {
|
||||
config: {
|
||||
dreaming: {
|
||||
enabled: true,
|
||||
frequency: "0 2 * * *",
|
||||
timezone: "UTC",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
pluginConfig: {},
|
||||
logger,
|
||||
runtime: {},
|
||||
registerHook: (event: string, handler: Parameters<typeof registerInternalHook>[1]) => {
|
||||
registerInternalHook(event, handler);
|
||||
},
|
||||
on: onMock,
|
||||
} as never;
|
||||
|
||||
try {
|
||||
registerShortTermPromotionDreaming(api);
|
||||
await triggerInternalHook(
|
||||
createInternalHookEvent("gateway", "startup", "gateway:startup", {
|
||||
cfg: api.config,
|
||||
deps: { cron: harness.cron },
|
||||
}),
|
||||
);
|
||||
|
||||
expect(harness.listCalls).toBe(1);
|
||||
|
||||
const beforeAgentReply = getBeforeAgentReplyHandler(onMock);
|
||||
await beforeAgentReply({ cleanedBody: "hello" }, { trigger: "user", workspaceDir: "." });
|
||||
await beforeAgentReply(
|
||||
{ cleanedBody: "hello again" },
|
||||
{ trigger: "user", workspaceDir: "." },
|
||||
);
|
||||
|
||||
expect(harness.listCalls).toBe(1);
|
||||
} finally {
|
||||
clearInternalHooks();
|
||||
}
|
||||
});
|
||||
|
||||
it("does not reconcile managed cron on every repeated runtime heartbeat", async () => {
|
||||
clearInternalHooks();
|
||||
const logger = createLogger();
|
||||
const harness = createCronHarness();
|
||||
@@ -1019,10 +1082,13 @@ describe("gateway startup reconciliation", () => {
|
||||
expect(harness.listCalls).toBe(1);
|
||||
|
||||
const beforeAgentReply = getBeforeAgentReplyHandler(onMock);
|
||||
await beforeAgentReply({ cleanedBody: "hello" }, { trigger: "user", workspaceDir: "." });
|
||||
await beforeAgentReply(
|
||||
{ cleanedBody: "hello again" },
|
||||
{ trigger: "user", workspaceDir: "." },
|
||||
{ cleanedBody: constants.DREAMING_SYSTEM_EVENT_TEXT },
|
||||
{ trigger: "heartbeat", workspaceDir: "." },
|
||||
);
|
||||
await beforeAgentReply(
|
||||
{ cleanedBody: constants.DREAMING_SYSTEM_EVENT_TEXT },
|
||||
{ trigger: "heartbeat", workspaceDir: "." },
|
||||
);
|
||||
|
||||
expect(harness.listCalls).toBe(2);
|
||||
|
||||
@@ -710,6 +710,9 @@ export function registerShortTermPromotionDreaming(api: OpenClawPluginApi): void
|
||||
|
||||
api.on("before_agent_reply", async (event, ctx) => {
|
||||
try {
|
||||
if (ctx.trigger !== "heartbeat") {
|
||||
return undefined;
|
||||
}
|
||||
const config = await reconcileManagedDreamingCron({
|
||||
reason: "runtime",
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user