diff --git a/src/auto-reply/reply/strip-inbound-meta.ts b/src/auto-reply/reply/strip-inbound-meta.ts index aac05f85df9..e4db1246c57 100644 --- a/src/auto-reply/reply/strip-inbound-meta.ts +++ b/src/auto-reply/reply/strip-inbound-meta.ts @@ -12,9 +12,6 @@ * do not show AI-facing envelope metadata as user text. */ -import { z } from "zod"; -import { safeParseJsonWithSchema } from "../../utils/zod-parse.js"; - const LEADING_TIMESTAMP_PREFIX_RE = /^\[[A-Za-z]{3} \d{4}-\d{2}-\d{2} \d{2}:\d{2}[^\]]*\] */; /** @@ -35,7 +32,6 @@ const UNTRUSTED_CONTEXT_HEADER = const ACTIVE_MEMORY_OPEN_TAG = ""; const ACTIVE_MEMORY_CLOSE_TAG = ""; const [CONVERSATION_INFO_SENTINEL, SENDER_INFO_SENTINEL] = INBOUND_META_SENTINELS; -const InboundMetaBlockSchema = z.record(z.string(), z.unknown()); // Pre-compiled fast-path regex — avoids line-by-line parse when no blocks present. const SENTINEL_FAST_RE = new RegExp( @@ -64,6 +60,18 @@ function restoreNeutralizedMarkdownFences(value: unknown): unknown { ); } +function parseJsonObjectRecord(jsonText: string): Record | null { + try { + const parsed: unknown = JSON.parse(jsonText); + if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) { + return null; + } + return parsed as Record; + } catch { + return null; + } +} + function parseInboundMetaBlock(lines: string[], sentinel: string): Record | null { for (let i = 0; i < lines.length; i++) { if (lines[i]?.trim() !== sentinel) { @@ -86,7 +94,7 @@ function parseInboundMetaBlock(lines: string[], sentinel: string): Record) : null; } return null; diff --git a/src/entry.version-fast-path.test.ts b/src/entry.version-fast-path.test.ts index 673a0aef751..0edf58527ba 100644 --- a/src/entry.version-fast-path.test.ts +++ b/src/entry.version-fast-path.test.ts @@ -79,6 +79,12 @@ async function importEntry(scope: string) { ); } +async function flushEntrySideEffects() { + await Promise.resolve(); + await Promise.resolve(); + await new Promise((resolve) => setTimeout(resolve, 0)); +} + describe("entry root version fast path", () => { let originalArgv: string[]; let originalGatewayToken: string | undefined; @@ -109,13 +115,9 @@ describe("entry root version fast path", () => { const logSpy = vi.spyOn(console, "log").mockImplementation(() => {}); await importEntry("commit-tagged"); - await vi.waitFor( - () => { - expect(logSpy).toHaveBeenCalledWith("OpenClaw 9.9.9-test (abc1234)"); - expect(exitSpy).toHaveBeenCalledWith(0); - }, - { interval: 1 }, - ); + await flushEntrySideEffects(); + expect(logSpy).toHaveBeenCalledWith("OpenClaw 9.9.9-test (abc1234)"); + expect(exitSpy).toHaveBeenCalledWith(0); logSpy.mockRestore(); }); @@ -125,13 +127,9 @@ describe("entry root version fast path", () => { const logSpy = vi.spyOn(console, "log").mockImplementation(() => {}); await importEntry("plain-version"); - await vi.waitFor( - () => { - expect(logSpy).toHaveBeenCalledWith("OpenClaw 9.9.9-test"); - expect(exitSpy).toHaveBeenCalledWith(0); - }, - { interval: 1 }, - ); + await flushEntrySideEffects(); + expect(logSpy).toHaveBeenCalledWith("OpenClaw 9.9.9-test"); + expect(exitSpy).toHaveBeenCalledWith(0); logSpy.mockRestore(); }); @@ -141,12 +139,8 @@ describe("entry root version fast path", () => { const logSpy = vi.spyOn(console, "log").mockImplementation(() => {}); await importEntry("container-target"); - await vi.waitFor( - () => { - expect(runCliMock).toHaveBeenCalledWith(["node", "openclaw", "--version"]); - }, - { interval: 1 }, - ); + await flushEntrySideEffects(); + expect(runCliMock).toHaveBeenCalledWith(["node", "openclaw", "--version"]); expect(logSpy).not.toHaveBeenCalled(); expect(exitSpy).not.toHaveBeenCalled(); @@ -159,12 +153,8 @@ describe("entry root version fast path", () => { const errorSpy = vi.spyOn(console, "error").mockImplementation(() => {}); await importEntry("gateway-override"); - await vi.waitFor( - () => { - expect(runCliMock).toHaveBeenCalledWith(["node", "openclaw", "--version"]); - }, - { interval: 1 }, - ); + await flushEntrySideEffects(); + expect(runCliMock).toHaveBeenCalledWith(["node", "openclaw", "--version"]); expect(errorSpy).not.toHaveBeenCalled(); expect(exitSpy).not.toHaveBeenCalled(); diff --git a/ui/src/ui/chat/context-notice.test.ts b/ui/src/ui/chat/context-notice.test.ts index 48381082d1c..20ae5a9e14c 100644 --- a/ui/src/ui/chat/context-notice.test.ts +++ b/ui/src/ui/chat/context-notice.test.ts @@ -17,15 +17,16 @@ import { renderSideResult } from "./side-result-render.ts"; describe("context notice", () => { afterEach(() => { - document.documentElement.style.removeProperty("--warn"); - document.documentElement.style.removeProperty("--danger"); + vi.restoreAllMocks(); resetContextNoticeThemeCacheForTest(); }); it("renders only for fresh high current usage", () => { const container = document.createElement("div"); - document.documentElement.style.setProperty("--warn", "rgb(1, 2, 3)"); - document.documentElement.style.setProperty("--danger", "tomato"); + vi.spyOn(window, "getComputedStyle").mockReturnValue({ + getPropertyValue: (name: string) => + name === "--warn" ? "#010203" : name === "--danger" ? "#040506" : "", + } as CSSStyleDeclaration); resetContextNoticeThemeCacheForTest(); expect( @@ -58,6 +59,7 @@ describe("context notice", () => { const notice = container.querySelector(".context-notice"); expect(notice).not.toBeNull(); expect(notice?.style.getPropertyValue("--ctx-color")).toContain("rgb("); + expect(notice?.style.getPropertyValue("--ctx-color")).toContain("4, 5, 6"); expect(notice?.style.getPropertyValue("--ctx-color")).not.toContain("NaN"); expect(notice?.style.getPropertyValue("--ctx-bg")).not.toContain("NaN"); diff --git a/ui/src/ui/views/chat.test.ts b/ui/src/ui/views/chat.test.ts index c8416e30245..9cd466b46d6 100644 --- a/ui/src/ui/views/chat.test.ts +++ b/ui/src/ui/views/chat.test.ts @@ -12,6 +12,10 @@ import type { SessionsListResult } from "../types.ts"; import type { MessageGroup } from "../types/chat-types.ts"; import { renderChat, type ChatProps } from "./chat.ts"; +vi.mock("../markdown.ts", () => ({ + toSanitizedMarkdownHtml: (value: string) => value, +})); + vi.mock("./markdown-sidebar.ts", async () => { const { html } = await import("lit"); return {