diff --git a/CHANGELOG.md b/CHANGELOG.md index 2a581edb9..ff1cb2aae 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -40,6 +40,7 @@ Docs: https://docs.openclaw.ai - Exec approvals/Forwarding: restore Discord text forwarding when component approvals are not configured, and carry request snapshots through resolve events so resolved notices still forward after cache misses/restarts. (#22988) Thanks @bubmiller. - Security/Elevated: match `tools.elevated.allowFrom` against sender identities only (not recipient `ctx.To`), closing a recipient-token bypass for `/elevated` authorization. (#11022) Thanks @coygeek. - Webchat/Sessions: preserve external session routing metadata when internal `chat.send` turns run under `webchat`, so explicit channel-keyed sessions (for example Telegram) no longer get rewritten to `webchat` and misroute follow-up delivery. (#23258) Thanks @binary64. +- Webchat/Sessions: preserve existing session `label` across `/new` and `/reset` rollovers so reset sessions remain discoverable in session history lists. (#23755) Thanks @ThunderStormer. - Config/Memory: allow `"mistral"` in `agents.defaults.memorySearch.provider` and `agents.defaults.memorySearch.fallback` schema validation. (#14934) Thanks @ThomsenDrake. - Security/Feishu: enforce ID-only allowlist matching for DM/group sender authorization, normalize Feishu ID prefixes during checks, and ignore mutable display names so display-name collisions cannot satisfy allowlist entries. This ships in the next npm release. Thanks @jiseoung for reporting. - Security/Group policy: harden `channels.*.groups.*.toolsBySender` matching by requiring explicit sender-key types (`id:`, `e164:`, `username:`, `name:`), preventing cross-identifier collisions across mutable/display-name fields while keeping legacy untyped keys on a deprecated ID-only path. This ships in the next npm release. Thanks @jiseoung for reporting. diff --git a/src/auto-reply/reply/session.test.ts b/src/auto-reply/reply/session.test.ts index d73f2b1ad..bbba0bed8 100644 --- a/src/auto-reply/reply/session.test.ts +++ b/src/auto-reply/reply/session.test.ts @@ -996,6 +996,42 @@ describe("initSessionState preserves behavior overrides across /new and /reset", expect(result.sessionEntry.reasoningLevel).toBe("low"); }); + it("/new preserves session label from previous session", async () => { + const storePath = await createStorePath("openclaw-reset-label-"); + const sessionKey = "agent:main:telegram:dm:user-label"; + const existingSessionId = "existing-session-label"; + await seedSessionStoreWithOverrides({ + storePath, + sessionKey, + sessionId: existingSessionId, + overrides: { label: "telegram-priority" }, + }); + + const cfg = { + session: { store: storePath, idleMinutes: 999 }, + } as OpenClawConfig; + + const result = await initSessionState({ + ctx: { + Body: "/new", + RawBody: "/new", + CommandBody: "/new", + From: "user-label", + To: "bot", + ChatType: "direct", + SessionKey: sessionKey, + Provider: "telegram", + Surface: "telegram", + }, + cfg, + commandAuthorized: true, + }); + + expect(result.isNewSession).toBe(true); + expect(result.resetTriggered).toBe(true); + expect(result.sessionEntry.label).toBe("telegram-priority"); + }); + it("/new in a new session does not preserve overrides", async () => { const storePath = await createStorePath("openclaw-new-no-preserve-"); const sessionKey = "agent:main:telegram:dm:user3"; diff --git a/src/auto-reply/reply/session.ts b/src/auto-reply/reply/session.ts index d95cfa825..2f34f6689 100644 --- a/src/auto-reply/reply/session.ts +++ b/src/auto-reply/reply/session.ts @@ -168,6 +168,7 @@ export async function initSessionState(params: { let persistedTtsAuto: TtsAutoMode | undefined; let persistedModelOverride: string | undefined; let persistedProviderOverride: string | undefined; + let persistedLabel: string | undefined; const normalizedChatType = normalizeChatType(ctx.ChatType); const isGroup = @@ -265,6 +266,7 @@ export async function initSessionState(params: { persistedTtsAuto = entry.ttsAuto; persistedModelOverride = entry.modelOverride; persistedProviderOverride = entry.providerOverride; + persistedLabel = entry.label; } else { sessionId = crypto.randomUUID(); isNewSession = true; @@ -280,6 +282,7 @@ export async function initSessionState(params: { persistedTtsAuto = entry.ttsAuto; persistedModelOverride = entry.modelOverride; persistedProviderOverride = entry.providerOverride; + persistedLabel = entry.label; } } @@ -339,6 +342,7 @@ export async function initSessionState(params: { responseUsage: baseEntry?.responseUsage, modelOverride: persistedModelOverride ?? baseEntry?.modelOverride, providerOverride: persistedProviderOverride ?? baseEntry?.providerOverride, + label: persistedLabel ?? baseEntry?.label, sendPolicy: baseEntry?.sendPolicy, queueMode: baseEntry?.queueMode, queueDebounceMs: baseEntry?.queueDebounceMs,