mirror of
https://github.com/openclaw/openclaw.git
synced 2026-04-11 01:01:13 +00:00
fix: preserve session_end reasons
This commit is contained in:
@@ -31,6 +31,7 @@ Docs: https://docs.openclaw.ai
|
||||
- Plugins/runtime: reuse compatible active registries for `web_search` and `web_fetch` provider snapshot resolution so repeated runtime reads do not re-import the same bundled plugin set on each agent message. Related #48380.
|
||||
- Infra/tailscale: ignore `OPENCLAW_TEST_TAILSCALE_BINARY` outside explicit test environments and block it from workspace `.env`, so test-only binary overrides cannot be injected through trusted repository state. (#58468) Thanks @eleqtrizit.
|
||||
- Agents/tool policy: preserve restrictive plugin-only allowlists instead of silently widening access to core tools, and keep allowlist warnings aligned with the enforced policy. (#58476) Thanks @eleqtrizit.
|
||||
- Hooks/session_end: preserve deterministic reason metadata for custom reset aliases and overlapping idle-plus-daily rollovers so plugins can rely on lifecycle reason reporting. (#59715) Thanks @jalehman.
|
||||
|
||||
## 2026.4.2
|
||||
|
||||
|
||||
@@ -150,6 +150,34 @@ describe("session hook context wiring", () => {
|
||||
expect(event).toMatchObject({ reason: "reset" });
|
||||
});
|
||||
|
||||
it("maps custom reset trigger aliases to the new-session reason", async () => {
|
||||
const sessionKey = "agent:main:telegram:direct:alias";
|
||||
const storePath = await createStorePath("openclaw-session-hook-reset-alias");
|
||||
const transcriptPath = await writeTranscript(storePath, "alias-session", "alias me");
|
||||
await writeStore(storePath, {
|
||||
[sessionKey]: {
|
||||
sessionId: "alias-session",
|
||||
sessionFile: transcriptPath,
|
||||
updatedAt: Date.now(),
|
||||
},
|
||||
});
|
||||
const cfg = {
|
||||
session: {
|
||||
store: storePath,
|
||||
resetTriggers: ["/fresh"],
|
||||
},
|
||||
} as OpenClawConfig;
|
||||
|
||||
await initSessionState({
|
||||
ctx: { Body: "/fresh", SessionKey: sessionKey },
|
||||
cfg,
|
||||
commandAuthorized: true,
|
||||
});
|
||||
|
||||
const [event] = hookRunnerMocks.runSessionEnd.mock.calls[0] ?? [];
|
||||
expect(event).toMatchObject({ reason: "new" });
|
||||
});
|
||||
|
||||
it("marks daily stale rollovers and exposes the archived transcript path", async () => {
|
||||
vi.useFakeTimers();
|
||||
try {
|
||||
@@ -221,4 +249,42 @@ describe("session hook context wiring", () => {
|
||||
vi.useRealTimers();
|
||||
}
|
||||
});
|
||||
|
||||
it("prefers idle over daily when both rollover conditions are true", async () => {
|
||||
vi.useFakeTimers();
|
||||
try {
|
||||
vi.setSystemTime(new Date(2026, 0, 18, 5, 30, 0));
|
||||
const sessionKey = "agent:main:telegram:direct:overlap";
|
||||
const storePath = await createStorePath("openclaw-session-hook-overlap");
|
||||
const transcriptPath = await writeTranscript(storePath, "overlap-session", "overlap");
|
||||
await writeStore(storePath, {
|
||||
[sessionKey]: {
|
||||
sessionId: "overlap-session",
|
||||
sessionFile: transcriptPath,
|
||||
updatedAt: new Date(2026, 0, 18, 4, 45, 0).getTime(),
|
||||
},
|
||||
});
|
||||
const cfg = {
|
||||
session: {
|
||||
store: storePath,
|
||||
reset: {
|
||||
mode: "daily",
|
||||
atHour: 4,
|
||||
idleMinutes: 30,
|
||||
},
|
||||
},
|
||||
} as OpenClawConfig;
|
||||
|
||||
await initSessionState({
|
||||
ctx: { Body: "hello", SessionKey: sessionKey },
|
||||
cfg,
|
||||
commandAuthorized: true,
|
||||
});
|
||||
|
||||
const [event] = hookRunnerMocks.runSessionEnd.mock.calls[0] ?? [];
|
||||
expect(event).toMatchObject({ reason: "idle" });
|
||||
} finally {
|
||||
vi.useRealTimers();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
@@ -63,14 +63,7 @@ function loadSessionArchiveRuntime() {
|
||||
function resolveExplicitSessionEndReason(
|
||||
matchedResetTriggerLower?: string,
|
||||
): PluginHookSessionEndReason {
|
||||
switch (matchedResetTriggerLower) {
|
||||
case "/new":
|
||||
return "new";
|
||||
case "/reset":
|
||||
return "reset";
|
||||
default:
|
||||
return "unknown";
|
||||
}
|
||||
return matchedResetTriggerLower === "/reset" ? "reset" : "new";
|
||||
}
|
||||
|
||||
function resolveStaleSessionEndReason(params: {
|
||||
@@ -85,10 +78,13 @@ function resolveStaleSessionEndReason(params: {
|
||||
params.freshness.dailyResetAt != null && params.entry.updatedAt < params.freshness.dailyResetAt;
|
||||
const staleIdle =
|
||||
params.freshness.idleExpiresAt != null && params.now > params.freshness.idleExpiresAt;
|
||||
if (staleDaily === staleIdle) {
|
||||
return staleDaily ? "unknown" : undefined;
|
||||
if (staleIdle) {
|
||||
return "idle";
|
||||
}
|
||||
return staleDaily ? "daily" : "idle";
|
||||
if (staleDaily) {
|
||||
return "daily";
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
export type SessionInitResult = {
|
||||
|
||||
Reference in New Issue
Block a user