


























@@ -974,6 +974,79 @@ describe("initSessionState RawBody", () => {
974974expect(store[sessionKey]?.modelOverrideSource).toBe("user");
975975});
976976977+it("preserves user-set behavior overrides across an implicit daily stale rollover (#92562)", async () => {
978+// Regression: session-level behavior overrides (/think, /verbose, /reasoning,
979+// /trace, ttsAuto) survive an explicit /new but were dropped after the
980+// automatic daily/idle reset, because the carryover was gated on
981+// resetTriggered while the implicit stale rollover runs with
982+// resetTriggered === false. The model override on this same path was already
983+// fixed (#90119); the behavior overrides must follow suit.
984+const root = await makeCaseDir("openclaw-daily-rollover-behavior-");
985+const storePath = path.join(root, "sessions.json");
986+const sessionKey = "agent:main:discord:channel:daily-rollover-behavior";
987+const existingSessionId = "session-before-daily-reset-behavior";
988+// Stale under the default daily reset (atHour 4): started ~48h ago.
989+const staleStartedAt = Date.now() - 48 * 60 * 60 * 1000;
990+991+await writeSessionStoreFast(storePath, {
992+[sessionKey]: {
993+sessionId: existingSessionId,
994+updatedAt: staleStartedAt,
995+sessionStartedAt: staleStartedAt,
996+lastInteractionAt: staleStartedAt,
997+systemSent: true,
998+// User-set behavior overrides (what /think, /verbose, etc. write).
999+thinkingLevel: "medium",
1000+verboseLevel: "on",
1001+traceLevel: "high",
1002+reasoningLevel: "low",
1003+ttsAuto: "always",
1004+},
1005+});
1006+1007+const cfg = {
1008+session: { store: storePath },
1009+} as OpenClawConfig;
1010+1011+const result = await initSessionState({
1012+ctx: {
1013+// Ordinary message — NOT a reset trigger.
1014+RawBody: "hello again",
1015+ChatType: "channel",
1016+SessionKey: sessionKey,
1017+},
1018+ cfg,
1019+commandAuthorized: true,
1020+});
1021+1022+// The session rolled over implicitly (stale), not via /new.
1023+expect(result.isNewSession).toBe(true);
1024+expect(result.resetTriggered).toBe(false);
1025+expect(result.sessionId).not.toBe(existingSessionId);
1026+// The user-set behavior overrides must survive the implicit rollover.
1027+expect(result.sessionEntry.thinkingLevel).toBe("medium");
1028+expect(result.sessionEntry.verboseLevel).toBe("on");
1029+expect(result.sessionEntry.traceLevel).toBe("high");
1030+expect(result.sessionEntry.reasoningLevel).toBe("low");
1031+expect(result.sessionEntry.ttsAuto).toBe("always");
1032+1033+const store = JSON.parse(await fs.readFile(storePath, "utf-8")) as Record<
1034+string,
1035+{
1036+thinkingLevel?: string;
1037+verboseLevel?: string;
1038+traceLevel?: string;
1039+reasoningLevel?: string;
1040+ttsAuto?: string;
1041+}
1042+>;
1043+expect(store[sessionKey]?.thinkingLevel).toBe("medium");
1044+expect(store[sessionKey]?.verboseLevel).toBe("on");
1045+expect(store[sessionKey]?.traceLevel).toBe("high");
1046+expect(store[sessionKey]?.reasoningLevel).toBe("low");
1047+expect(store[sessionKey]?.ttsAuto).toBe("always");
1048+});
1049+9771050it("clears an auto-fallback model override on an implicit daily stale rollover (#90119)", async () => {
9781051// Counterpart: auto-created fallback overrides must still be cleared on a
9791052// daily rollover so stale sessions return to the configured default.
@@ -1019,6 +1092,58 @@ describe("initSessionState RawBody", () => {
10191092expect(result.sessionEntry.modelOverrideSource).toBeUndefined();
10201093});
102110941095+it("preserves behavior overrides while clearing an auto-fallback model override on one implicit daily rollover (#92562)", async () => {
1096+// Documents the clear/preserve split on a single implicit rollover: a
1097+// user-set /think survives (no fallback provenance to filter) while an
1098+// auto-fallback model override is dropped back to the configured default.
1099+// The two travel separate paths (direct carry vs resolveResetPreservedSelection),
1100+// so they must not interfere.
1101+const root = await makeCaseDir("openclaw-daily-rollover-mixed-");
1102+const storePath = path.join(root, "sessions.json");
1103+const sessionKey = "agent:main:discord:channel:daily-rollover-mixed";
1104+const existingSessionId = "session-before-daily-reset-mixed";
1105+const staleStartedAt = Date.now() - 48 * 60 * 60 * 1000;
1106+1107+await writeSessionStoreFast(storePath, {
1108+[sessionKey]: {
1109+sessionId: existingSessionId,
1110+updatedAt: staleStartedAt,
1111+sessionStartedAt: staleStartedAt,
1112+lastInteractionAt: staleStartedAt,
1113+systemSent: true,
1114+// User-set behavior override (must survive).
1115+thinkingLevel: "medium",
1116+// Auto-fallback model override (must be cleared).
1117+providerOverride: "minimax",
1118+modelOverride: "m2.7",
1119+modelOverrideFallbackOriginProvider: "openai",
1120+modelOverrideFallbackOriginModel: "gpt-4o-mini",
1121+},
1122+});
1123+1124+const cfg = {
1125+session: { store: storePath },
1126+} as OpenClawConfig;
1127+1128+const result = await initSessionState({
1129+ctx: {
1130+RawBody: "hello again",
1131+ChatType: "channel",
1132+SessionKey: sessionKey,
1133+},
1134+ cfg,
1135+commandAuthorized: true,
1136+});
1137+1138+expect(result.isNewSession).toBe(true);
1139+expect(result.resetTriggered).toBe(false);
1140+// Behavior override preserved...
1141+expect(result.sessionEntry.thinkingLevel).toBe("medium");
1142+// ...while the auto-fallback model override is cleared.
1143+expect(result.sessionEntry.modelOverride).toBeUndefined();
1144+expect(result.sessionEntry.providerOverride).toBeUndefined();
1145+});
1146+10221147it("rotates local session state for /new on bound ACP sessions", async () => {
10231148const root = await makeCaseDir("openclaw-rawbody-acp-reset-");
10241149const storePath = path.join(root, "sessions.json");
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。