diff --git a/CHANGELOG.md b/CHANGELOG.md index 5e25b4f77f..6353fd1e15 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -71,6 +71,7 @@ Docs: https://docs.openclaw.ai - CLI/Doctor: auto-repair `dmPolicy="open"` configs missing wildcard allowlists and write channel-correct repair paths (including `channels.googlechat.dm.allowFrom`) so `openclaw doctor --fix` no longer leaves Google Chat configs invalid after attempted repair. (#18544) - CLI/Doctor: detect gateway service token drift when the gateway token is only provided via environment variables, keeping service repairs aligned after token rotation. - Gateway/Update: prevent restart crash loops after failed self-updates by restarting only on successful updates, stopping early on failed install/build steps, and running `openclaw doctor --fix` during updates to sanitize config. (#18131) Thanks @RamiNoodle733. +- Gateway/Update: preserve update.run restart delivery context so post-update status replies route back to the initiating channel/thread. (#18267) Thanks @yinghaosang. - CLI/Update: run a standalone restart helper after updates, honoring service-name overrides and reporting restart initiation separately from confirmed restarts. (#18050) - CLI/Daemon: warn when a gateway restart sees a stale service token so users can reinstall with `openclaw gateway install --force`, and skip drift warnings for non-gateway service restarts. (#18018) - CLI/Status: fix `openclaw status --all` token summaries for bot-token-only channels so Mattermost/Zalo no longer show a bot+app warning. (#18527) Thanks @echo931. diff --git a/src/config/sessions/delivery-info.test.ts b/src/config/sessions/delivery-info.test.ts new file mode 100644 index 0000000000..e2c6897d87 --- /dev/null +++ b/src/config/sessions/delivery-info.test.ts @@ -0,0 +1,94 @@ +import { beforeEach, describe, expect, it, vi } from "vitest"; +import type { SessionEntry } from "./types.js"; + +const storeState = vi.hoisted(() => ({ + store: {} as Record, +})); + +vi.mock("../io.js", () => ({ + loadConfig: () => ({}), +})); + +vi.mock("./paths.js", () => ({ + resolveStorePath: () => "/tmp/sessions.json", +})); + +vi.mock("./store.js", () => ({ + loadSessionStore: () => storeState.store, +})); + +import { extractDeliveryInfo } from "./delivery-info.js"; + +const buildEntry = (deliveryContext: SessionEntry["deliveryContext"]): SessionEntry => ({ + sessionId: "session-1", + updatedAt: Date.now(), + deliveryContext, +}); + +beforeEach(() => { + storeState.store = {}; +}); + +describe("extractDeliveryInfo", () => { + it("returns deliveryContext for direct session keys", () => { + const sessionKey = "agent:main:webchat:dm:user-123"; + storeState.store[sessionKey] = buildEntry({ + channel: "webchat", + to: "webchat:user-123", + accountId: "default", + }); + + const result = extractDeliveryInfo(sessionKey); + + expect(result).toEqual({ + deliveryContext: { + channel: "webchat", + to: "webchat:user-123", + accountId: "default", + }, + threadId: undefined, + }); + }); + + it("falls back to base sessions for :thread: keys", () => { + const baseKey = "agent:main:slack:channel:C0123ABC"; + const threadKey = `${baseKey}:thread:1234567890.123456`; + storeState.store[baseKey] = buildEntry({ + channel: "slack", + to: "slack:C0123ABC", + accountId: "workspace-1", + }); + + const result = extractDeliveryInfo(threadKey); + + expect(result).toEqual({ + deliveryContext: { + channel: "slack", + to: "slack:C0123ABC", + accountId: "workspace-1", + }, + threadId: "1234567890.123456", + }); + }); + + it("falls back to base sessions for :topic: keys", () => { + const baseKey = "agent:main:telegram:group:98765"; + const topicKey = `${baseKey}:topic:55`; + storeState.store[baseKey] = buildEntry({ + channel: "telegram", + to: "group:98765", + accountId: "main", + }); + + const result = extractDeliveryInfo(topicKey); + + expect(result).toEqual({ + deliveryContext: { + channel: "telegram", + to: "group:98765", + accountId: "main", + }, + threadId: "55", + }); + }); +});