






















@@ -15,7 +15,11 @@ import {
1515getSessionBindingService,
1616registerSessionBindingAdapter,
1717} from "../../infra/outbound/session-binding-service.js";
18-import { enqueueSystemEvent, resetSystemEventsForTest } from "../../infra/system-events.js";
18+import {
19+enqueueSystemEvent,
20+peekSystemEvents,
21+resetSystemEventsForTest,
22+} from "../../infra/system-events.js";
1923import { resetPluginRuntimeStateForTest, setActivePluginRegistry } from "../../plugins/runtime.js";
2024import {
2125createChannelTestPluginBase,
@@ -292,6 +296,7 @@ beforeEach(() => {
292296});
293297});
294298afterEach(async () => {
299+resetSystemEventsForTest();
295300await sessionMcpTesting.resetSessionMcpRuntimeManager();
296301});
297302describe("initSessionState thread forking", () => {
@@ -787,6 +792,53 @@ describe("initSessionState RawBody", () => {
787792expect(result.triggerBodyNormalized).toBe("/NEW KeepThisCase");
788793});
789794795+it("drains stale system events when /new rotates an existing session", async () => {
796+const root = await makeCaseDir("openclaw-rawbody-reset-system-events-");
797+const storePath = path.join(root, "sessions.json");
798+const sessionKey = "agent:main:whatsapp:dm:system-events";
799+const existingSessionId = "session-with-stale-events";
800+801+await writeSessionStoreFast(storePath, {
802+[sessionKey]: {
803+sessionId: existingSessionId,
804+updatedAt: Date.now(),
805+systemSent: true,
806+},
807+});
808+enqueueSystemEvent("stale session-key event", { sessionKey });
809+enqueueSystemEvent("stale session-id event", { sessionKey: existingSessionId });
810+811+const cfg = {
812+session: {
813+store: storePath,
814+resetTriggers: ["/new"],
815+},
816+} as OpenClawConfig;
817+818+const result = await initSessionState({
819+ctx: {
820+RawBody: "/new continue",
821+ChatType: "direct",
822+SessionKey: sessionKey,
823+},
824+ cfg,
825+commandAuthorized: true,
826+});
827+828+expect(result.isNewSession).toBe(true);
829+expect(result.resetTriggered).toBe(true);
830+expect(result.sessionId).not.toBe(existingSessionId);
831+await expect(
832+drainFormattedSystemEvents({
833+ cfg,
834+ sessionKey,
835+isMainSession: false,
836+isNewSession: true,
837+}),
838+).resolves.toBeUndefined();
839+expect(peekSystemEvents(existingSessionId)).toEqual([]);
840+});
841+790842it("rotates local session state for /new on bound ACP sessions", async () => {
791843const root = await makeCaseDir("openclaw-rawbody-acp-reset-");
792844const storePath = path.join(root, "sessions.json");
@@ -1334,6 +1386,10 @@ describe("initSessionState reset policy", () => {
13341386updatedAt: new Date(2026, 0, 18, 3, 0, 0).getTime(),
13351387},
13361388});
1389+enqueueSystemEvent("stale daily rollover event", { sessionKey });
1390+enqueueSystemEvent("stale daily rollover session-id event", {
1391+sessionKey: existingSessionId,
1392+});
1337139313381394const cfg = { session: { store: storePath } } as OpenClawConfig;
13391395const result = await initSessionState({
@@ -1348,6 +1404,15 @@ describe("initSessionState reset policy", () => {
13481404 sessionKey,
13491405previousSessionId: existingSessionId,
13501406});
1407+await expect(
1408+drainFormattedSystemEvents({
1409+ cfg,
1410+ sessionKey,
1411+isMainSession: false,
1412+isNewSession: true,
1413+}),
1414+).resolves.toBeUndefined();
1415+expect(peekSystemEvents(existingSessionId)).toEqual([]);
13511416});
1352141713531418it("treats sessions as stale before the daily reset when updated before yesterday's boundary", async () => {
@@ -1405,6 +1470,50 @@ describe("initSessionState reset policy", () => {
14051470expect(result.sessionId).not.toBe(existingSessionId);
14061471});
140714721473+it("drains stale system events when idle rollover creates a new session", async () => {
1474+vi.setSystemTime(new Date(2026, 0, 18, 5, 30, 0));
1475+const root = await makeCaseDir("openclaw-reset-idle-system-events-");
1476+const storePath = path.join(root, "sessions.json");
1477+const sessionKey = "agent:main:whatsapp:dm:idle-system-events";
1478+const existingSessionId = "idle-system-events-session";
1479+1480+await writeSessionStoreFast(storePath, {
1481+[sessionKey]: {
1482+sessionId: existingSessionId,
1483+updatedAt: new Date(2026, 0, 18, 4, 45, 0).getTime(),
1484+},
1485+});
1486+enqueueSystemEvent("stale idle rollover event", { sessionKey });
1487+enqueueSystemEvent("stale idle rollover session-id event", {
1488+sessionKey: existingSessionId,
1489+});
1490+1491+const cfg = {
1492+session: {
1493+store: storePath,
1494+reset: { mode: "idle", idleMinutes: 30 },
1495+},
1496+} as OpenClawConfig;
1497+const result = await initSessionState({
1498+ctx: { Body: "hello", SessionKey: sessionKey },
1499+ cfg,
1500+commandAuthorized: true,
1501+});
1502+1503+expect(result.isNewSession).toBe(true);
1504+expect(result.resetTriggered).toBe(false);
1505+expect(result.sessionId).not.toBe(existingSessionId);
1506+await expect(
1507+drainFormattedSystemEvents({
1508+ cfg,
1509+ sessionKey,
1510+isMainSession: false,
1511+isNewSession: true,
1512+}),
1513+).resolves.toBeUndefined();
1514+expect(peekSystemEvents(existingSessionId)).toEqual([]);
1515+});
1516+14081517it("keeps the existing stale session for /reset soft", async () => {
14091518vi.setSystemTime(new Date(2026, 0, 18, 5, 30, 0));
14101519const root = await makeCaseDir("openclaw-reset-soft-stale-");
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。