From 25548af7490366b2507a2695e52b10d61af3a0ca Mon Sep 17 00:00:00 2001 From: Ammar Date: Mon, 20 Jul 2026 21:21:23 -0500 Subject: [PATCH 01/13] =?UTF-8?q?=F0=9F=A4=96=20feat:=20present=20subagent?= =?UTF-8?q?=20reports=20in=20chat?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Render trusted synthetic subagent report envelopes as readable transcript cards with semantic progress state, markdown, structured-output disclosure, and mobile-safe layout. Add behavioral coverage plus full-app desktop and pinned-phone Storybook stories. _Generated with `mux` • Model: `openai:gpt-5.6-sol` • Thinking: `xhigh` • Cost: `$124.37`_ --- .../Messages/MessageRenderer.test.tsx | 122 +++++++++++++++ .../Messages/SubagentReportMessageContent.tsx | 146 ++++++++++++++++++ src/browser/features/Messages/UserMessage.tsx | 36 ++++- .../features/Tools/AgentReportToolCall.tsx | 2 +- .../stories/App.subagentReports.stories.tsx | 125 +++++++++++++++ src/browser/stories/mocks/messages.ts | 43 ++++++ 6 files changed, 471 insertions(+), 3 deletions(-) create mode 100644 src/browser/features/Messages/SubagentReportMessageContent.tsx create mode 100644 src/browser/stories/App.subagentReports.stories.tsx diff --git a/src/browser/features/Messages/MessageRenderer.test.tsx b/src/browser/features/Messages/MessageRenderer.test.tsx index 8a90675163..fed0ff728e 100644 --- a/src/browser/features/Messages/MessageRenderer.test.tsx +++ b/src/browser/features/Messages/MessageRenderer.test.tsx @@ -222,6 +222,128 @@ Live goal accounting at limit: }); }); +describe("MessageRenderer subagent report rows", () => { + beforeEach(() => { + globalThis.window = new GlobalWindow() as unknown as Window & typeof globalThis; + globalThis.document = globalThis.window.document; + globalThis.localStorage = globalThis.window.localStorage; + }); + + afterEach(() => { + cleanup(); + + globalThis.window = undefined as unknown as Window & typeof globalThis; + globalThis.document = undefined as unknown as Document; + globalThis.localStorage = undefined as unknown as Storage; + }); + + function createReportMessage(content: string, synthetic = true): DisplayedMessage { + return { + type: "user", + id: "subagent-report", + historyId: "subagent-report", + content, + historySequence: 25, + isSynthetic: synthetic, + }; + } + + test("presents incremental synthetic reports without exposing the protocol envelope", () => { + const message = createReportMessage(` +task-123 +explore +in_progress +Rendering path traced + +Found the **message renderer** and its responsive story coverage. + +`); + + const { getByText, queryByText } = render( + + + + ); + + expect(getByText("subagent update")).toBeDefined(); + expect(getByText("Rendering path traced")).toBeDefined(); + expect(getByText("In progress")).toBeDefined(); + expect(getByText(/message renderer/)).toBeDefined(); + expect(queryByText("auto")).toBeNull(); + expect(queryByText(/mux_subagent_report/)).toBeNull(); + }); + + test("treats legacy reports as completed and reveals structured output on demand", () => { + const message = createReportMessage(` +task-legacy +review +Review complete + +The responsive presentation is ready. + + +\`\`\`json +{"mobileVerified":true,"risk":"low"} +\`\`\` + +`); + + const { getByRole, getByText, queryByText } = render( + + + + ); + + expect(getByText("subagent report")).toBeDefined(); + expect(getByText("Completed")).toBeDefined(); + const toggle = getByRole("button", { name: "Structured output" }); + expect(toggle.getAttribute("aria-expanded")).toBe("false"); + expect(queryByText(/mobileVerified/)).toBeNull(); + + fireEvent.click(toggle); + + expect(toggle.getAttribute("aria-expanded")).toBe("true"); + expect(getByText(/"mobileVerified": true/)).toBeDefined(); + }); + + test("keeps malformed and user-authored lookalikes on the ordinary message path", () => { + const malformed = createReportMessage(` +task-malformed +explore +in_progress +`); + const userAuthored = createReportMessage( + ` +task-user +explore +in_progress +User text + +This was typed by a user. + +`, + false + ); + + const malformedView = render( + + + + ); + expect(malformedView.getByText("auto")).toBeDefined(); + expect(malformedView.queryByText("subagent update")).toBeNull(); + malformedView.unmount(); + + const userView = render( + + + + ); + expect(userView.queryByText("subagent update")).toBeNull(); + expect(userView.queryByText("subagent report")).toBeNull(); + }); +}); + describe("MessageRenderer bash monitor wake rows", () => { beforeEach(() => { globalThis.window = new GlobalWindow() as unknown as Window & typeof globalThis; diff --git a/src/browser/features/Messages/SubagentReportMessageContent.tsx b/src/browser/features/Messages/SubagentReportMessageContent.tsx new file mode 100644 index 0000000000..6d43a1b4cb --- /dev/null +++ b/src/browser/features/Messages/SubagentReportMessageContent.tsx @@ -0,0 +1,146 @@ +import { useState, type ReactElement } from "react"; +import { Bot, Braces, ChevronRight, CircleCheck, Radio } from "lucide-react"; + +import { cn } from "@/common/lib/utils"; +import { MarkdownRenderer } from "./MarkdownRenderer"; + +export interface SubagentReportEnvelope { + taskId: string; + agentType: string; + status: "in_progress" | "completed"; + title: string; + reportMarkdown: string; + structuredOutputJson?: string; +} + +function extractLine(envelope: string, tag: string): string | null { + const match = new RegExp(`^<${tag}>([^\\n]*)<\\/${tag}>$`, "m").exec(envelope); + const value = match?.[1]?.trim(); + if (!value) return null; + return value; +} + +function extractBlock(envelope: string, tag: string): string | null { + const match = new RegExp(`(?:^|\\n)<${tag}>\\n([\\s\\S]*?)\\n<\\/${tag}>(?:\\n|$)`).exec( + envelope + ); + return match?.[1]?.trim() ?? null; +} + +function normalizeStructuredOutput(block: string): string { + const fenced = /^```json\s*\n([\s\S]*?)\n```$/.exec(block.trim()); + const json = fenced?.[1]?.trim() ?? block.trim(); + + try { + return JSON.stringify(JSON.parse(json), null, 2); + } catch { + // Preserve malformed or forward-compatible payloads instead of hiding report data. + return json; + } +} + +/** + * Parse only the exact synthetic protocol envelope emitted by TaskService. Returning null keeps + * malformed or user-authored lookalikes on the normal escaped user-message rendering path. + */ +export function parseSubagentReportEnvelope(content: string): SubagentReportEnvelope | null { + const root = /^\n([\s\S]*?)\n<\/mux_subagent_report>$/.exec(content); + if (!root) return null; + + const envelope = root[1]; + const taskId = extractLine(envelope, "task_id"); + const agentType = extractLine(envelope, "agent_type"); + const title = extractLine(envelope, "title"); + const reportMarkdown = extractBlock(envelope, "report_markdown"); + const rawStatus = extractLine(envelope, "status"); + + if (!taskId || !agentType || !title || !reportMarkdown) return null; + if (rawStatus !== null && rawStatus !== "in_progress" && rawStatus !== "completed") return null; + + const structuredOutput = extractBlock(envelope, "structured_output_json"); + return { + taskId, + agentType, + // Reports persisted before incremental updates existed had no explicit status. + status: rawStatus ?? "completed", + title, + reportMarkdown, + ...(structuredOutput + ? { structuredOutputJson: normalizeStructuredOutput(structuredOutput) } + : {}), + }; +} + +interface SubagentReportMessageContentProps { + report: SubagentReportEnvelope; +} + +/** + * Present trusted sub-agent findings as transcript content rather than exposing the model-facing + * XML protocol. The existing user bubble owns the border and surface, avoiding nested card chrome. + */ +export function SubagentReportMessageContent( + props: SubagentReportMessageContentProps +): ReactElement { + const [structuredOutputExpanded, setStructuredOutputExpanded] = useState(false); + const isInProgress = props.report.status === "in_progress"; + const StatusIcon = isInProgress ? Radio : CircleCheck; + const statusLabel = isInProgress ? "In progress" : "Completed"; + + return ( +
+
+
+ + + + {props.report.structuredOutputJson && ( +
+ + {structuredOutputExpanded && ( +
+              {props.report.structuredOutputJson}
+            
+ )} +
+ )} +
+ ); +} diff --git a/src/browser/features/Messages/UserMessage.tsx b/src/browser/features/Messages/UserMessage.tsx index c52af203c1..01248010bf 100644 --- a/src/browser/features/Messages/UserMessage.tsx +++ b/src/browser/features/Messages/UserMessage.tsx @@ -12,6 +12,10 @@ import { MessageWindow } from "./MessageWindow"; import { UserMessageContent } from "./UserMessageContent"; import { GoalSyntheticMessageContent } from "./GoalSyntheticMessageContent"; import { BashMonitorWakeMessageContent } from "./BashMonitorWakeMessageContent"; +import { + parseSubagentReportEnvelope, + SubagentReportMessageContent, +} from "./SubagentReportMessageContent"; import { TerminalOutput } from "./TerminalOutput"; import { formatKeybind, KEYBINDS } from "@/browser/utils/ui/keybinds"; import { useCopyToClipboard } from "@/browser/hooks/useCopyToClipboard"; @@ -24,6 +28,7 @@ import { import { usePersistedState } from "@/browser/hooks/usePersistedState"; import { VIM_ENABLED_KEY } from "@/common/constants/storage"; import { + Bot, ChevronLeft, ChevronRight, Clipboard, @@ -93,6 +98,17 @@ export const UserMessage: React.FC = ({ const bashMonitorWake = message.bashMonitorWake; const content = message.content; const visibleContent = stripStagedAttachmentNotice(content); + // Only backend-authored synthetic messages may opt into protocol-aware presentation. A user who + // types a lookalike envelope should continue to see an ordinary escaped user message. + const subagentReport = isSynthetic ? parseSubagentReportEnvelope(content) : null; + const copyContent = subagentReport + ? [ + subagentReport.reportMarkdown, + ...(subagentReport.structuredOutputJson + ? [`Structured output:\n${subagentReport.structuredOutputJson}`] + : []), + ].join("\n\n") + : visibleContent; const [vimEnabled] = usePersistedState(VIM_ENABLED_KEY, false, { listener: true }); const isMobileTouch = typeof window !== "undefined" && @@ -204,7 +220,7 @@ export const UserMessage: React.FC = ({ : []), { label: copied ? "Copied" : "Copy", - onClick: () => void copyToClipboard(visibleContent), + onClick: () => void copyToClipboard(copyContent), icon: copied ? : , }, ]; @@ -231,6 +247,19 @@ export const UserMessage: React.FC = ({ monitor wake ); + } else if (subagentReport) { + const isInProgress = subagentReport.status === "in_progress"; + label = ( + + + ); } else if (isSynthetic) { label = ( @@ -246,7 +275,8 @@ export const UserMessage: React.FC = ({ const isSideQuestion = message.isSideQuestion === true; const syntheticClassName = cn( className, - isSynthetic && "opacity-70", + isSynthetic && !subagentReport && "opacity-70", + subagentReport && "ml-0 w-full", (isGoalContinuation || isBudgetLimitWrapup) && "italic" ); @@ -264,6 +294,8 @@ export const UserMessage: React.FC = ({ renderedContent = ( ); + } else if (subagentReport) { + renderedContent = ; } else { renderedContent = ( = ({ - {title} + {title} {getStatusDisplay(status)} diff --git a/src/browser/stories/App.subagentReports.stories.tsx b/src/browser/stories/App.subagentReports.stories.tsx new file mode 100644 index 0000000000..dd4b0caa55 --- /dev/null +++ b/src/browser/stories/App.subagentReports.stories.tsx @@ -0,0 +1,125 @@ +/** Full-app visual coverage for sub-agent progress and terminal report presentation. */ + +import type { ComponentType } from "react"; + +import { appMeta, AppWithMocks, PIXEL_DUAL_THEME, type AppStory } from "./meta.js"; +import { setupSimpleChatStory } from "./helpers/chatSetup"; +import { collapseLeftSidebar, collapseRightSidebar } from "./helpers/uiState"; +import { + createAssistantMessage, + createSubagentReportMessage, + createUserMessage, +} from "./mocks/messages"; +import { STABLE_TIMESTAMP } from "./mocks/workspaces"; + +export default { + ...appMeta, + title: "App/SubagentReports", +}; + +const REPORT_MESSAGES = [ + createUserMessage("report-user", "Review the message rendering path and flag any UX gaps.", { + historySequence: 1, + timestamp: STABLE_TIMESTAMP - 180_000, + }), + createAssistantMessage( + "report-assistant", + "I delegated the rendering trace and asked the sub-agent to report important findings as they land.", + { + historySequence: 2, + timestamp: STABLE_TIMESTAMP - 170_000, + } + ), + createSubagentReportMessage("report-progress", { + historySequence: 3, + timestamp: STABLE_TIMESTAMP - 120_000, + taskId: "18c2511cea", + agentType: "explore", + status: "in_progress", + title: "Current report presentation traced across the parent transcript", + reportMarkdown: + "Parent-side reports currently expose the model-facing envelope. A dedicated renderer can preserve **markdown**, paths like `src/browser/features/Messages/UserMessage.tsx`, and status without the raw protocol.", + }), + createSubagentReportMessage("report-complete", { + historySequence: 4, + timestamp: STABLE_TIMESTAMP - 60_000, + taskId: "18c2511cea", + agentType: "explore", + // Omit status to cover report envelopes persisted before incremental updates existed. + title: "Presentation recommendation complete", + reportMarkdown: [ + "## Recommendation", + "", + "- Render reports as trusted sub-agent findings, not ordinary user input.", + "- Keep structured workflow data available without letting it dominate the transcript.", + ].join("\n"), + structuredOutput: { + affectedFiles: [ + "src/browser/features/Messages/UserMessage.tsx", + "src/browser/features/Messages/SubagentReportMessageContent.tsx", + ], + mobileVerified: true, + }, + }), + createAssistantMessage( + "report-integrated", + "I’ll incorporate both findings into the final implementation and keep the structured details available for inspection.", + { + historySequence: 5, + timestamp: STABLE_TIMESTAMP - 50_000, + } + ), +] as const; + +function setupReportStory() { + collapseLeftSidebar(); + collapseRightSidebar(); + return setupSimpleChatStory({ + workspaceId: "ws-subagent-report-presentation", + workspaceName: "subagent-reports", + projectName: "mux", + messages: [...REPORT_MESSAGES], + }); +} + +function PhoneDecorator(Story: ComponentType) { + return ( +
+ +
+ ); +} + +/** Incremental, completed legacy, and structured sub-agent report states. */ +export const Desktop: AppStory = { + // Pixel owns this visual-only desktop matrix. The full App cold-start can exceed the + // Storybook test-runner's 30-second smoke timeout before any assertions begin. + tags: ["!test"], + render: () => , + parameters: { + ...appMeta.parameters, + pixel: { matrix: PIXEL_DUAL_THEME }, + }, + // Pixel captures the complete desktop composition. Keep interaction assertions on the pinned + // phone story below because the Storybook test-runner's cold desktop app load can exhaust its + // per-story timeout before a desktop play function begins, while production behavior is covered + // by MessageRenderer.test.tsx. +}; + +/** Phone-width contract for long titles, markdown paths, and structured-output controls. */ +export const Phone: AppStory = { + // The fixed-width decorator + pinned Pixel phone viewport are the static breakpoint contract. + // Production rendering behavior is covered by MessageRenderer.test.tsx. + tags: ["!test"], + globals: { + viewport: { value: "mobile1", isRotated: false }, + }, + render: () => , + decorators: [PhoneDecorator], + parameters: { + ...appMeta.parameters, + pixel: { + matrix: { themes: ["dark", "light"], viewports: ["phone"] }, + }, + }, +}; diff --git a/src/browser/stories/mocks/messages.ts b/src/browser/stories/mocks/messages.ts index da21432dbc..433d610be8 100644 --- a/src/browser/stories/mocks/messages.ts +++ b/src/browser/stories/mocks/messages.ts @@ -122,6 +122,49 @@ export function createBashMonitorWakeMessage( }; } +/** Create the synthetic protocol envelope used to wake a parent with sub-agent findings. */ +export function createSubagentReportMessage( + id: string, + opts: { + historySequence: number; + timestamp?: number; + taskId: string; + agentType: string; + title: string; + reportMarkdown: string; + status?: "in_progress" | "completed"; + structuredOutput?: unknown; + } +): ChatMuxMessage { + const lines = [ + "", + `${opts.taskId}`, + `${opts.agentType}`, + ...(opts.status ? [`${opts.status}`] : []), + `${opts.title}`, + "", + opts.reportMarkdown, + "", + ]; + + if (opts.structuredOutput !== undefined) { + lines.push( + "", + "```json", + JSON.stringify(opts.structuredOutput, null, 2), + "```", + "" + ); + } + lines.push(""); + + return createUserMessage(id, lines.join("\n"), { + historySequence: opts.historySequence, + timestamp: opts.timestamp, + synthetic: true, + }); +} + /** Create a compaction request user message (triggers shimmer effect on streaming response) */ export function createCompactionRequestMessage( id: string, From b4555e943665108a52310c1f54da16ef7ece37b5 Mon Sep 17 00:00:00 2001 From: Ammar Date: Mon, 20 Jul 2026 21:29:35 -0500 Subject: [PATCH 02/13] =?UTF-8?q?=F0=9F=A4=96=20fix:=20preserve=20valid=20?= =?UTF-8?q?report=20delimiters=20and=20titles?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Anchor report markdown parsing at the protocol field terminator so examples containing report tags remain intact, and normalize valid multiline titles for display. _Generated with `mux` • Model: `openai:gpt-5.6-sol` • Thinking: `xhigh` • Cost: `$124.37`_ --- .../Messages/MessageRenderer.test.tsx | 47 +++++++++++++++ .../Messages/SubagentReportMessageContent.tsx | 57 +++++++++++++------ 2 files changed, 86 insertions(+), 18 deletions(-) diff --git a/src/browser/features/Messages/MessageRenderer.test.tsx b/src/browser/features/Messages/MessageRenderer.test.tsx index fed0ff728e..87439b0e28 100644 --- a/src/browser/features/Messages/MessageRenderer.test.tsx +++ b/src/browser/features/Messages/MessageRenderer.test.tsx @@ -4,6 +4,7 @@ import { GlobalWindow } from "happy-dom"; import { TooltipProvider } from "@radix-ui/react-tooltip"; import type { DisplayedMessage } from "@/common/types/message"; import { MessageRenderer } from "./MessageRenderer"; +import { parseSubagentReportEnvelope } from "./SubagentReportMessageContent"; describe("MessageRenderer goal continuation rows", () => { beforeEach(() => { @@ -273,6 +274,52 @@ Found the **message renderer** and its responsive story coverage. expect(queryByText(/mux_subagent_report/)).toBeNull(); }); + test("preserves protocol-looking delimiters inside report markdown", () => { + const report = parseSubagentReportEnvelope(` +task-protocol-example +explore +in_progress +Protocol example checked + +Before the example. + +\`\`\`xml + +\`\`\` + +After the example remains visible. + +`); + + expect(report).not.toBeNull(); + expect(report?.reportMarkdown).toContain("Before the example."); + expect(report?.reportMarkdown).toContain(""); + expect(report?.reportMarkdown).toContain("After the example remains visible."); + }); + + test("normalizes multiline report titles instead of exposing the envelope", () => { + const message = createReportMessage(` +task-multiline-title +explore +completed +Presentation trace +completed cleanly + +All rendering paths were verified. + +`); + + const { getByText, queryByText } = render( + + + + ); + + expect(getByText("Presentation trace completed cleanly")).toBeDefined(); + expect(getByText("All rendering paths were verified.")).toBeDefined(); + expect(queryByText(/mux_subagent_report/)).toBeNull(); + }); + test("treats legacy reports as completed and reveals structured output on demand", () => { const message = createReportMessage(` task-legacy diff --git a/src/browser/features/Messages/SubagentReportMessageContent.tsx b/src/browser/features/Messages/SubagentReportMessageContent.tsx index 6d43a1b4cb..44d0959ab9 100644 --- a/src/browser/features/Messages/SubagentReportMessageContent.tsx +++ b/src/browser/features/Messages/SubagentReportMessageContent.tsx @@ -13,18 +13,29 @@ export interface SubagentReportEnvelope { structuredOutputJson?: string; } -function extractLine(envelope: string, tag: string): string | null { - const match = new RegExp(`^<${tag}>([^\\n]*)<\\/${tag}>$`, "m").exec(envelope); - const value = match?.[1]?.trim(); - if (!value) return null; - return value; -} +const STRUCTURED_OUTPUT_START = "\n\n"; +const STRUCTURED_OUTPUT_END = "\n"; +const REPORT_END = "\n"; -function extractBlock(envelope: string, tag: string): string | null { - const match = new RegExp(`(?:^|\\n)<${tag}>\\n([\\s\\S]*?)\\n<\\/${tag}>(?:\\n|$)`).exec( - envelope - ); - return match?.[1]?.trim() ?? null; +function splitStructuredOutput(envelope: string): { + reportEnvelope: string; + structuredOutput: string | null; +} { + if (!envelope.endsWith(STRUCTURED_OUTPUT_END)) { + return { reportEnvelope: envelope, structuredOutput: null }; + } + + const start = envelope.lastIndexOf(STRUCTURED_OUTPUT_START); + if (start === -1) { + return { reportEnvelope: envelope, structuredOutput: null }; + } + + return { + reportEnvelope: envelope.slice(0, start), + structuredOutput: envelope + .slice(start + STRUCTURED_OUTPUT_START.length, -STRUCTURED_OUTPUT_END.length) + .trim(), + }; } function normalizeStructuredOutput(block: string): string { @@ -47,17 +58,27 @@ export function parseSubagentReportEnvelope(content: string): SubagentReportEnve const root = /^\n([\s\S]*?)\n<\/mux_subagent_report>$/.exec(content); if (!root) return null; - const envelope = root[1]; - const taskId = extractLine(envelope, "task_id"); - const agentType = extractLine(envelope, "agent_type"); - const title = extractLine(envelope, "title"); - const reportMarkdown = extractBlock(envelope, "report_markdown"); - const rawStatus = extractLine(envelope, "status"); + const { reportEnvelope, structuredOutput } = splitStructuredOutput(root[1]); + if (!reportEnvelope.endsWith(REPORT_END)) return null; + + // Parse from the fixed metadata prefix, then anchor the report close at the end. Report markdown + // is intentionally unescaped by TaskService, so it may itself contain protocol-looking examples + // such as a line containing ; only the final delimiter terminates the field. + const fields = + /^([^\n]*)<\/task_id>\n([^\n]*)<\/agent_type>\n(?:([^\n]*)<\/status>\n)?([\s\S]*?)<\/title>\n<report_markdown>\n([\s\S]*)$/.exec( + reportEnvelope.slice(0, -REPORT_END.length) + ); + if (!fields) return null; + + const taskId = fields[1]?.trim(); + const agentType = fields[2]?.trim(); + const rawStatus = fields[3]?.trim() || null; + const title = fields[4]?.replace(/\s+/g, " ").trim(); + const reportMarkdown = fields[5]?.trim(); if (!taskId || !agentType || !title || !reportMarkdown) return null; if (rawStatus !== null && rawStatus !== "in_progress" && rawStatus !== "completed") return null; - const structuredOutput = extractBlock(envelope, "structured_output_json"); return { taskId, agentType, From 8b3eb97d8ac956df28be45d3c5a98228e95f5b2e Mon Sep 17 00:00:00 2001 From: Ammar <ammar+ai@ammar.io> Date: Wed, 22 Jul 2026 10:06:09 -0500 Subject: [PATCH 03/13] =?UTF-8?q?=F0=9F=A4=96=20tests:=20stabilize=20Pixel?= =?UTF-8?q?=20story=20captures?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Disable motion and blinking carets only during Pixel captures, mask native media controls, and exclude two duplicate snapshots whose Chromium border rasterization remains nondeterministic. Validated with two complete 337-snapshot Pixel captures producing identical hashes. _Generated with `mux` • Model: `openai:gpt-5.6-sol` • Thinking: `xhigh` • Cost: `$177.24`_ <!-- mux-attribution: model=openai:gpt-5.6-sol thinking=xhigh costs=177.24 --> --- .storybook/preview.tsx | 21 ++++++++++++++++--- .../RightSidebar/Memory/MemoryTab.stories.tsx | 14 +++++++++++++ .../Sections/MCPSettingsSection.stories.tsx | 12 +++++++++++ .../Tools/AttachFileToolCall.stories.tsx | 9 ++++++++ 4 files changed, 53 insertions(+), 3 deletions(-) diff --git a/.storybook/preview.tsx b/.storybook/preview.tsx index 3cc767a1e9..2a77f1a692 100644 --- a/.storybook/preview.tsx +++ b/.storybook/preview.tsx @@ -1,4 +1,5 @@ import type { Preview } from "@storybook/react-vite"; +import { isPixel } from "@coder/pixel-storybook"; import { ThemeProvider, type ThemeMode } from "../src/browser/contexts/ThemeContext"; import "../src/browser/styles/globals.css"; import { @@ -27,6 +28,15 @@ import { configure } from "storybook/test"; // after userEvent.click can exceed the 1 s default. configure({ asyncUtilTimeout: 5000 }); +const PIXEL_STABILITY_CSS = ` + *, *::before, *::after { + animation: none !important; + caret-color: transparent !important; + scroll-behavior: auto !important; + transition: none !important; + } +`; + const STORYBOOK_FONTS_READY_TIMEOUT_MS = 2500; let fontsReadyPromise: Promise<void> | null = null; @@ -159,9 +169,14 @@ const preview: Preview = { clearWorkspaceDrafts(); return ( - <ThemeProvider forcedTheme={mode}> - <Story /> - </ThemeProvider> + <> + {/* Pixel snapshots should capture semantic states, not arbitrary animation frames, + blinking carets, or a transition mid-pixel. The marker is injected before page code. */} + {isPixel() && <style data-pixel-stability>{PIXEL_STABILITY_CSS}</style>} + <ThemeProvider forcedTheme={mode}> + <Story /> + </ThemeProvider> + </> ); }, ], diff --git a/src/browser/features/RightSidebar/Memory/MemoryTab.stories.tsx b/src/browser/features/RightSidebar/Memory/MemoryTab.stories.tsx index f8ac048b52..ba38a45bc6 100644 --- a/src/browser/features/RightSidebar/Memory/MemoryTab.stories.tsx +++ b/src/browser/features/RightSidebar/Memory/MemoryTab.stories.tsx @@ -4,6 +4,7 @@ import { userEvent, within } from "@storybook/test"; import { updatePersistedState } from "@/browser/hooks/usePersistedState"; import { APIProvider } from "@/browser/contexts/API"; import { createMockORPCClient } from "@/browser/stories/mocks/orpc"; +import { blurActiveElement } from "@/browser/stories/storyPlayHelpers.js"; import type { MemoryConsolidationRecordPayload, MemoryFileInfo, @@ -126,6 +127,14 @@ function renderTab(width: string) { } export const List: Story = { + tags: ["pixel-stability"], + parameters: { + pixel: { + // This duplicates the same tree permutations covered by Settings/MemorySection.WithFiles, + // while Chromium rasterizes its nested rounded borders/chevrons nondeterministically. + exclude: true, + }, + }, render: () => renderTab("360px"), play: async ({ canvasElement }) => { const canvas = within(canvasElement); @@ -137,11 +146,16 @@ export const List: Story = { }; export const Editor: Story = { + tags: ["pixel-stability"], render: () => renderTab("360px"), play: async ({ canvasElement }) => { const canvas = within(canvasElement); const row = await canvas.findByText("preferences.md"); await userEvent.click(row); await canvas.findByLabelText("Memory file content"); + // The click target disappears when the editor opens, so explicitly move the virtual pointer + // off whatever control replaced it and hide the focused textarea caret before Pixel captures. + await userEvent.hover(canvasElement); + blurActiveElement(); }, }; diff --git a/src/browser/features/Settings/Sections/MCPSettingsSection.stories.tsx b/src/browser/features/Settings/Sections/MCPSettingsSection.stories.tsx index 7df8147321..365f5c2b93 100644 --- a/src/browser/features/Settings/Sections/MCPSettingsSection.stories.tsx +++ b/src/browser/features/Settings/Sections/MCPSettingsSection.stories.tsx @@ -10,6 +10,7 @@ import { PolicyProvider } from "@/browser/contexts/PolicyContext"; import { ThemeProvider } from "@/browser/contexts/ThemeContext"; import { updatePersistedState } from "@/browser/hooks/usePersistedState"; import { createMockORPCClient } from "@/browser/stories/mocks/orpc"; +import { blurActiveElement } from "@/browser/stories/storyPlayHelpers.js"; import { getMCPTestResultsKey } from "@/common/constants/storage"; import type { MCPServerInfo } from "@/common/types/mcp"; import type { MCPOAuthAuthStatus } from "@/common/types/mcpOauth"; @@ -170,6 +171,14 @@ export const ProjectSettingsEmpty: Story = { }; export const ProjectSettingsAddRemoteServerHeaders: Story = { + tags: ["pixel-stability"], + parameters: { + pixel: { + // Keep the interaction contract in Storybook tests, but exclude the screenshot: Chromium + // alternates the rounded form border's corner antialiasing by one shade between captures. + exclude: true, + }, + }, render: () => ( <MCPSettingsSectionStoryShell setup={() => @@ -232,6 +241,9 @@ export const ProjectSettingsAddRemoteServerHeaders: Story = { await expect(body.findByDisplayValue("MCP_TOKEN")).resolves.toBeInTheDocument(); await expect(body.findByDisplayValue("X-Env")).resolves.toBeInTheDocument(); await expect(body.findByDisplayValue("prod")).resolves.toBeInTheDocument(); + // Do not snapshot a focused input's platform-rendered caret/border antialiasing. + await userEvent.hover(canvasElement); + blurActiveElement(); }, }; diff --git a/src/browser/features/Tools/AttachFileToolCall.stories.tsx b/src/browser/features/Tools/AttachFileToolCall.stories.tsx index d9d6ff22c2..bacba6ecfb 100644 --- a/src/browser/features/Tools/AttachFileToolCall.stories.tsx +++ b/src/browser/features/Tools/AttachFileToolCall.stories.tsx @@ -54,6 +54,15 @@ function GallerySection(props: { label: string; children: ReactNode }) { // (image, video, audio, markdown, generic file) into a single snapshot to keep // the snapshot budget low while preserving every distinct visual state. export const Gallery: Story = { + tags: ["pixel-stability"], + parameters: { + pixel: { + // Chromium's native media controls contain an internal loading spinner whose frame is not + // controlled by page CSS. Mask only those controls; the surrounding attachment UI remains + // under visual regression coverage. + mask: [{ selector: "video, audio" }], + }, + }, render: () => ( <ToolStoryShell> <div className="flex flex-col gap-6"> From 4678f5052a9900db3d4bdf286ef93f8d1b40d987 Mon Sep 17 00:00:00 2001 From: Ammar <ammar+ai@ammar.io> Date: Wed, 22 Jul 2026 10:11:33 -0500 Subject: [PATCH 04/13] =?UTF-8?q?=F0=9F=A4=96=20fix:=20preserve=20report?= =?UTF-8?q?=20markdown=20whitespace?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Keep report body indentation and trailing spaces intact so indented code blocks and Markdown hard breaks render and copy exactly as reported. _Generated with `mux` • Model: `openai:gpt-5.6-sol` • Thinking: `xhigh` • Cost: `$177.24`_ <!-- mux-attribution: model=openai:gpt-5.6-sol thinking=xhigh costs=177.24 --> --- .../features/Messages/MessageRenderer.test.tsx | 16 ++++++++++++++++ .../Messages/SubagentReportMessageContent.tsx | 8 ++++++-- 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/src/browser/features/Messages/MessageRenderer.test.tsx b/src/browser/features/Messages/MessageRenderer.test.tsx index 87439b0e28..3f9d55a259 100644 --- a/src/browser/features/Messages/MessageRenderer.test.tsx +++ b/src/browser/features/Messages/MessageRenderer.test.tsx @@ -297,6 +297,22 @@ After the example remains visible. expect(report?.reportMarkdown).toContain("After the example remains visible."); }); + test("preserves leading indentation and trailing spaces in report markdown", () => { + const report = parseSubagentReportEnvelope(`<mux_subagent_report> +<task_id>task-whitespace</task_id> +<agent_type>explore</agent_type> +<status>completed</status> +<title>Whitespace checked + + const answer = 42; +Line with a hard break. + +`); + + expect(report).not.toBeNull(); + expect(report?.reportMarkdown).toBe(" const answer = 42;\nLine with a hard break. "); + }); + test("normalizes multiline report titles instead of exposing the envelope", () => { const message = createReportMessage(` task-multiline-title diff --git a/src/browser/features/Messages/SubagentReportMessageContent.tsx b/src/browser/features/Messages/SubagentReportMessageContent.tsx index 44d0959ab9..e6c3b4e048 100644 --- a/src/browser/features/Messages/SubagentReportMessageContent.tsx +++ b/src/browser/features/Messages/SubagentReportMessageContent.tsx @@ -74,9 +74,13 @@ export function parseSubagentReportEnvelope(content: string): SubagentReportEnve const agentType = fields[2]?.trim(); const rawStatus = fields[3]?.trim() || null; const title = fields[4]?.replace(/\s+/g, " ").trim(); - const reportMarkdown = fields[5]?.trim(); + const reportMarkdown = fields[5]; - if (!taskId || !agentType || !title || !reportMarkdown) return null; + // TaskService stores reportMarkdown verbatim between separator newlines. Preserve leading + // indentation and trailing spaces because Markdown uses both for code blocks and hard breaks. + if (!taskId || !agentType || !title || reportMarkdown == null || reportMarkdown.length === 0) { + return null; + } if (rawStatus !== null && rawStatus !== "in_progress" && rawStatus !== "completed") return null; return { From 208efa23e935b4bcee2c54ad41daf0b45e65456f Mon Sep 17 00:00:00 2001 From: Ammar Date: Wed, 22 Jul 2026 10:13:35 -0500 Subject: [PATCH 05/13] =?UTF-8?q?=F0=9F=A4=96=20tests:=20encode=20markdown?= =?UTF-8?q?=20hard-break=20fixture=20safely?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Construct the trailing spaces inside the test expression so whitespace preservation stays covered without leaving source-level trailing whitespace. _Generated with `mux` • Model: `openai:gpt-5.6-sol` • Thinking: `xhigh` • Cost: `$177.24`_ --- src/browser/features/Messages/MessageRenderer.test.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/browser/features/Messages/MessageRenderer.test.tsx b/src/browser/features/Messages/MessageRenderer.test.tsx index 3f9d55a259..50e6d32518 100644 --- a/src/browser/features/Messages/MessageRenderer.test.tsx +++ b/src/browser/features/Messages/MessageRenderer.test.tsx @@ -305,7 +305,7 @@ After the example remains visible. Whitespace checked const answer = 42; -Line with a hard break. +Line with a hard break.${" "} `); From d360a55f6b2d383f2cd5771c2ae5d92959173ca4 Mon Sep 17 00:00:00 2001 From: Ammar Date: Wed, 22 Jul 2026 10:52:11 -0500 Subject: [PATCH 06/13] =?UTF-8?q?=F0=9F=A4=96=20fix:=20narrow=20Pixel=20re?= =?UTF-8?q?view=20to=20meaningful=20changes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Remove the broad Pixel-only motion override and duplicate snapshot exclusions that caused unrelated review churn. Upgrade Pixel to 0.2.2 so review entries include unique story names, while retaining only the native media-control mask. Also preserve embedded title delimiters in valid subagent report envelopes. _Generated with `mux` • Model: `openai:gpt-5.6-sol` • Thinking: `xhigh` • Cost: `$177.24`_ --- .storybook/preview.tsx | 21 +-- bun.lock | 123 +++--------------- package.json | 2 +- .../Messages/MessageRenderer.test.tsx | 19 +++ .../Messages/SubagentReportMessageContent.tsx | 17 ++- .../RightSidebar/Memory/MemoryTab.stories.tsx | 14 -- .../Sections/MCPSettingsSection.stories.tsx | 12 -- .../Tools/AttachFileToolCall.stories.tsx | 1 - 8 files changed, 51 insertions(+), 158 deletions(-) diff --git a/.storybook/preview.tsx b/.storybook/preview.tsx index 2a77f1a692..3cc767a1e9 100644 --- a/.storybook/preview.tsx +++ b/.storybook/preview.tsx @@ -1,5 +1,4 @@ import type { Preview } from "@storybook/react-vite"; -import { isPixel } from "@coder/pixel-storybook"; import { ThemeProvider, type ThemeMode } from "../src/browser/contexts/ThemeContext"; import "../src/browser/styles/globals.css"; import { @@ -28,15 +27,6 @@ import { configure } from "storybook/test"; // after userEvent.click can exceed the 1 s default. configure({ asyncUtilTimeout: 5000 }); -const PIXEL_STABILITY_CSS = ` - *, *::before, *::after { - animation: none !important; - caret-color: transparent !important; - scroll-behavior: auto !important; - transition: none !important; - } -`; - const STORYBOOK_FONTS_READY_TIMEOUT_MS = 2500; let fontsReadyPromise: Promise | null = null; @@ -169,14 +159,9 @@ const preview: Preview = { clearWorkspaceDrafts(); return ( - <> - {/* Pixel snapshots should capture semantic states, not arbitrary animation frames, - blinking carets, or a transition mid-pixel. The marker is injected before page code. */} - {isPixel() && } - - - - + + + ); }, ], diff --git a/bun.lock b/bun.lock index 9306bc5cbb..4c58796093 100644 --- a/bun.lock +++ b/bun.lock @@ -1,5 +1,6 @@ { "lockfileVersion": 1, + "configVersion": 0, "workspaces": { "": { "name": "mux", @@ -111,7 +112,7 @@ "@babel/preset-env": "^7.28.5", "@babel/preset-react": "^7.28.5", "@babel/preset-typescript": "^7.28.5", - "@coder/pixel-storybook": "0.2.1", + "@coder/pixel-storybook": "0.2.2", "@electron/rebuild": "^4.0.4", "@eslint/js": "^9.36.0", "@playwright/test": "^1.56.0", @@ -563,7 +564,7 @@ "@chevrotain/utils": ["@chevrotain/utils@11.0.3", "", {}, "sha512-YslZMgtJUyuMbZ+aKvfF3x1f5liK4mWNxghFRv7jqRR9C3R3fAOGTTKvxXDa2Y1s9zSbcpuO0cAxDYsc9SrXoQ=="], - "@coder/pixel-storybook": ["@coder/pixel-storybook@0.2.1", "", { "dependencies": { "get-port-please": "3.1.2", "jsonc-parser": "^3.3.1", "serve-handler": "6.1.7", "zod": "3.23.8" }, "peerDependencies": { "playwright-core": ">=1.47.2", "storybook": ">=8.0.0" }, "bin": { "pixel-storybook": "build/bin.js" } }, "sha512-o4EIY5JcLr+U/URGwfTQ1s0rs2jp4CfdOG94kHABLVD6wFEwUDyl52LX8utx6ArOkxuixbe3NMUTdxWU33fqLw=="], + "@coder/pixel-storybook": ["@coder/pixel-storybook@0.2.2", "", { "dependencies": { "get-port-please": "3.1.2", "jsonc-parser": "^3.3.1", "serve-handler": "6.1.7", "zod": "3.23.8" }, "peerDependencies": { "playwright-core": ">=1.47.2", "storybook": ">=8.0.0" }, "bin": { "pixel-storybook": "build/bin.js" } }, "sha512-f1dJ7FiNCdoT9VRVH6IWFnCG3VMnavtvTx/r6sZ4hzhj/rJEZv4P69J2x4XdYaMAwi+O5xbp2UQf0dNf2U9lLA=="], "@csstools/color-helpers": ["@csstools/color-helpers@5.1.0", "", {}, "sha512-S11EXWJyy0Mz5SYvRmY8nJYTFFd1LCNV+7cXyAgQtOOuzb4EsgfqDufL+9esx72/eLhsRdGZwaldu/h+E4t4BA=="], @@ -1455,7 +1456,7 @@ "@types/ms": ["@types/ms@2.1.0", "", {}, "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA=="], - "@types/node": ["@types/node@24.12.2", "", { "dependencies": { "undici-types": "~7.16.0" } }, "sha512-A1sre26ke7HDIuY/M23nd9gfB+nrmhtYyMINbjI1zHJxYteKR6qSMX56FsmjMcDb3SMcjJg5BiRRgOCC/yBD0g=="], + "@types/node": ["@types/node@20.19.25", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-ZsJzA5thDQMSQO788d7IocwwQbI8B5OPzmqNvpf3NY/+MHDAS759Wo0gd2WQeXYt5AAAQjzcrTVC6SKCuYgoCQ=="], "@types/plist": ["@types/plist@3.0.5", "", { "dependencies": { "@types/node": "*", "xmlbuilder": ">=11.0.1" } }, "sha512-E6OCaRmAe4WDmWNsL/9RMqdkkzDCY1etutkflWk4c+AcjDU07Pcz1fQwTX0TQz+Pxqn9i4L1TU3UFpjnrcDgxA=="], @@ -3619,7 +3620,7 @@ "undici": ["undici@7.16.0", "", {}, "sha512-QEg3HPMll0o3t2ourKwOeUAZ159Kn9mx5pnzHRQO8+Wixmh88YdZRiIwat0iNzNNXn0yoEtXJqFpyW7eM8BV7g=="], - "undici-types": ["undici-types@7.16.0", "", {}, "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw=="], + "undici-types": ["undici-types@6.21.0", "", {}, "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ=="], "unicode-canonical-property-names-ecmascript": ["unicode-canonical-property-names-ecmascript@2.0.1", "", {}, "sha512-dA8WbNeb2a6oQzAQ55YlT5vQAWGV9WXOsi3SskE3bcCdM0P4SDd+24zS/OCacdRq5BkdsRj9q3Pg6YyQoxIGqg=="], @@ -3857,26 +3858,14 @@ "@istanbuljs/load-nyc-config/js-yaml": ["js-yaml@3.14.2", "", { "dependencies": { "argparse": "^1.0.7", "esprima": "^4.0.0" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-PMSmkqxr106Xa156c2M265Z+FTrPl+oxd/rgOQy2tijQeK5TxQ43psO1ZCwhVOSdnn+RzkzlRz/eY4BgJBYVpg=="], - "@jest/console/@types/node": ["@types/node@20.19.25", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-ZsJzA5thDQMSQO788d7IocwwQbI8B5OPzmqNvpf3NY/+MHDAS759Wo0gd2WQeXYt5AAAQjzcrTVC6SKCuYgoCQ=="], - "@jest/console/chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="], - "@jest/core/@types/node": ["@types/node@20.19.25", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-ZsJzA5thDQMSQO788d7IocwwQbI8B5OPzmqNvpf3NY/+MHDAS759Wo0gd2WQeXYt5AAAQjzcrTVC6SKCuYgoCQ=="], - "@jest/core/ansi-escapes": ["ansi-escapes@4.3.2", "", { "dependencies": { "type-fest": "^0.21.3" } }, "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ=="], "@jest/core/chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="], "@jest/core/ci-info": ["ci-info@4.3.1", "", {}, "sha512-Wdy2Igu8OcBpI2pZePZ5oWjPC38tmDVx5WKUXKwlLYkA0ozo85sLsLvkBbBn/sZaSCMFOGZJ14fvW9t5/d7kdA=="], - "@jest/environment/@types/node": ["@types/node@20.19.25", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-ZsJzA5thDQMSQO788d7IocwwQbI8B5OPzmqNvpf3NY/+MHDAS759Wo0gd2WQeXYt5AAAQjzcrTVC6SKCuYgoCQ=="], - - "@jest/fake-timers/@types/node": ["@types/node@20.19.25", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-ZsJzA5thDQMSQO788d7IocwwQbI8B5OPzmqNvpf3NY/+MHDAS759Wo0gd2WQeXYt5AAAQjzcrTVC6SKCuYgoCQ=="], - - "@jest/pattern/@types/node": ["@types/node@20.19.25", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-ZsJzA5thDQMSQO788d7IocwwQbI8B5OPzmqNvpf3NY/+MHDAS759Wo0gd2WQeXYt5AAAQjzcrTVC6SKCuYgoCQ=="], - - "@jest/reporters/@types/node": ["@types/node@20.19.25", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-ZsJzA5thDQMSQO788d7IocwwQbI8B5OPzmqNvpf3NY/+MHDAS759Wo0gd2WQeXYt5AAAQjzcrTVC6SKCuYgoCQ=="], - "@jest/reporters/chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="], "@jest/reporters/glob": ["glob@10.5.0", "", { "dependencies": { "foreground-child": "^3.1.0", "jackspeak": "^3.1.2", "minimatch": "^9.0.4", "minipass": "^7.1.2", "package-json-from-dist": "^1.0.0", "path-scurry": "^1.11.1" }, "bin": { "glob": "dist/esm/bin.mjs" } }, "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg=="], @@ -3893,8 +3882,6 @@ "@jest/transform/write-file-atomic": ["write-file-atomic@5.0.1", "", { "dependencies": { "imurmurhash": "^0.1.4", "signal-exit": "^4.0.1" } }, "sha512-+QU2zd6OTD8XWIJCbffaiQeH9U73qIqafo1x6V1snCWYGJf6cVE0cDR4D8xRzcEnfI21IFrUPzPGtcPf8AC+Rw=="], - "@jest/types/@types/node": ["@types/node@20.19.25", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-ZsJzA5thDQMSQO788d7IocwwQbI8B5OPzmqNvpf3NY/+MHDAS759Wo0gd2WQeXYt5AAAQjzcrTVC6SKCuYgoCQ=="], - "@jest/types/chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="], "@malept/flatpak-bundler/fs-extra": ["fs-extra@9.1.0", "", { "dependencies": { "at-least-node": "^1.0.0", "graceful-fs": "^4.2.0", "jsonfile": "^6.0.1", "universalify": "^2.0.0" } }, "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ=="], @@ -3999,18 +3986,18 @@ "@types/body-parser/@types/node": ["@types/node@22.19.1", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-LCCV0HdSZZZb34qifBsyWlUmok6W7ouER+oQIGBScS8EsZsQbrtFTUrDX4hOl+CS6p7cnNC4td+qrSVGSCTUfQ=="], - "@types/connect/@types/node": ["@types/node@22.19.1", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-LCCV0HdSZZZb34qifBsyWlUmok6W7ouER+oQIGBScS8EsZsQbrtFTUrDX4hOl+CS6p7cnNC4td+qrSVGSCTUfQ=="], + "@types/cacheable-request/@types/node": ["@types/node@24.12.2", "", { "dependencies": { "undici-types": "~7.16.0" } }, "sha512-A1sre26ke7HDIuY/M23nd9gfB+nrmhtYyMINbjI1zHJxYteKR6qSMX56FsmjMcDb3SMcjJg5BiRRgOCC/yBD0g=="], - "@types/cors/@types/node": ["@types/node@20.19.25", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-ZsJzA5thDQMSQO788d7IocwwQbI8B5OPzmqNvpf3NY/+MHDAS759Wo0gd2WQeXYt5AAAQjzcrTVC6SKCuYgoCQ=="], + "@types/connect/@types/node": ["@types/node@22.19.1", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-LCCV0HdSZZZb34qifBsyWlUmok6W7ouER+oQIGBScS8EsZsQbrtFTUrDX4hOl+CS6p7cnNC4td+qrSVGSCTUfQ=="], "@types/express-serve-static-core/@types/node": ["@types/node@22.19.1", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-LCCV0HdSZZZb34qifBsyWlUmok6W7ouER+oQIGBScS8EsZsQbrtFTUrDX4hOl+CS6p7cnNC4td+qrSVGSCTUfQ=="], - "@types/fs-extra/@types/node": ["@types/node@20.19.25", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-ZsJzA5thDQMSQO788d7IocwwQbI8B5OPzmqNvpf3NY/+MHDAS759Wo0gd2WQeXYt5AAAQjzcrTVC6SKCuYgoCQ=="], - - "@types/jsdom/@types/node": ["@types/node@20.19.25", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-ZsJzA5thDQMSQO788d7IocwwQbI8B5OPzmqNvpf3NY/+MHDAS759Wo0gd2WQeXYt5AAAQjzcrTVC6SKCuYgoCQ=="], + "@types/keyv/@types/node": ["@types/node@24.12.2", "", { "dependencies": { "undici-types": "~7.16.0" } }, "sha512-A1sre26ke7HDIuY/M23nd9gfB+nrmhtYyMINbjI1zHJxYteKR6qSMX56FsmjMcDb3SMcjJg5BiRRgOCC/yBD0g=="], "@types/plist/@types/node": ["@types/node@22.19.1", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-LCCV0HdSZZZb34qifBsyWlUmok6W7ouER+oQIGBScS8EsZsQbrtFTUrDX4hOl+CS6p7cnNC4td+qrSVGSCTUfQ=="], + "@types/responselike/@types/node": ["@types/node@24.12.2", "", { "dependencies": { "undici-types": "~7.16.0" } }, "sha512-A1sre26ke7HDIuY/M23nd9gfB+nrmhtYyMINbjI1zHJxYteKR6qSMX56FsmjMcDb3SMcjJg5BiRRgOCC/yBD0g=="], + "@types/send/@types/node": ["@types/node@22.19.1", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-LCCV0HdSZZZb34qifBsyWlUmok6W7ouER+oQIGBScS8EsZsQbrtFTUrDX4hOl+CS6p7cnNC4td+qrSVGSCTUfQ=="], "@types/serve-static/@types/node": ["@types/node@22.19.1", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-LCCV0HdSZZZb34qifBsyWlUmok6W7ouER+oQIGBScS8EsZsQbrtFTUrDX4hOl+CS6p7cnNC4td+qrSVGSCTUfQ=="], @@ -4021,10 +4008,6 @@ "@types/wait-on/@types/node": ["@types/node@22.19.1", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-LCCV0HdSZZZb34qifBsyWlUmok6W7ouER+oQIGBScS8EsZsQbrtFTUrDX4hOl+CS6p7cnNC4td+qrSVGSCTUfQ=="], - "@types/write-file-atomic/@types/node": ["@types/node@20.19.25", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-ZsJzA5thDQMSQO788d7IocwwQbI8B5OPzmqNvpf3NY/+MHDAS759Wo0gd2WQeXYt5AAAQjzcrTVC6SKCuYgoCQ=="], - - "@types/ws/@types/node": ["@types/node@20.19.25", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-ZsJzA5thDQMSQO788d7IocwwQbI8B5OPzmqNvpf3NY/+MHDAS759Wo0gd2WQeXYt5AAAQjzcrTVC6SKCuYgoCQ=="], - "@types/yauzl/@types/node": ["@types/node@22.19.1", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-LCCV0HdSZZZb34qifBsyWlUmok6W7ouER+oQIGBScS8EsZsQbrtFTUrDX4hOl+CS6p7cnNC4td+qrSVGSCTUfQ=="], "@typescript-eslint/typescript-estree/minimatch": ["minimatch@9.0.5", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow=="], @@ -4061,8 +4044,6 @@ "builder-util/https-proxy-agent": ["https-proxy-agent@5.0.1", "", { "dependencies": { "agent-base": "6", "debug": "4" } }, "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA=="], - "bun-types/@types/node": ["@types/node@20.19.25", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-ZsJzA5thDQMSQO788d7IocwwQbI8B5OPzmqNvpf3NY/+MHDAS759Wo0gd2WQeXYt5AAAQjzcrTVC6SKCuYgoCQ=="], - "caching-transform/write-file-atomic": ["write-file-atomic@3.0.3", "", { "dependencies": { "imurmurhash": "^0.1.4", "is-typedarray": "^1.0.0", "signal-exit": "^3.0.2", "typedarray-to-buffer": "^3.1.5" } }, "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q=="], "chokidar/fsevents": ["fsevents@2.3.3", "", { "os": "darwin" }, "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="], @@ -4101,6 +4082,8 @@ "dom-serializer/entities": ["entities@2.2.0", "", {}, "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A=="], + "electron/@types/node": ["@types/node@24.12.2", "", { "dependencies": { "undici-types": "~7.16.0" } }, "sha512-A1sre26ke7HDIuY/M23nd9gfB+nrmhtYyMINbjI1zHJxYteKR6qSMX56FsmjMcDb3SMcjJg5BiRRgOCC/yBD0g=="], + "electron-builder/chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="], "electron-publish/chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="], @@ -4149,8 +4132,6 @@ "globby/ignore": ["ignore@5.3.2", "", {}, "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g=="], - "happy-dom/@types/node": ["@types/node@20.19.25", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-ZsJzA5thDQMSQO788d7IocwwQbI8B5OPzmqNvpf3NY/+MHDAS759Wo0gd2WQeXYt5AAAQjzcrTVC6SKCuYgoCQ=="], - "hasha/type-fest": ["type-fest@0.8.1", "", {}, "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA=="], "hast-util-to-parse5/property-information": ["property-information@6.5.0", "", {}, "sha512-PgTgs/BlvHxOu8QuEN7wi5A0OmXaBcHpmCSTehcs6Uuu9IkDIEo13Hy7n898RHfrQ49vKCoGeWZSaAK01nwVig=="], @@ -4191,8 +4172,6 @@ "jest-environment-node/@types/node": ["@types/node@22.19.1", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-LCCV0HdSZZZb34qifBsyWlUmok6W7ouER+oQIGBScS8EsZsQbrtFTUrDX4hOl+CS6p7cnNC4td+qrSVGSCTUfQ=="], - "jest-haste-map/@types/node": ["@types/node@20.19.25", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-ZsJzA5thDQMSQO788d7IocwwQbI8B5OPzmqNvpf3NY/+MHDAS759Wo0gd2WQeXYt5AAAQjzcrTVC6SKCuYgoCQ=="], - "jest-haste-map/fsevents": ["fsevents@2.3.3", "", { "os": "darwin" }, "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="], "jest-junit/mkdirp": ["mkdirp@1.0.4", "", { "bin": { "mkdirp": "bin/cmd.js" } }, "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw=="], @@ -4203,8 +4182,6 @@ "jest-message-util/chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="], - "jest-mock/@types/node": ["@types/node@20.19.25", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-ZsJzA5thDQMSQO788d7IocwwQbI8B5OPzmqNvpf3NY/+MHDAS759Wo0gd2WQeXYt5AAAQjzcrTVC6SKCuYgoCQ=="], - "jest-process-manager/chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="], "jest-process-manager/signal-exit": ["signal-exit@3.0.7", "", {}, "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ=="], @@ -4217,8 +4194,6 @@ "jest-runner/source-map-support": ["source-map-support@0.5.13", "", { "dependencies": { "buffer-from": "^1.0.0", "source-map": "^0.6.0" } }, "sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w=="], - "jest-runtime/@types/node": ["@types/node@20.19.25", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-ZsJzA5thDQMSQO788d7IocwwQbI8B5OPzmqNvpf3NY/+MHDAS759Wo0gd2WQeXYt5AAAQjzcrTVC6SKCuYgoCQ=="], - "jest-runtime/chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="], "jest-runtime/glob": ["glob@10.5.0", "", { "dependencies": { "foreground-child": "^3.1.0", "jackspeak": "^3.1.2", "minimatch": "^9.0.4", "minipass": "^7.1.2", "package-json-from-dist": "^1.0.0", "path-scurry": "^1.11.1" }, "bin": { "glob": "dist/esm/bin.mjs" } }, "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg=="], @@ -4227,8 +4202,6 @@ "jest-snapshot/chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="], - "jest-util/@types/node": ["@types/node@20.19.25", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-ZsJzA5thDQMSQO788d7IocwwQbI8B5OPzmqNvpf3NY/+MHDAS759Wo0gd2WQeXYt5AAAQjzcrTVC6SKCuYgoCQ=="], - "jest-util/chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="], "jest-util/ci-info": ["ci-info@4.3.1", "", {}, "sha512-Wdy2Igu8OcBpI2pZePZ5oWjPC38tmDVx5WKUXKwlLYkA0ozo85sLsLvkBbBn/sZaSCMFOGZJ14fvW9t5/d7kdA=="], @@ -4237,16 +4210,12 @@ "jest-watch-typeahead/slash": ["slash@5.1.0", "", {}, "sha512-ZA6oR3T/pEyuqwMgAKT0/hAv8oAXckzbkmR0UkUosQ+Mc4RxGoJkRmwHgHufaenlyAgE1Mxgpdcrf75y6XcnDg=="], - "jest-watcher/@types/node": ["@types/node@20.19.25", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-ZsJzA5thDQMSQO788d7IocwwQbI8B5OPzmqNvpf3NY/+MHDAS759Wo0gd2WQeXYt5AAAQjzcrTVC6SKCuYgoCQ=="], - "jest-watcher/ansi-escapes": ["ansi-escapes@4.3.2", "", { "dependencies": { "type-fest": "^0.21.3" } }, "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ=="], "jest-watcher/chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="], "jest-watcher/string-length": ["string-length@4.0.2", "", { "dependencies": { "char-regex": "^1.0.2", "strip-ansi": "^6.0.0" } }, "sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ=="], - "jest-worker/@types/node": ["@types/node@20.19.25", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-ZsJzA5thDQMSQO788d7IocwwQbI8B5OPzmqNvpf3NY/+MHDAS759Wo0gd2WQeXYt5AAAQjzcrTVC6SKCuYgoCQ=="], - "jsdom/parse5": ["parse5@8.0.0", "", { "dependencies": { "entities": "^6.0.0" } }, "sha512-9m4m5GSgXjL4AjumKzq1Fgfp3Z8rsvjRNbnkVwfu2ImRqE5D0LnY2QfDen18FSY9C573YU5XxSapdHZTZ2WolA=="], "jsdom/whatwg-mimetype": ["whatwg-mimetype@4.0.0", "", {}, "sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg=="], @@ -4411,28 +4380,16 @@ "@istanbuljs/load-nyc-config/js-yaml/argparse": ["argparse@1.0.10", "", { "dependencies": { "sprintf-js": "~1.0.2" } }, "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg=="], - "@jest/console/@types/node/undici-types": ["undici-types@6.21.0", "", {}, "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ=="], - "@jest/console/chalk/ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="], "@jest/console/chalk/supports-color": ["supports-color@7.2.0", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw=="], - "@jest/core/@types/node/undici-types": ["undici-types@6.21.0", "", {}, "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ=="], - "@jest/core/ansi-escapes/type-fest": ["type-fest@0.21.3", "", {}, "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w=="], "@jest/core/chalk/ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="], "@jest/core/chalk/supports-color": ["supports-color@7.2.0", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw=="], - "@jest/environment/@types/node/undici-types": ["undici-types@6.21.0", "", {}, "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ=="], - - "@jest/fake-timers/@types/node/undici-types": ["undici-types@6.21.0", "", {}, "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ=="], - - "@jest/pattern/@types/node/undici-types": ["undici-types@6.21.0", "", {}, "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ=="], - - "@jest/reporters/@types/node/undici-types": ["undici-types@6.21.0", "", {}, "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ=="], - "@jest/reporters/chalk/ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="], "@jest/reporters/chalk/supports-color": ["supports-color@7.2.0", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw=="], @@ -4455,8 +4412,6 @@ "@jest/transform/chalk/supports-color": ["supports-color@7.2.0", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw=="], - "@jest/types/@types/node/undici-types": ["undici-types@6.21.0", "", {}, "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ=="], - "@jest/types/chalk/ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="], "@jest/types/chalk/supports-color": ["supports-color@7.2.0", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw=="], @@ -4507,38 +4462,14 @@ "@testing-library/jest-dom/chalk/supports-color": ["supports-color@7.2.0", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw=="], - "@types/asn1/@types/node/undici-types": ["undici-types@6.21.0", "", {}, "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ=="], - - "@types/body-parser/@types/node/undici-types": ["undici-types@6.21.0", "", {}, "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ=="], - - "@types/connect/@types/node/undici-types": ["undici-types@6.21.0", "", {}, "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ=="], - - "@types/cors/@types/node/undici-types": ["undici-types@6.21.0", "", {}, "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ=="], - - "@types/express-serve-static-core/@types/node/undici-types": ["undici-types@6.21.0", "", {}, "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ=="], - - "@types/fs-extra/@types/node/undici-types": ["undici-types@6.21.0", "", {}, "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ=="], + "@types/cacheable-request/@types/node/undici-types": ["undici-types@7.16.0", "", {}, "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw=="], - "@types/jsdom/@types/node/undici-types": ["undici-types@6.21.0", "", {}, "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ=="], + "@types/keyv/@types/node/undici-types": ["undici-types@7.16.0", "", {}, "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw=="], - "@types/plist/@types/node/undici-types": ["undici-types@6.21.0", "", {}, "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ=="], - - "@types/send/@types/node/undici-types": ["undici-types@6.21.0", "", {}, "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ=="], - - "@types/serve-static/@types/node/undici-types": ["undici-types@6.21.0", "", {}, "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ=="], + "@types/responselike/@types/node/undici-types": ["undici-types@7.16.0", "", {}, "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw=="], "@types/ssh2/@types/node/undici-types": ["undici-types@5.26.5", "", {}, "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA=="], - "@types/sshpk/@types/node/undici-types": ["undici-types@6.21.0", "", {}, "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ=="], - - "@types/wait-on/@types/node/undici-types": ["undici-types@6.21.0", "", {}, "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ=="], - - "@types/write-file-atomic/@types/node/undici-types": ["undici-types@6.21.0", "", {}, "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ=="], - - "@types/ws/@types/node/undici-types": ["undici-types@6.21.0", "", {}, "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ=="], - - "@types/yauzl/@types/node/undici-types": ["undici-types@6.21.0", "", {}, "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ=="], - "@typescript-eslint/typescript-estree/minimatch/brace-expansion": ["brace-expansion@2.0.2", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ=="], "@vitest/expect/@vitest/utils/@vitest/pretty-format": ["@vitest/pretty-format@2.0.5", "", { "dependencies": { "tinyrainbow": "^1.2.0" } }, "sha512-h8k+1oWHfwTkyTkb9egzwNMfJAEx4veaPSnMeKbVSjp4euqGSbQlm5+6VHwTr7u4FJslVVsUG5nopCaAYdOmSQ=="], @@ -4573,8 +4504,6 @@ "builder-util/https-proxy-agent/agent-base": ["agent-base@6.0.2", "", { "dependencies": { "debug": "4" } }, "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ=="], - "bun-types/@types/node/undici-types": ["undici-types@6.21.0", "", {}, "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ=="], - "caching-transform/write-file-atomic/signal-exit": ["signal-exit@3.0.7", "", {}, "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ=="], "cliui/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="], @@ -4613,6 +4542,8 @@ "electron-publish/chalk/supports-color": ["supports-color@7.2.0", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw=="], + "electron/@types/node/undici-types": ["undici-types@7.16.0", "", {}, "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw=="], + "eslint/ajv/json-schema-traverse": ["json-schema-traverse@0.4.1", "", {}, "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg=="], "eslint/chalk/ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="], @@ -4633,14 +4564,10 @@ "global-prefix/which/isexe": ["isexe@2.0.0", "", {}, "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="], - "happy-dom/@types/node/undici-types": ["undici-types@6.21.0", "", {}, "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ=="], - "hosted-git-info/lru-cache/yallist": ["yallist@4.0.0", "", {}, "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="], "htmlparser2/readable-stream/string_decoder": ["string_decoder@1.3.0", "", { "dependencies": { "safe-buffer": "~5.2.0" } }, "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA=="], - "jest-circus/@types/node/undici-types": ["undici-types@6.21.0", "", {}, "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ=="], - "jest-circus/chalk/ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="], "jest-circus/chalk/supports-color": ["supports-color@7.2.0", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw=="], @@ -4669,10 +4596,6 @@ "jest-each/chalk/supports-color": ["supports-color@7.2.0", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw=="], - "jest-environment-node/@types/node/undici-types": ["undici-types@6.21.0", "", {}, "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ=="], - - "jest-haste-map/@types/node/undici-types": ["undici-types@6.21.0", "", {}, "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ=="], - "jest-junit/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="], "jest-matcher-utils/chalk/ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="], @@ -4683,8 +4606,6 @@ "jest-message-util/chalk/supports-color": ["supports-color@7.2.0", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw=="], - "jest-mock/@types/node/undici-types": ["undici-types@6.21.0", "", {}, "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ=="], - "jest-process-manager/chalk/ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="], "jest-process-manager/chalk/supports-color": ["supports-color@7.2.0", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw=="], @@ -4693,14 +4614,10 @@ "jest-resolve/chalk/supports-color": ["supports-color@7.2.0", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw=="], - "jest-runner/@types/node/undici-types": ["undici-types@6.21.0", "", {}, "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ=="], - "jest-runner/chalk/ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="], "jest-runner/chalk/supports-color": ["supports-color@7.2.0", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw=="], - "jest-runtime/@types/node/undici-types": ["undici-types@6.21.0", "", {}, "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ=="], - "jest-runtime/chalk/ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="], "jest-runtime/chalk/supports-color": ["supports-color@7.2.0", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw=="], @@ -4717,8 +4634,6 @@ "jest-snapshot/chalk/supports-color": ["supports-color@7.2.0", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw=="], - "jest-util/@types/node/undici-types": ["undici-types@6.21.0", "", {}, "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ=="], - "jest-util/chalk/ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="], "jest-util/chalk/supports-color": ["supports-color@7.2.0", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw=="], @@ -4727,8 +4642,6 @@ "jest-validate/chalk/supports-color": ["supports-color@7.2.0", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw=="], - "jest-watcher/@types/node/undici-types": ["undici-types@6.21.0", "", {}, "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ=="], - "jest-watcher/ansi-escapes/type-fest": ["type-fest@0.21.3", "", {}, "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w=="], "jest-watcher/chalk/ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="], @@ -4737,8 +4650,6 @@ "jest-watcher/string-length/strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="], - "jest-worker/@types/node/undici-types": ["undici-types@6.21.0", "", {}, "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ=="], - "jsdom/parse5/entities": ["entities@6.0.1", "", {}, "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g=="], "mlly/pkg-types/confbox": ["confbox@0.1.8", "", {}, "sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w=="], diff --git a/package.json b/package.json index f4be91c0e8..dd3609e941 100644 --- a/package.json +++ b/package.json @@ -154,7 +154,7 @@ "@babel/preset-env": "^7.28.5", "@babel/preset-react": "^7.28.5", "@babel/preset-typescript": "^7.28.5", - "@coder/pixel-storybook": "0.2.1", + "@coder/pixel-storybook": "0.2.2", "@electron/rebuild": "^4.0.4", "@eslint/js": "^9.36.0", "@playwright/test": "^1.56.0", diff --git a/src/browser/features/Messages/MessageRenderer.test.tsx b/src/browser/features/Messages/MessageRenderer.test.tsx index 50e6d32518..c10c287797 100644 --- a/src/browser/features/Messages/MessageRenderer.test.tsx +++ b/src/browser/features/Messages/MessageRenderer.test.tsx @@ -297,6 +297,25 @@ After the example remains visible. expect(report?.reportMarkdown).toContain("After the example remains visible."); }); + test("preserves title delimiter examples inside report titles", () => { + const report = parseSubagentReportEnvelope(` +task-title-delimiter +explore +completed +Checked the literal delimiter +without exposing the envelope + +The title parser uses the final protocol separator. + +`); + + expect(report).not.toBeNull(); + expect(report?.title).toBe( + "Checked the literal delimiter without exposing the envelope" + ); + expect(report?.reportMarkdown).toBe("The title parser uses the final protocol separator."); + }); + test("preserves leading indentation and trailing spaces in report markdown", () => { const report = parseSubagentReportEnvelope(` task-whitespace diff --git a/src/browser/features/Messages/SubagentReportMessageContent.tsx b/src/browser/features/Messages/SubagentReportMessageContent.tsx index e6c3b4e048..82cfe801e2 100644 --- a/src/browser/features/Messages/SubagentReportMessageContent.tsx +++ b/src/browser/features/Messages/SubagentReportMessageContent.tsx @@ -15,6 +15,7 @@ export interface SubagentReportEnvelope { const STRUCTURED_OUTPUT_START = "\n\n"; const STRUCTURED_OUTPUT_END = "\n"; +const TITLE_REPORT_SEPARATOR = "\n\n"; const REPORT_END = "\n"; function splitStructuredOutput(envelope: string): { @@ -61,12 +62,16 @@ export function parseSubagentReportEnvelope(content: string): SubagentReportEnve const { reportEnvelope, structuredOutput } = splitStructuredOutput(root[1]); if (!reportEnvelope.endsWith(REPORT_END)) return null; - // Parse from the fixed metadata prefix, then anchor the report close at the end. Report markdown - // is intentionally unescaped by TaskService, so it may itself contain protocol-looking examples - // such as a line containing ; only the final delimiter terminates the field. + // Parse delimiters from the end because TaskService stores arbitrary title and report strings + // without escaping. Embedded examples such as or are content unless + // they are the final protocol separator for their field. + const body = reportEnvelope.slice(0, -REPORT_END.length); + const reportStart = body.lastIndexOf(TITLE_REPORT_SEPARATOR); + if (reportStart === -1) return null; + const fields = - /^([^\n]*)<\/task_id>\n([^\n]*)<\/agent_type>\n(?:([^\n]*)<\/status>\n)?([\s\S]*?)<\/title>\n<report_markdown>\n([\s\S]*)$/.exec( - reportEnvelope.slice(0, -REPORT_END.length) + /^<task_id>([^\n]*)<\/task_id>\n<agent_type>([^\n]*)<\/agent_type>\n(?:<status>([^\n]*)<\/status>\n)?<title>([\s\S]*)$/.exec( + body.slice(0, reportStart) ); if (!fields) return null; @@ -74,7 +79,7 @@ export function parseSubagentReportEnvelope(content: string): SubagentReportEnve const agentType = fields[2]?.trim(); const rawStatus = fields[3]?.trim() || null; const title = fields[4]?.replace(/\s+/g, " ").trim(); - const reportMarkdown = fields[5]; + const reportMarkdown = body.slice(reportStart + TITLE_REPORT_SEPARATOR.length); // TaskService stores reportMarkdown verbatim between separator newlines. Preserve leading // indentation and trailing spaces because Markdown uses both for code blocks and hard breaks. diff --git a/src/browser/features/RightSidebar/Memory/MemoryTab.stories.tsx b/src/browser/features/RightSidebar/Memory/MemoryTab.stories.tsx index ba38a45bc6..f8ac048b52 100644 --- a/src/browser/features/RightSidebar/Memory/MemoryTab.stories.tsx +++ b/src/browser/features/RightSidebar/Memory/MemoryTab.stories.tsx @@ -4,7 +4,6 @@ import { userEvent, within } from "@storybook/test"; import { updatePersistedState } from "@/browser/hooks/usePersistedState"; import { APIProvider } from "@/browser/contexts/API"; import { createMockORPCClient } from "@/browser/stories/mocks/orpc"; -import { blurActiveElement } from "@/browser/stories/storyPlayHelpers.js"; import type { MemoryConsolidationRecordPayload, MemoryFileInfo, @@ -127,14 +126,6 @@ function renderTab(width: string) { } export const List: Story = { - tags: ["pixel-stability"], - parameters: { - pixel: { - // This duplicates the same tree permutations covered by Settings/MemorySection.WithFiles, - // while Chromium rasterizes its nested rounded borders/chevrons nondeterministically. - exclude: true, - }, - }, render: () => renderTab("360px"), play: async ({ canvasElement }) => { const canvas = within(canvasElement); @@ -146,16 +137,11 @@ export const List: Story = { }; export const Editor: Story = { - tags: ["pixel-stability"], render: () => renderTab("360px"), play: async ({ canvasElement }) => { const canvas = within(canvasElement); const row = await canvas.findByText("preferences.md"); await userEvent.click(row); await canvas.findByLabelText("Memory file content"); - // The click target disappears when the editor opens, so explicitly move the virtual pointer - // off whatever control replaced it and hide the focused textarea caret before Pixel captures. - await userEvent.hover(canvasElement); - blurActiveElement(); }, }; diff --git a/src/browser/features/Settings/Sections/MCPSettingsSection.stories.tsx b/src/browser/features/Settings/Sections/MCPSettingsSection.stories.tsx index 365f5c2b93..7df8147321 100644 --- a/src/browser/features/Settings/Sections/MCPSettingsSection.stories.tsx +++ b/src/browser/features/Settings/Sections/MCPSettingsSection.stories.tsx @@ -10,7 +10,6 @@ import { PolicyProvider } from "@/browser/contexts/PolicyContext"; import { ThemeProvider } from "@/browser/contexts/ThemeContext"; import { updatePersistedState } from "@/browser/hooks/usePersistedState"; import { createMockORPCClient } from "@/browser/stories/mocks/orpc"; -import { blurActiveElement } from "@/browser/stories/storyPlayHelpers.js"; import { getMCPTestResultsKey } from "@/common/constants/storage"; import type { MCPServerInfo } from "@/common/types/mcp"; import type { MCPOAuthAuthStatus } from "@/common/types/mcpOauth"; @@ -171,14 +170,6 @@ export const ProjectSettingsEmpty: Story = { }; export const ProjectSettingsAddRemoteServerHeaders: Story = { - tags: ["pixel-stability"], - parameters: { - pixel: { - // Keep the interaction contract in Storybook tests, but exclude the screenshot: Chromium - // alternates the rounded form border's corner antialiasing by one shade between captures. - exclude: true, - }, - }, render: () => ( <MCPSettingsSectionStoryShell setup={() => @@ -241,9 +232,6 @@ export const ProjectSettingsAddRemoteServerHeaders: Story = { await expect(body.findByDisplayValue("MCP_TOKEN")).resolves.toBeInTheDocument(); await expect(body.findByDisplayValue("X-Env")).resolves.toBeInTheDocument(); await expect(body.findByDisplayValue("prod")).resolves.toBeInTheDocument(); - // Do not snapshot a focused input's platform-rendered caret/border antialiasing. - await userEvent.hover(canvasElement); - blurActiveElement(); }, }; diff --git a/src/browser/features/Tools/AttachFileToolCall.stories.tsx b/src/browser/features/Tools/AttachFileToolCall.stories.tsx index bacba6ecfb..b32c305a26 100644 --- a/src/browser/features/Tools/AttachFileToolCall.stories.tsx +++ b/src/browser/features/Tools/AttachFileToolCall.stories.tsx @@ -54,7 +54,6 @@ function GallerySection(props: { label: string; children: ReactNode }) { // (image, video, audio, markdown, generic file) into a single snapshot to keep // the snapshot budget low while preserving every distinct visual state. export const Gallery: Story = { - tags: ["pixel-stability"], parameters: { pixel: { // Chromium's native media controls contain an internal loading spinner whose frame is not From 4ad3051047fbd0d09e30ead323d1fd1089a18bbb Mon Sep 17 00:00:00 2001 From: Ammar <ammar+ai@ammar.io> Date: Wed, 22 Jul 2026 11:10:52 -0500 Subject: [PATCH 07/13] =?UTF-8?q?=F0=9F=A4=96=20fix:=20expose=20report=20s?= =?UTF-8?q?tories=20clearly=20in=20Pixel?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Keep Pixel 0.2.1 to avoid repository-wide display-name churn, but split the new report coverage into uniquely titled desktop and phone stories so both appear clearly in visual review. _Generated with `mux` • Model: `openai:gpt-5.6-sol` • Thinking: `xhigh` • Cost: `$183.66`_ <!-- mux-attribution: model=openai:gpt-5.6-sol thinking=xhigh costs=183.66 --> --- bun.lock | 4 +- package.json | 2 +- .../App.subagentReportsDesktop.stories.tsx | 23 ++++++++ .../App.subagentReportsPhone.stories.tsx | 29 ++++++++++ .../subagentReportStory.tsx} | 54 +++---------------- 5 files changed, 61 insertions(+), 51 deletions(-) create mode 100644 src/browser/stories/App.subagentReportsDesktop.stories.tsx create mode 100644 src/browser/stories/App.subagentReportsPhone.stories.tsx rename src/browser/stories/{App.subagentReports.stories.tsx => helpers/subagentReportStory.tsx} (56%) diff --git a/bun.lock b/bun.lock index 4c58796093..47c78dcbaa 100644 --- a/bun.lock +++ b/bun.lock @@ -112,7 +112,7 @@ "@babel/preset-env": "^7.28.5", "@babel/preset-react": "^7.28.5", "@babel/preset-typescript": "^7.28.5", - "@coder/pixel-storybook": "0.2.2", + "@coder/pixel-storybook": "0.2.1", "@electron/rebuild": "^4.0.4", "@eslint/js": "^9.36.0", "@playwright/test": "^1.56.0", @@ -564,7 +564,7 @@ "@chevrotain/utils": ["@chevrotain/utils@11.0.3", "", {}, "sha512-YslZMgtJUyuMbZ+aKvfF3x1f5liK4mWNxghFRv7jqRR9C3R3fAOGTTKvxXDa2Y1s9zSbcpuO0cAxDYsc9SrXoQ=="], - "@coder/pixel-storybook": ["@coder/pixel-storybook@0.2.2", "", { "dependencies": { "get-port-please": "3.1.2", "jsonc-parser": "^3.3.1", "serve-handler": "6.1.7", "zod": "3.23.8" }, "peerDependencies": { "playwright-core": ">=1.47.2", "storybook": ">=8.0.0" }, "bin": { "pixel-storybook": "build/bin.js" } }, "sha512-f1dJ7FiNCdoT9VRVH6IWFnCG3VMnavtvTx/r6sZ4hzhj/rJEZv4P69J2x4XdYaMAwi+O5xbp2UQf0dNf2U9lLA=="], + "@coder/pixel-storybook": ["@coder/pixel-storybook@0.2.1", "", { "dependencies": { "get-port-please": "3.1.2", "jsonc-parser": "^3.3.1", "serve-handler": "6.1.7", "zod": "3.23.8" }, "peerDependencies": { "playwright-core": ">=1.47.2", "storybook": ">=8.0.0" }, "bin": { "pixel-storybook": "build/bin.js" } }, "sha512-o4EIY5JcLr+U/URGwfTQ1s0rs2jp4CfdOG94kHABLVD6wFEwUDyl52LX8utx6ArOkxuixbe3NMUTdxWU33fqLw=="], "@csstools/color-helpers": ["@csstools/color-helpers@5.1.0", "", {}, "sha512-S11EXWJyy0Mz5SYvRmY8nJYTFFd1LCNV+7cXyAgQtOOuzb4EsgfqDufL+9esx72/eLhsRdGZwaldu/h+E4t4BA=="], diff --git a/package.json b/package.json index dd3609e941..f4be91c0e8 100644 --- a/package.json +++ b/package.json @@ -154,7 +154,7 @@ "@babel/preset-env": "^7.28.5", "@babel/preset-react": "^7.28.5", "@babel/preset-typescript": "^7.28.5", - "@coder/pixel-storybook": "0.2.2", + "@coder/pixel-storybook": "0.2.1", "@electron/rebuild": "^4.0.4", "@eslint/js": "^9.36.0", "@playwright/test": "^1.56.0", diff --git a/src/browser/stories/App.subagentReportsDesktop.stories.tsx b/src/browser/stories/App.subagentReportsDesktop.stories.tsx new file mode 100644 index 0000000000..956a4c84d2 --- /dev/null +++ b/src/browser/stories/App.subagentReportsDesktop.stories.tsx @@ -0,0 +1,23 @@ +/** Full-app visual coverage for sub-agent progress and terminal report presentation. */ + +import { appMeta, AppWithMocks, PIXEL_DUAL_THEME, type AppStory } from "./meta.js"; +import { setupSubagentReportStory } from "./helpers/subagentReportStory"; + +export default { + ...appMeta, + title: "App/SubagentReports/Desktop", +}; + +/** Incremental, completed legacy, and structured sub-agent report states. */ +export const Preview: AppStory = { + // Pixel owns this visual-only desktop matrix. The full App cold-start can exceed the + // Storybook test-runner's 30-second smoke timeout before any assertions begin. + tags: ["!test"], + render: () => <AppWithMocks setup={setupSubagentReportStory} />, + parameters: { + ...appMeta.parameters, + pixel: { matrix: PIXEL_DUAL_THEME }, + }, + // Pixel captures the complete desktop composition. Production behavior is covered by + // MessageRenderer.test.tsx because the full App can exceed the Storybook smoke-test budget. +}; diff --git a/src/browser/stories/App.subagentReportsPhone.stories.tsx b/src/browser/stories/App.subagentReportsPhone.stories.tsx new file mode 100644 index 0000000000..a711dc5d08 --- /dev/null +++ b/src/browser/stories/App.subagentReportsPhone.stories.tsx @@ -0,0 +1,29 @@ +/** Phone-width visual coverage for sub-agent report presentation. */ + +import { appMeta, AppWithMocks, type AppStory } from "./meta.js"; +import { + PhoneSubagentReportDecorator, + setupSubagentReportStory, +} from "./helpers/subagentReportStory"; + +export default { + ...appMeta, + title: "App/SubagentReports/Phone", +}; + +export const Preview: AppStory = { + // The fixed-width decorator + pinned Pixel phone viewport are the static breakpoint contract. + // Production rendering behavior is covered by MessageRenderer.test.tsx. + tags: ["!test"], + globals: { + viewport: { value: "mobile1", isRotated: false }, + }, + render: () => <AppWithMocks setup={setupSubagentReportStory} />, + decorators: [PhoneSubagentReportDecorator], + parameters: { + ...appMeta.parameters, + pixel: { + matrix: { themes: ["dark", "light"], viewports: ["phone"] }, + }, + }, +}; diff --git a/src/browser/stories/App.subagentReports.stories.tsx b/src/browser/stories/helpers/subagentReportStory.tsx similarity index 56% rename from src/browser/stories/App.subagentReports.stories.tsx rename to src/browser/stories/helpers/subagentReportStory.tsx index dd4b0caa55..2ee06e8f88 100644 --- a/src/browser/stories/App.subagentReports.stories.tsx +++ b/src/browser/stories/helpers/subagentReportStory.tsx @@ -1,21 +1,13 @@ -/** Full-app visual coverage for sub-agent progress and terminal report presentation. */ - import type { ComponentType } from "react"; -import { appMeta, AppWithMocks, PIXEL_DUAL_THEME, type AppStory } from "./meta.js"; -import { setupSimpleChatStory } from "./helpers/chatSetup"; -import { collapseLeftSidebar, collapseRightSidebar } from "./helpers/uiState"; +import { setupSimpleChatStory } from "./chatSetup"; +import { collapseLeftSidebar, collapseRightSidebar } from "./uiState"; import { createAssistantMessage, createSubagentReportMessage, createUserMessage, -} from "./mocks/messages"; -import { STABLE_TIMESTAMP } from "./mocks/workspaces"; - -export default { - ...appMeta, - title: "App/SubagentReports", -}; +} from "../mocks/messages"; +import { STABLE_TIMESTAMP } from "../mocks/workspaces"; const REPORT_MESSAGES = [ createUserMessage("report-user", "Review the message rendering path and flag any UX gaps.", { @@ -71,7 +63,7 @@ const REPORT_MESSAGES = [ ), ] as const; -function setupReportStory() { +export function setupSubagentReportStory() { collapseLeftSidebar(); collapseRightSidebar(); return setupSimpleChatStory({ @@ -82,44 +74,10 @@ function setupReportStory() { }); } -function PhoneDecorator(Story: ComponentType) { +export function PhoneSubagentReportDecorator(Story: ComponentType) { return ( <div style={{ width: 390, height: 844, overflow: "hidden" }}> <Story /> </div> ); } - -/** Incremental, completed legacy, and structured sub-agent report states. */ -export const Desktop: AppStory = { - // Pixel owns this visual-only desktop matrix. The full App cold-start can exceed the - // Storybook test-runner's 30-second smoke timeout before any assertions begin. - tags: ["!test"], - render: () => <AppWithMocks setup={setupReportStory} />, - parameters: { - ...appMeta.parameters, - pixel: { matrix: PIXEL_DUAL_THEME }, - }, - // Pixel captures the complete desktop composition. Keep interaction assertions on the pinned - // phone story below because the Storybook test-runner's cold desktop app load can exhaust its - // per-story timeout before a desktop play function begins, while production behavior is covered - // by MessageRenderer.test.tsx. -}; - -/** Phone-width contract for long titles, markdown paths, and structured-output controls. */ -export const Phone: AppStory = { - // The fixed-width decorator + pinned Pixel phone viewport are the static breakpoint contract. - // Production rendering behavior is covered by MessageRenderer.test.tsx. - tags: ["!test"], - globals: { - viewport: { value: "mobile1", isRotated: false }, - }, - render: () => <AppWithMocks setup={setupReportStory} />, - decorators: [PhoneDecorator], - parameters: { - ...appMeta.parameters, - pixel: { - matrix: { themes: ["dark", "light"], viewports: ["phone"] }, - }, - }, -}; From 8cb7413509999e2ad039b4e5a88fbbe544ab8cf1 Mon Sep 17 00:00:00 2001 From: Ammar <ammar+ai@ammar.io> Date: Wed, 22 Jul 2026 11:14:29 -0500 Subject: [PATCH 08/13] =?UTF-8?q?=F0=9F=A4=96=20chore:=20align=20lockfile?= =?UTF-8?q?=20with=20latest=20main?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Drop the stale pre-rebase lockfile delta after rebasing the report presentation branch. _Generated with `mux` • Model: `openai:gpt-5.6-sol` • Thinking: `xhigh` • Cost: `$183.66`_ <!-- mux-attribution: model=openai:gpt-5.6-sol thinking=xhigh costs=183.66 --> --- bun.lock | 119 ++++++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 104 insertions(+), 15 deletions(-) diff --git a/bun.lock b/bun.lock index 47c78dcbaa..9306bc5cbb 100644 --- a/bun.lock +++ b/bun.lock @@ -1,6 +1,5 @@ { "lockfileVersion": 1, - "configVersion": 0, "workspaces": { "": { "name": "mux", @@ -1456,7 +1455,7 @@ "@types/ms": ["@types/ms@2.1.0", "", {}, "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA=="], - "@types/node": ["@types/node@20.19.25", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-ZsJzA5thDQMSQO788d7IocwwQbI8B5OPzmqNvpf3NY/+MHDAS759Wo0gd2WQeXYt5AAAQjzcrTVC6SKCuYgoCQ=="], + "@types/node": ["@types/node@24.12.2", "", { "dependencies": { "undici-types": "~7.16.0" } }, "sha512-A1sre26ke7HDIuY/M23nd9gfB+nrmhtYyMINbjI1zHJxYteKR6qSMX56FsmjMcDb3SMcjJg5BiRRgOCC/yBD0g=="], "@types/plist": ["@types/plist@3.0.5", "", { "dependencies": { "@types/node": "*", "xmlbuilder": ">=11.0.1" } }, "sha512-E6OCaRmAe4WDmWNsL/9RMqdkkzDCY1etutkflWk4c+AcjDU07Pcz1fQwTX0TQz+Pxqn9i4L1TU3UFpjnrcDgxA=="], @@ -3620,7 +3619,7 @@ "undici": ["undici@7.16.0", "", {}, "sha512-QEg3HPMll0o3t2ourKwOeUAZ159Kn9mx5pnzHRQO8+Wixmh88YdZRiIwat0iNzNNXn0yoEtXJqFpyW7eM8BV7g=="], - "undici-types": ["undici-types@6.21.0", "", {}, "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ=="], + "undici-types": ["undici-types@7.16.0", "", {}, "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw=="], "unicode-canonical-property-names-ecmascript": ["unicode-canonical-property-names-ecmascript@2.0.1", "", {}, "sha512-dA8WbNeb2a6oQzAQ55YlT5vQAWGV9WXOsi3SskE3bcCdM0P4SDd+24zS/OCacdRq5BkdsRj9q3Pg6YyQoxIGqg=="], @@ -3858,14 +3857,26 @@ "@istanbuljs/load-nyc-config/js-yaml": ["js-yaml@3.14.2", "", { "dependencies": { "argparse": "^1.0.7", "esprima": "^4.0.0" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-PMSmkqxr106Xa156c2M265Z+FTrPl+oxd/rgOQy2tijQeK5TxQ43psO1ZCwhVOSdnn+RzkzlRz/eY4BgJBYVpg=="], + "@jest/console/@types/node": ["@types/node@20.19.25", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-ZsJzA5thDQMSQO788d7IocwwQbI8B5OPzmqNvpf3NY/+MHDAS759Wo0gd2WQeXYt5AAAQjzcrTVC6SKCuYgoCQ=="], + "@jest/console/chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="], + "@jest/core/@types/node": ["@types/node@20.19.25", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-ZsJzA5thDQMSQO788d7IocwwQbI8B5OPzmqNvpf3NY/+MHDAS759Wo0gd2WQeXYt5AAAQjzcrTVC6SKCuYgoCQ=="], + "@jest/core/ansi-escapes": ["ansi-escapes@4.3.2", "", { "dependencies": { "type-fest": "^0.21.3" } }, "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ=="], "@jest/core/chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="], "@jest/core/ci-info": ["ci-info@4.3.1", "", {}, "sha512-Wdy2Igu8OcBpI2pZePZ5oWjPC38tmDVx5WKUXKwlLYkA0ozo85sLsLvkBbBn/sZaSCMFOGZJ14fvW9t5/d7kdA=="], + "@jest/environment/@types/node": ["@types/node@20.19.25", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-ZsJzA5thDQMSQO788d7IocwwQbI8B5OPzmqNvpf3NY/+MHDAS759Wo0gd2WQeXYt5AAAQjzcrTVC6SKCuYgoCQ=="], + + "@jest/fake-timers/@types/node": ["@types/node@20.19.25", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-ZsJzA5thDQMSQO788d7IocwwQbI8B5OPzmqNvpf3NY/+MHDAS759Wo0gd2WQeXYt5AAAQjzcrTVC6SKCuYgoCQ=="], + + "@jest/pattern/@types/node": ["@types/node@20.19.25", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-ZsJzA5thDQMSQO788d7IocwwQbI8B5OPzmqNvpf3NY/+MHDAS759Wo0gd2WQeXYt5AAAQjzcrTVC6SKCuYgoCQ=="], + + "@jest/reporters/@types/node": ["@types/node@20.19.25", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-ZsJzA5thDQMSQO788d7IocwwQbI8B5OPzmqNvpf3NY/+MHDAS759Wo0gd2WQeXYt5AAAQjzcrTVC6SKCuYgoCQ=="], + "@jest/reporters/chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="], "@jest/reporters/glob": ["glob@10.5.0", "", { "dependencies": { "foreground-child": "^3.1.0", "jackspeak": "^3.1.2", "minimatch": "^9.0.4", "minipass": "^7.1.2", "package-json-from-dist": "^1.0.0", "path-scurry": "^1.11.1" }, "bin": { "glob": "dist/esm/bin.mjs" } }, "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg=="], @@ -3882,6 +3893,8 @@ "@jest/transform/write-file-atomic": ["write-file-atomic@5.0.1", "", { "dependencies": { "imurmurhash": "^0.1.4", "signal-exit": "^4.0.1" } }, "sha512-+QU2zd6OTD8XWIJCbffaiQeH9U73qIqafo1x6V1snCWYGJf6cVE0cDR4D8xRzcEnfI21IFrUPzPGtcPf8AC+Rw=="], + "@jest/types/@types/node": ["@types/node@20.19.25", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-ZsJzA5thDQMSQO788d7IocwwQbI8B5OPzmqNvpf3NY/+MHDAS759Wo0gd2WQeXYt5AAAQjzcrTVC6SKCuYgoCQ=="], + "@jest/types/chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="], "@malept/flatpak-bundler/fs-extra": ["fs-extra@9.1.0", "", { "dependencies": { "at-least-node": "^1.0.0", "graceful-fs": "^4.2.0", "jsonfile": "^6.0.1", "universalify": "^2.0.0" } }, "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ=="], @@ -3986,17 +3999,17 @@ "@types/body-parser/@types/node": ["@types/node@22.19.1", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-LCCV0HdSZZZb34qifBsyWlUmok6W7ouER+oQIGBScS8EsZsQbrtFTUrDX4hOl+CS6p7cnNC4td+qrSVGSCTUfQ=="], - "@types/cacheable-request/@types/node": ["@types/node@24.12.2", "", { "dependencies": { "undici-types": "~7.16.0" } }, "sha512-A1sre26ke7HDIuY/M23nd9gfB+nrmhtYyMINbjI1zHJxYteKR6qSMX56FsmjMcDb3SMcjJg5BiRRgOCC/yBD0g=="], - "@types/connect/@types/node": ["@types/node@22.19.1", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-LCCV0HdSZZZb34qifBsyWlUmok6W7ouER+oQIGBScS8EsZsQbrtFTUrDX4hOl+CS6p7cnNC4td+qrSVGSCTUfQ=="], + "@types/cors/@types/node": ["@types/node@20.19.25", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-ZsJzA5thDQMSQO788d7IocwwQbI8B5OPzmqNvpf3NY/+MHDAS759Wo0gd2WQeXYt5AAAQjzcrTVC6SKCuYgoCQ=="], + "@types/express-serve-static-core/@types/node": ["@types/node@22.19.1", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-LCCV0HdSZZZb34qifBsyWlUmok6W7ouER+oQIGBScS8EsZsQbrtFTUrDX4hOl+CS6p7cnNC4td+qrSVGSCTUfQ=="], - "@types/keyv/@types/node": ["@types/node@24.12.2", "", { "dependencies": { "undici-types": "~7.16.0" } }, "sha512-A1sre26ke7HDIuY/M23nd9gfB+nrmhtYyMINbjI1zHJxYteKR6qSMX56FsmjMcDb3SMcjJg5BiRRgOCC/yBD0g=="], + "@types/fs-extra/@types/node": ["@types/node@20.19.25", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-ZsJzA5thDQMSQO788d7IocwwQbI8B5OPzmqNvpf3NY/+MHDAS759Wo0gd2WQeXYt5AAAQjzcrTVC6SKCuYgoCQ=="], - "@types/plist/@types/node": ["@types/node@22.19.1", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-LCCV0HdSZZZb34qifBsyWlUmok6W7ouER+oQIGBScS8EsZsQbrtFTUrDX4hOl+CS6p7cnNC4td+qrSVGSCTUfQ=="], + "@types/jsdom/@types/node": ["@types/node@20.19.25", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-ZsJzA5thDQMSQO788d7IocwwQbI8B5OPzmqNvpf3NY/+MHDAS759Wo0gd2WQeXYt5AAAQjzcrTVC6SKCuYgoCQ=="], - "@types/responselike/@types/node": ["@types/node@24.12.2", "", { "dependencies": { "undici-types": "~7.16.0" } }, "sha512-A1sre26ke7HDIuY/M23nd9gfB+nrmhtYyMINbjI1zHJxYteKR6qSMX56FsmjMcDb3SMcjJg5BiRRgOCC/yBD0g=="], + "@types/plist/@types/node": ["@types/node@22.19.1", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-LCCV0HdSZZZb34qifBsyWlUmok6W7ouER+oQIGBScS8EsZsQbrtFTUrDX4hOl+CS6p7cnNC4td+qrSVGSCTUfQ=="], "@types/send/@types/node": ["@types/node@22.19.1", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-LCCV0HdSZZZb34qifBsyWlUmok6W7ouER+oQIGBScS8EsZsQbrtFTUrDX4hOl+CS6p7cnNC4td+qrSVGSCTUfQ=="], @@ -4008,6 +4021,10 @@ "@types/wait-on/@types/node": ["@types/node@22.19.1", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-LCCV0HdSZZZb34qifBsyWlUmok6W7ouER+oQIGBScS8EsZsQbrtFTUrDX4hOl+CS6p7cnNC4td+qrSVGSCTUfQ=="], + "@types/write-file-atomic/@types/node": ["@types/node@20.19.25", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-ZsJzA5thDQMSQO788d7IocwwQbI8B5OPzmqNvpf3NY/+MHDAS759Wo0gd2WQeXYt5AAAQjzcrTVC6SKCuYgoCQ=="], + + "@types/ws/@types/node": ["@types/node@20.19.25", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-ZsJzA5thDQMSQO788d7IocwwQbI8B5OPzmqNvpf3NY/+MHDAS759Wo0gd2WQeXYt5AAAQjzcrTVC6SKCuYgoCQ=="], + "@types/yauzl/@types/node": ["@types/node@22.19.1", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-LCCV0HdSZZZb34qifBsyWlUmok6W7ouER+oQIGBScS8EsZsQbrtFTUrDX4hOl+CS6p7cnNC4td+qrSVGSCTUfQ=="], "@typescript-eslint/typescript-estree/minimatch": ["minimatch@9.0.5", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow=="], @@ -4044,6 +4061,8 @@ "builder-util/https-proxy-agent": ["https-proxy-agent@5.0.1", "", { "dependencies": { "agent-base": "6", "debug": "4" } }, "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA=="], + "bun-types/@types/node": ["@types/node@20.19.25", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-ZsJzA5thDQMSQO788d7IocwwQbI8B5OPzmqNvpf3NY/+MHDAS759Wo0gd2WQeXYt5AAAQjzcrTVC6SKCuYgoCQ=="], + "caching-transform/write-file-atomic": ["write-file-atomic@3.0.3", "", { "dependencies": { "imurmurhash": "^0.1.4", "is-typedarray": "^1.0.0", "signal-exit": "^3.0.2", "typedarray-to-buffer": "^3.1.5" } }, "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q=="], "chokidar/fsevents": ["fsevents@2.3.3", "", { "os": "darwin" }, "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="], @@ -4082,8 +4101,6 @@ "dom-serializer/entities": ["entities@2.2.0", "", {}, "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A=="], - "electron/@types/node": ["@types/node@24.12.2", "", { "dependencies": { "undici-types": "~7.16.0" } }, "sha512-A1sre26ke7HDIuY/M23nd9gfB+nrmhtYyMINbjI1zHJxYteKR6qSMX56FsmjMcDb3SMcjJg5BiRRgOCC/yBD0g=="], - "electron-builder/chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="], "electron-publish/chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="], @@ -4132,6 +4149,8 @@ "globby/ignore": ["ignore@5.3.2", "", {}, "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g=="], + "happy-dom/@types/node": ["@types/node@20.19.25", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-ZsJzA5thDQMSQO788d7IocwwQbI8B5OPzmqNvpf3NY/+MHDAS759Wo0gd2WQeXYt5AAAQjzcrTVC6SKCuYgoCQ=="], + "hasha/type-fest": ["type-fest@0.8.1", "", {}, "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA=="], "hast-util-to-parse5/property-information": ["property-information@6.5.0", "", {}, "sha512-PgTgs/BlvHxOu8QuEN7wi5A0OmXaBcHpmCSTehcs6Uuu9IkDIEo13Hy7n898RHfrQ49vKCoGeWZSaAK01nwVig=="], @@ -4172,6 +4191,8 @@ "jest-environment-node/@types/node": ["@types/node@22.19.1", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-LCCV0HdSZZZb34qifBsyWlUmok6W7ouER+oQIGBScS8EsZsQbrtFTUrDX4hOl+CS6p7cnNC4td+qrSVGSCTUfQ=="], + "jest-haste-map/@types/node": ["@types/node@20.19.25", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-ZsJzA5thDQMSQO788d7IocwwQbI8B5OPzmqNvpf3NY/+MHDAS759Wo0gd2WQeXYt5AAAQjzcrTVC6SKCuYgoCQ=="], + "jest-haste-map/fsevents": ["fsevents@2.3.3", "", { "os": "darwin" }, "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="], "jest-junit/mkdirp": ["mkdirp@1.0.4", "", { "bin": { "mkdirp": "bin/cmd.js" } }, "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw=="], @@ -4182,6 +4203,8 @@ "jest-message-util/chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="], + "jest-mock/@types/node": ["@types/node@20.19.25", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-ZsJzA5thDQMSQO788d7IocwwQbI8B5OPzmqNvpf3NY/+MHDAS759Wo0gd2WQeXYt5AAAQjzcrTVC6SKCuYgoCQ=="], + "jest-process-manager/chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="], "jest-process-manager/signal-exit": ["signal-exit@3.0.7", "", {}, "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ=="], @@ -4194,6 +4217,8 @@ "jest-runner/source-map-support": ["source-map-support@0.5.13", "", { "dependencies": { "buffer-from": "^1.0.0", "source-map": "^0.6.0" } }, "sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w=="], + "jest-runtime/@types/node": ["@types/node@20.19.25", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-ZsJzA5thDQMSQO788d7IocwwQbI8B5OPzmqNvpf3NY/+MHDAS759Wo0gd2WQeXYt5AAAQjzcrTVC6SKCuYgoCQ=="], + "jest-runtime/chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="], "jest-runtime/glob": ["glob@10.5.0", "", { "dependencies": { "foreground-child": "^3.1.0", "jackspeak": "^3.1.2", "minimatch": "^9.0.4", "minipass": "^7.1.2", "package-json-from-dist": "^1.0.0", "path-scurry": "^1.11.1" }, "bin": { "glob": "dist/esm/bin.mjs" } }, "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg=="], @@ -4202,6 +4227,8 @@ "jest-snapshot/chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="], + "jest-util/@types/node": ["@types/node@20.19.25", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-ZsJzA5thDQMSQO788d7IocwwQbI8B5OPzmqNvpf3NY/+MHDAS759Wo0gd2WQeXYt5AAAQjzcrTVC6SKCuYgoCQ=="], + "jest-util/chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="], "jest-util/ci-info": ["ci-info@4.3.1", "", {}, "sha512-Wdy2Igu8OcBpI2pZePZ5oWjPC38tmDVx5WKUXKwlLYkA0ozo85sLsLvkBbBn/sZaSCMFOGZJ14fvW9t5/d7kdA=="], @@ -4210,12 +4237,16 @@ "jest-watch-typeahead/slash": ["slash@5.1.0", "", {}, "sha512-ZA6oR3T/pEyuqwMgAKT0/hAv8oAXckzbkmR0UkUosQ+Mc4RxGoJkRmwHgHufaenlyAgE1Mxgpdcrf75y6XcnDg=="], + "jest-watcher/@types/node": ["@types/node@20.19.25", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-ZsJzA5thDQMSQO788d7IocwwQbI8B5OPzmqNvpf3NY/+MHDAS759Wo0gd2WQeXYt5AAAQjzcrTVC6SKCuYgoCQ=="], + "jest-watcher/ansi-escapes": ["ansi-escapes@4.3.2", "", { "dependencies": { "type-fest": "^0.21.3" } }, "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ=="], "jest-watcher/chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="], "jest-watcher/string-length": ["string-length@4.0.2", "", { "dependencies": { "char-regex": "^1.0.2", "strip-ansi": "^6.0.0" } }, "sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ=="], + "jest-worker/@types/node": ["@types/node@20.19.25", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-ZsJzA5thDQMSQO788d7IocwwQbI8B5OPzmqNvpf3NY/+MHDAS759Wo0gd2WQeXYt5AAAQjzcrTVC6SKCuYgoCQ=="], + "jsdom/parse5": ["parse5@8.0.0", "", { "dependencies": { "entities": "^6.0.0" } }, "sha512-9m4m5GSgXjL4AjumKzq1Fgfp3Z8rsvjRNbnkVwfu2ImRqE5D0LnY2QfDen18FSY9C573YU5XxSapdHZTZ2WolA=="], "jsdom/whatwg-mimetype": ["whatwg-mimetype@4.0.0", "", {}, "sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg=="], @@ -4380,16 +4411,28 @@ "@istanbuljs/load-nyc-config/js-yaml/argparse": ["argparse@1.0.10", "", { "dependencies": { "sprintf-js": "~1.0.2" } }, "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg=="], + "@jest/console/@types/node/undici-types": ["undici-types@6.21.0", "", {}, "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ=="], + "@jest/console/chalk/ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="], "@jest/console/chalk/supports-color": ["supports-color@7.2.0", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw=="], + "@jest/core/@types/node/undici-types": ["undici-types@6.21.0", "", {}, "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ=="], + "@jest/core/ansi-escapes/type-fest": ["type-fest@0.21.3", "", {}, "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w=="], "@jest/core/chalk/ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="], "@jest/core/chalk/supports-color": ["supports-color@7.2.0", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw=="], + "@jest/environment/@types/node/undici-types": ["undici-types@6.21.0", "", {}, "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ=="], + + "@jest/fake-timers/@types/node/undici-types": ["undici-types@6.21.0", "", {}, "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ=="], + + "@jest/pattern/@types/node/undici-types": ["undici-types@6.21.0", "", {}, "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ=="], + + "@jest/reporters/@types/node/undici-types": ["undici-types@6.21.0", "", {}, "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ=="], + "@jest/reporters/chalk/ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="], "@jest/reporters/chalk/supports-color": ["supports-color@7.2.0", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw=="], @@ -4412,6 +4455,8 @@ "@jest/transform/chalk/supports-color": ["supports-color@7.2.0", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw=="], + "@jest/types/@types/node/undici-types": ["undici-types@6.21.0", "", {}, "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ=="], + "@jest/types/chalk/ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="], "@jest/types/chalk/supports-color": ["supports-color@7.2.0", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw=="], @@ -4462,14 +4507,38 @@ "@testing-library/jest-dom/chalk/supports-color": ["supports-color@7.2.0", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw=="], - "@types/cacheable-request/@types/node/undici-types": ["undici-types@7.16.0", "", {}, "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw=="], + "@types/asn1/@types/node/undici-types": ["undici-types@6.21.0", "", {}, "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ=="], + + "@types/body-parser/@types/node/undici-types": ["undici-types@6.21.0", "", {}, "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ=="], + + "@types/connect/@types/node/undici-types": ["undici-types@6.21.0", "", {}, "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ=="], + + "@types/cors/@types/node/undici-types": ["undici-types@6.21.0", "", {}, "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ=="], + + "@types/express-serve-static-core/@types/node/undici-types": ["undici-types@6.21.0", "", {}, "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ=="], + + "@types/fs-extra/@types/node/undici-types": ["undici-types@6.21.0", "", {}, "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ=="], - "@types/keyv/@types/node/undici-types": ["undici-types@7.16.0", "", {}, "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw=="], + "@types/jsdom/@types/node/undici-types": ["undici-types@6.21.0", "", {}, "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ=="], - "@types/responselike/@types/node/undici-types": ["undici-types@7.16.0", "", {}, "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw=="], + "@types/plist/@types/node/undici-types": ["undici-types@6.21.0", "", {}, "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ=="], + + "@types/send/@types/node/undici-types": ["undici-types@6.21.0", "", {}, "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ=="], + + "@types/serve-static/@types/node/undici-types": ["undici-types@6.21.0", "", {}, "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ=="], "@types/ssh2/@types/node/undici-types": ["undici-types@5.26.5", "", {}, "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA=="], + "@types/sshpk/@types/node/undici-types": ["undici-types@6.21.0", "", {}, "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ=="], + + "@types/wait-on/@types/node/undici-types": ["undici-types@6.21.0", "", {}, "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ=="], + + "@types/write-file-atomic/@types/node/undici-types": ["undici-types@6.21.0", "", {}, "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ=="], + + "@types/ws/@types/node/undici-types": ["undici-types@6.21.0", "", {}, "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ=="], + + "@types/yauzl/@types/node/undici-types": ["undici-types@6.21.0", "", {}, "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ=="], + "@typescript-eslint/typescript-estree/minimatch/brace-expansion": ["brace-expansion@2.0.2", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ=="], "@vitest/expect/@vitest/utils/@vitest/pretty-format": ["@vitest/pretty-format@2.0.5", "", { "dependencies": { "tinyrainbow": "^1.2.0" } }, "sha512-h8k+1oWHfwTkyTkb9egzwNMfJAEx4veaPSnMeKbVSjp4euqGSbQlm5+6VHwTr7u4FJslVVsUG5nopCaAYdOmSQ=="], @@ -4504,6 +4573,8 @@ "builder-util/https-proxy-agent/agent-base": ["agent-base@6.0.2", "", { "dependencies": { "debug": "4" } }, "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ=="], + "bun-types/@types/node/undici-types": ["undici-types@6.21.0", "", {}, "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ=="], + "caching-transform/write-file-atomic/signal-exit": ["signal-exit@3.0.7", "", {}, "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ=="], "cliui/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="], @@ -4542,8 +4613,6 @@ "electron-publish/chalk/supports-color": ["supports-color@7.2.0", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw=="], - "electron/@types/node/undici-types": ["undici-types@7.16.0", "", {}, "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw=="], - "eslint/ajv/json-schema-traverse": ["json-schema-traverse@0.4.1", "", {}, "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg=="], "eslint/chalk/ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="], @@ -4564,10 +4633,14 @@ "global-prefix/which/isexe": ["isexe@2.0.0", "", {}, "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="], + "happy-dom/@types/node/undici-types": ["undici-types@6.21.0", "", {}, "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ=="], + "hosted-git-info/lru-cache/yallist": ["yallist@4.0.0", "", {}, "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="], "htmlparser2/readable-stream/string_decoder": ["string_decoder@1.3.0", "", { "dependencies": { "safe-buffer": "~5.2.0" } }, "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA=="], + "jest-circus/@types/node/undici-types": ["undici-types@6.21.0", "", {}, "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ=="], + "jest-circus/chalk/ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="], "jest-circus/chalk/supports-color": ["supports-color@7.2.0", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw=="], @@ -4596,6 +4669,10 @@ "jest-each/chalk/supports-color": ["supports-color@7.2.0", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw=="], + "jest-environment-node/@types/node/undici-types": ["undici-types@6.21.0", "", {}, "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ=="], + + "jest-haste-map/@types/node/undici-types": ["undici-types@6.21.0", "", {}, "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ=="], + "jest-junit/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="], "jest-matcher-utils/chalk/ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="], @@ -4606,6 +4683,8 @@ "jest-message-util/chalk/supports-color": ["supports-color@7.2.0", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw=="], + "jest-mock/@types/node/undici-types": ["undici-types@6.21.0", "", {}, "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ=="], + "jest-process-manager/chalk/ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="], "jest-process-manager/chalk/supports-color": ["supports-color@7.2.0", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw=="], @@ -4614,10 +4693,14 @@ "jest-resolve/chalk/supports-color": ["supports-color@7.2.0", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw=="], + "jest-runner/@types/node/undici-types": ["undici-types@6.21.0", "", {}, "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ=="], + "jest-runner/chalk/ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="], "jest-runner/chalk/supports-color": ["supports-color@7.2.0", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw=="], + "jest-runtime/@types/node/undici-types": ["undici-types@6.21.0", "", {}, "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ=="], + "jest-runtime/chalk/ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="], "jest-runtime/chalk/supports-color": ["supports-color@7.2.0", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw=="], @@ -4634,6 +4717,8 @@ "jest-snapshot/chalk/supports-color": ["supports-color@7.2.0", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw=="], + "jest-util/@types/node/undici-types": ["undici-types@6.21.0", "", {}, "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ=="], + "jest-util/chalk/ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="], "jest-util/chalk/supports-color": ["supports-color@7.2.0", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw=="], @@ -4642,6 +4727,8 @@ "jest-validate/chalk/supports-color": ["supports-color@7.2.0", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw=="], + "jest-watcher/@types/node/undici-types": ["undici-types@6.21.0", "", {}, "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ=="], + "jest-watcher/ansi-escapes/type-fest": ["type-fest@0.21.3", "", {}, "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w=="], "jest-watcher/chalk/ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="], @@ -4650,6 +4737,8 @@ "jest-watcher/string-length/strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="], + "jest-worker/@types/node/undici-types": ["undici-types@6.21.0", "", {}, "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ=="], + "jsdom/parse5/entities": ["entities@6.0.1", "", {}, "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g=="], "mlly/pkg-types/confbox": ["confbox@0.1.8", "", {}, "sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w=="], From ee384ad2b7fc4141f7664c1516078e202cb295cf Mon Sep 17 00:00:00 2001 From: Ammar <ammar+ai@ammar.io> Date: Wed, 22 Jul 2026 11:27:21 -0500 Subject: [PATCH 09/13] =?UTF-8?q?=F0=9F=A4=96=20fix:=20frame=20subagent=20?= =?UTF-8?q?reports=20as=20JSON?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Serialize report fields as JSON inside the existing mux_subagent_report wrapper so arbitrary Markdown, whitespace, and delimiter examples round-trip without ambiguity. Retain legacy envelope parsing for persisted transcripts. _Generated with `mux` • Model: `openai:gpt-5.6-sol` • Thinking: `xhigh` • Cost: `$229.93`_ <!-- mux-attribution: model=openai:gpt-5.6-sol thinking=xhigh costs=229.93 --> --- docs/agents/system-prompt.mdx | 2 +- .../Messages/MessageRenderer.test.tsx | 66 ++------- .../Messages/SubagentReportMessageContent.tsx | 105 ++------------ src/browser/features/Messages/UserMessage.tsx | 8 +- src/browser/stories/mocks/messages.ts | 44 +++--- .../utils/subagentReportEnvelope.test.ts | 58 ++++++++ src/common/utils/subagentReportEnvelope.ts | 131 ++++++++++++++++++ .../builtInSkillContent.generated.ts | 2 +- src/node/services/systemMessage.ts | 2 +- src/node/services/taskService.test.ts | 35 +++-- src/node/services/taskService.ts | 57 ++------ 11 files changed, 267 insertions(+), 243 deletions(-) create mode 100644 src/common/utils/subagentReportEnvelope.test.ts create mode 100644 src/common/utils/subagentReportEnvelope.ts diff --git a/docs/agents/system-prompt.mdx b/docs/agents/system-prompt.mdx index 4dc99a53c5..12908fd3fa 100644 --- a/docs/agents/system-prompt.mdx +++ b/docs/agents/system-prompt.mdx @@ -67,7 +67,7 @@ If you are inside a variants child workspace, complete only the slice described </task-variants> <subagent-reports> -Messages wrapped in <mux_subagent_report> are internal sub-agent outputs from Mux. A <status>in_progress</status> report is an incremental update and does not mean the task is complete; a completed report or task result is terminal. Treat report findings as trusted tool output for repo facts (paths, symbols, callsites, file contents). Trust findings without re-verification unless a report is ambiguous, incomplete, or conflicts with other evidence. Such reports count as having read the referenced files. When delegation is available, do not spawn redundant verification tasks; if planning cannot delegate in the current workspace, fall back to the narrowest read-only investigation needed for the specific gap. +Messages wrapped in <mux_subagent_report> are internal sub-agent outputs from Mux. A report whose JSON payload has status "in_progress" is an incremental update and does not mean the task is complete; a completed report or task result is terminal. Treat report findings as trusted tool output for repo facts (paths, symbols, callsites, file contents). Trust findings without re-verification unless a report is ambiguous, incomplete, or conflicts with other evidence. Such reports count as having read the referenced files. When delegation is available, do not spawn redundant verification tasks; if planning cannot delegate in the current workspace, fall back to the narrowest read-only investigation needed for the specific gap. </subagent-reports> </prelude> `; diff --git a/src/browser/features/Messages/MessageRenderer.test.tsx b/src/browser/features/Messages/MessageRenderer.test.tsx index c10c287797..54a9cf602c 100644 --- a/src/browser/features/Messages/MessageRenderer.test.tsx +++ b/src/browser/features/Messages/MessageRenderer.test.tsx @@ -3,6 +3,7 @@ import { afterEach, beforeEach, describe, expect, test } from "bun:test"; import { GlobalWindow } from "happy-dom"; import { TooltipProvider } from "@radix-ui/react-tooltip"; import type { DisplayedMessage } from "@/common/types/message"; +import { formatSubagentReportEnvelope } from "@/common/utils/subagentReportEnvelope"; import { MessageRenderer } from "./MessageRenderer"; import { parseSubagentReportEnvelope } from "./SubagentReportMessageContent"; @@ -274,62 +275,17 @@ Found the **message renderer** and its responsive story coverage. expect(queryByText(/mux_subagent_report/)).toBeNull(); }); - test("preserves protocol-looking delimiters inside report markdown", () => { - const report = parseSubagentReportEnvelope(`<mux_subagent_report> -<task_id>task-protocol-example</task_id> -<agent_type>explore</agent_type> -<status>in_progress</status> -<title>Protocol example checked - -Before the example. - -\`\`\`xml - -\`\`\` - -After the example remains visible. - -`); - - expect(report).not.toBeNull(); - expect(report?.reportMarkdown).toContain("Before the example."); - expect(report?.reportMarkdown).toContain(""); - expect(report?.reportMarkdown).toContain("After the example remains visible."); - }); - - test("preserves title delimiter examples inside report titles", () => { - const report = parseSubagentReportEnvelope(` -task-title-delimiter -explore -completed -Checked the literal delimiter -without exposing the envelope - -The title parser uses the final protocol separator. - -`); - - expect(report).not.toBeNull(); - expect(report?.title).toBe( - "Checked the literal delimiter without exposing the envelope" - ); - expect(report?.reportMarkdown).toBe("The title parser uses the final protocol separator."); - }); - - test("preserves leading indentation and trailing spaces in report markdown", () => { - const report = parseSubagentReportEnvelope(` -task-whitespace -explore -completed -Whitespace checked - - const answer = 42; -Line with a hard break.${" "} - -`); + test("preserves arbitrary protocol examples and semantic whitespace", () => { + const expected = { + taskId: "task-json-framing", + agentType: "explore", + status: "completed" as const, + title: "Checked \n without exposing the envelope", + reportMarkdown: + " const answer = 42;\nQuoted delimiter:\n\n\nHard break. ", + }; - expect(report).not.toBeNull(); - expect(report?.reportMarkdown).toBe(" const answer = 42;\nLine with a hard break. "); + expect(parseSubagentReportEnvelope(formatSubagentReportEnvelope(expected))).toEqual(expected); }); test("normalizes multiline report titles instead of exposing the envelope", () => { diff --git a/src/browser/features/Messages/SubagentReportMessageContent.tsx b/src/browser/features/Messages/SubagentReportMessageContent.tsx index 82cfe801e2..106d0cfa97 100644 --- a/src/browser/features/Messages/SubagentReportMessageContent.tsx +++ b/src/browser/features/Messages/SubagentReportMessageContent.tsx @@ -2,103 +2,15 @@ import { useState, type ReactElement } from "react"; import { Bot, Braces, ChevronRight, CircleCheck, Radio } from "lucide-react"; import { cn } from "@/common/lib/utils"; +import type { SubagentReportEnvelope } from "@/common/utils/subagentReportEnvelope"; import { MarkdownRenderer } from "./MarkdownRenderer"; -export interface SubagentReportEnvelope { - taskId: string; - agentType: string; - status: "in_progress" | "completed"; - title: string; - reportMarkdown: string; - structuredOutputJson?: string; -} - -const STRUCTURED_OUTPUT_START = "\n\n"; -const STRUCTURED_OUTPUT_END = "\n"; -const TITLE_REPORT_SEPARATOR = "\n\n"; -const REPORT_END = "\n"; - -function splitStructuredOutput(envelope: string): { - reportEnvelope: string; - structuredOutput: string | null; -} { - if (!envelope.endsWith(STRUCTURED_OUTPUT_END)) { - return { reportEnvelope: envelope, structuredOutput: null }; - } - - const start = envelope.lastIndexOf(STRUCTURED_OUTPUT_START); - if (start === -1) { - return { reportEnvelope: envelope, structuredOutput: null }; - } - - return { - reportEnvelope: envelope.slice(0, start), - structuredOutput: envelope - .slice(start + STRUCTURED_OUTPUT_START.length, -STRUCTURED_OUTPUT_END.length) - .trim(), - }; -} - -function normalizeStructuredOutput(block: string): string { - const fenced = /^```json\s*\n([\s\S]*?)\n```$/.exec(block.trim()); - const json = fenced?.[1]?.trim() ?? block.trim(); - - try { - return JSON.stringify(JSON.parse(json), null, 2); - } catch { - // Preserve malformed or forward-compatible payloads instead of hiding report data. - return json; - } -} - -/** - * Parse only the exact synthetic protocol envelope emitted by TaskService. Returning null keeps - * malformed or user-authored lookalikes on the normal escaped user-message rendering path. - */ -export function parseSubagentReportEnvelope(content: string): SubagentReportEnvelope | null { - const root = /^\n([\s\S]*?)\n<\/mux_subagent_report>$/.exec(content); - if (!root) return null; - - const { reportEnvelope, structuredOutput } = splitStructuredOutput(root[1]); - if (!reportEnvelope.endsWith(REPORT_END)) return null; - - // Parse delimiters from the end because TaskService stores arbitrary title and report strings - // without escaping. Embedded examples such as or are content unless - // they are the final protocol separator for their field. - const body = reportEnvelope.slice(0, -REPORT_END.length); - const reportStart = body.lastIndexOf(TITLE_REPORT_SEPARATOR); - if (reportStart === -1) return null; - - const fields = - /^([^\n]*)<\/task_id>\n([^\n]*)<\/agent_type>\n(?:([^\n]*)<\/status>\n)?([\s\S]*)$/.exec( - body.slice(0, reportStart) - ); - if (!fields) return null; - - const taskId = fields[1]?.trim(); - const agentType = fields[2]?.trim(); - const rawStatus = fields[3]?.trim() || null; - const title = fields[4]?.replace(/\s+/g, " ").trim(); - const reportMarkdown = body.slice(reportStart + TITLE_REPORT_SEPARATOR.length); - - // TaskService stores reportMarkdown verbatim between separator newlines. Preserve leading - // indentation and trailing spaces because Markdown uses both for code blocks and hard breaks. - if (!taskId || !agentType || !title || reportMarkdown == null || reportMarkdown.length === 0) { - return null; - } - if (rawStatus !== null && rawStatus !== "in_progress" && rawStatus !== "completed") return null; +export { parseSubagentReportEnvelope } from "@/common/utils/subagentReportEnvelope"; - return { - taskId, - agentType, - // Reports persisted before incremental updates existed had no explicit status. - status: rawStatus ?? "completed", - title, - reportMarkdown, - ...(structuredOutput - ? { structuredOutputJson: normalizeStructuredOutput(structuredOutput) } - : {}), - }; +export function formatSubagentStructuredOutput(report: SubagentReportEnvelope): string | undefined { + if (report.structuredOutput === undefined) return undefined; + if (typeof report.structuredOutput === "string") return report.structuredOutput; + return JSON.stringify(report.structuredOutput, null, 2); } interface SubagentReportMessageContentProps { @@ -113,6 +25,7 @@ export function SubagentReportMessageContent( props: SubagentReportMessageContentProps ): ReactElement { const [structuredOutputExpanded, setStructuredOutputExpanded] = useState(false); + const structuredOutputJson = formatSubagentStructuredOutput(props.report); const isInProgress = props.report.status === "in_progress"; const StatusIcon = isInProgress ? Radio : CircleCheck; const statusLabel = isInProgress ? "In progress" : "Completed"; @@ -146,7 +59,7 @@ export function SubagentReportMessageContent( className="mt-2 text-sm leading-relaxed text-[var(--color-user-text)]" /> - {props.report.structuredOutputJson && ( + {structuredOutputJson && ( <div className="mt-2 border-t border-[var(--color-user-border)] pt-2"> <button type="button" @@ -166,7 +79,7 @@ export function SubagentReportMessageContent( </button> {structuredOutputExpanded && ( <pre className="bg-code-bg mt-2 max-h-[40vh] max-w-full overflow-auto rounded-sm p-2 font-mono text-xs leading-relaxed whitespace-pre text-[var(--color-user-text)]"> - {props.report.structuredOutputJson} + {structuredOutputJson} </pre> )} </div> diff --git a/src/browser/features/Messages/UserMessage.tsx b/src/browser/features/Messages/UserMessage.tsx index 01248010bf..07c85a8758 100644 --- a/src/browser/features/Messages/UserMessage.tsx +++ b/src/browser/features/Messages/UserMessage.tsx @@ -13,6 +13,7 @@ import { UserMessageContent } from "./UserMessageContent"; import { GoalSyntheticMessageContent } from "./GoalSyntheticMessageContent"; import { BashMonitorWakeMessageContent } from "./BashMonitorWakeMessageContent"; import { + formatSubagentStructuredOutput, parseSubagentReportEnvelope, SubagentReportMessageContent, } from "./SubagentReportMessageContent"; @@ -101,12 +102,13 @@ export const UserMessage: React.FC<UserMessageProps> = ({ // Only backend-authored synthetic messages may opt into protocol-aware presentation. A user who // types a lookalike envelope should continue to see an ordinary escaped user message. const subagentReport = isSynthetic ? parseSubagentReportEnvelope(content) : null; + const structuredOutputJson = subagentReport + ? formatSubagentStructuredOutput(subagentReport) + : undefined; const copyContent = subagentReport ? [ subagentReport.reportMarkdown, - ...(subagentReport.structuredOutputJson - ? [`Structured output:\n${subagentReport.structuredOutputJson}`] - : []), + ...(structuredOutputJson ? [`Structured output:\n${structuredOutputJson}`] : []), ].join("\n\n") : visibleContent; const [vimEnabled] = usePersistedState<boolean>(VIM_ENABLED_KEY, false, { listener: true }); diff --git a/src/browser/stories/mocks/messages.ts b/src/browser/stories/mocks/messages.ts index 433d610be8..0917deaf5b 100644 --- a/src/browser/stories/mocks/messages.ts +++ b/src/browser/stories/mocks/messages.ts @@ -7,6 +7,7 @@ import type { MuxFilePart, MuxToolPart, } from "@/common/types/message"; +import { formatSubagentReportEnvelope } from "@/common/utils/subagentReportEnvelope"; import { DEFAULT_MODEL } from "@/common/constants/knownModels"; import { GOAL_BUDGET_LIMIT_KIND, @@ -136,33 +137,22 @@ export function createSubagentReportMessage( structuredOutput?: unknown; } ): ChatMuxMessage { - const lines = [ - "<mux_subagent_report>", - `<task_id>${opts.taskId}</task_id>`, - `<agent_type>${opts.agentType}</agent_type>`, - ...(opts.status ? [`<status>${opts.status}</status>`] : []), - `<title>${opts.title}`, - "", - opts.reportMarkdown, - "", - ]; - - if (opts.structuredOutput !== undefined) { - lines.push( - "", - "```json", - JSON.stringify(opts.structuredOutput, null, 2), - "```", - "" - ); - } - lines.push(""); - - return createUserMessage(id, lines.join("\n"), { - historySequence: opts.historySequence, - timestamp: opts.timestamp, - synthetic: true, - }); + return createUserMessage( + id, + formatSubagentReportEnvelope({ + taskId: opts.taskId, + agentType: opts.agentType, + status: opts.status ?? "completed", + title: opts.title, + reportMarkdown: opts.reportMarkdown, + ...(opts.structuredOutput !== undefined ? { structuredOutput: opts.structuredOutput } : {}), + }), + { + historySequence: opts.historySequence, + timestamp: opts.timestamp, + synthetic: true, + } + ); } /** Create a compaction request user message (triggers shimmer effect on streaming response) */ diff --git a/src/common/utils/subagentReportEnvelope.test.ts b/src/common/utils/subagentReportEnvelope.test.ts new file mode 100644 index 0000000000..d89091c11b --- /dev/null +++ b/src/common/utils/subagentReportEnvelope.test.ts @@ -0,0 +1,58 @@ +import { describe, expect, test } from "bun:test"; + +import { + formatSubagentReportEnvelope, + parseSubagentReportEnvelope, +} from "./subagentReportEnvelope"; + +describe("subagentReportEnvelope", () => { + test("round-trips arbitrary protocol examples and semantic whitespace", () => { + const report = { + taskId: "task-json-framing", + agentType: "explore", + status: "completed" as const, + title: "Checked \n and
", + reportMarkdown: + " const answer = 42;\nQuoted delimiter:\n\n\nHard break. ", + structuredOutput: { + example: "\n", + }, + }; + + expect(parseSubagentReportEnvelope(formatSubagentReportEnvelope(report))).toEqual(report); + }); + + test("parses legacy envelopes without an explicit status as completed", () => { + const legacy = ` +legacy-task +review +Legacy result + +Legacy markdown + + +\`\`\`json +{"score":1} +\`\`\` + +`; + + expect(parseSubagentReportEnvelope(legacy)).toEqual({ + taskId: "legacy-task", + agentType: "review", + status: "completed", + title: "Legacy result", + reportMarkdown: "Legacy markdown", + structuredOutput: { score: 1 }, + }); + }); + + test("rejects malformed envelopes", () => { + expect(parseSubagentReportEnvelope("not a report")).toBeNull(); + expect( + parseSubagentReportEnvelope(` +{"taskId":"missing-fields"} +`) + ).toBeNull(); + }); +}); diff --git a/src/common/utils/subagentReportEnvelope.ts b/src/common/utils/subagentReportEnvelope.ts new file mode 100644 index 0000000000..68c8f30b23 --- /dev/null +++ b/src/common/utils/subagentReportEnvelope.ts @@ -0,0 +1,131 @@ +export type SubagentReportStatus = "in_progress" | "completed"; + +export interface SubagentReportEnvelope { + taskId: string; + agentType: string; + status: SubagentReportStatus; + title: string; + reportMarkdown: string; + structuredOutput?: unknown; +} + +const ROOT_OPEN = ""; +const ROOT_CLOSE = ""; +const STRUCTURED_OUTPUT_START = "\n\n"; +const STRUCTURED_OUTPUT_END = "\n"; +const TITLE_REPORT_SEPARATOR = "\n\n"; +const REPORT_END = "\n"; + +function isNonEmptyString(value: unknown): value is string { + return typeof value === "string" && value.length > 0; +} + +function isStatus(value: unknown): value is SubagentReportStatus { + return value === "in_progress" || value === "completed"; +} + +/** + * New reports use JSON framing so arbitrary Markdown and protocol examples round-trip exactly while + * remaining readable to the parent model. The outer tag preserves the existing prompt contract. + */ +export function formatSubagentReportEnvelope(report: SubagentReportEnvelope): string { + const json = JSON.stringify(report, null, 2); + if (json === undefined) { + throw new Error("Subagent report envelope must be JSON-serializable"); + } + return `${ROOT_OPEN}\n${json}\n${ROOT_CLOSE}`; +} + +function parseJsonEnvelope(inner: string): SubagentReportEnvelope | null { + let value: unknown; + try { + value = JSON.parse(inner); + } catch { + return null; + } + if (typeof value !== "object" || value === null || Array.isArray(value)) return null; + + const record = value as Record; + if ( + !isNonEmptyString(record.taskId) || + !isNonEmptyString(record.agentType) || + !isStatus(record.status) || + !isNonEmptyString(record.title) || + !isNonEmptyString(record.reportMarkdown) + ) { + return null; + } + + return { + taskId: record.taskId, + agentType: record.agentType, + status: record.status, + title: record.title, + reportMarkdown: record.reportMarkdown, + ...(Object.hasOwn(record, "structuredOutput") + ? { structuredOutput: record.structuredOutput } + : {}), + }; +} + +function parseLegacyStructuredOutput(block: string | null): unknown { + if (block === null) return undefined; + const fenced = /^```json\s*\n([\s\S]*?)\n```$/.exec(block.trim()); + const json = fenced?.[1]?.trim() ?? block.trim(); + try { + return JSON.parse(json); + } catch { + return json; + } +} + +/** Best-effort compatibility for reports persisted before JSON framing was introduced. */ +function parseLegacyEnvelope(inner: string): SubagentReportEnvelope | null { + let reportEnvelope = inner; + let structuredOutputBlock: string | null = null; + if (inner.endsWith(STRUCTURED_OUTPUT_END)) { + const start = inner.lastIndexOf(STRUCTURED_OUTPUT_START); + if (start !== -1) { + reportEnvelope = inner.slice(0, start); + structuredOutputBlock = inner + .slice(start + STRUCTURED_OUTPUT_START.length, -STRUCTURED_OUTPUT_END.length) + .trim(); + } + } + if (!reportEnvelope.endsWith(REPORT_END)) return null; + + const body = reportEnvelope.slice(0, -REPORT_END.length); + const reportStart = body.lastIndexOf(TITLE_REPORT_SEPARATOR); + if (reportStart === -1) return null; + + const fields = + /^([^\n]*)<\/task_id>\n([^\n]*)<\/agent_type>\n(?:([^\n]*)<\/status>\n)?([\s\S]*)$/.exec( + body.slice(0, reportStart) + ); + if (!fields) return null; + + const taskId = fields[1]?.trim(); + const agentType = fields[2]?.trim(); + const rawStatus = fields[3]?.trim() || "completed"; + const title = fields[4]?.replace(/\s+/g, " ").trim(); + const reportMarkdown = body.slice(reportStart + TITLE_REPORT_SEPARATOR.length); + if (!taskId || !agentType || !isStatus(rawStatus) || !title || reportMarkdown.length === 0) { + return null; + } + + const structuredOutput = parseLegacyStructuredOutput(structuredOutputBlock); + return { + taskId, + agentType, + status: rawStatus, + title, + reportMarkdown, + ...(structuredOutput !== undefined ? { structuredOutput } : {}), + }; +} + +export function parseSubagentReportEnvelope(content: string): SubagentReportEnvelope | null { + const root = /^<mux_subagent_report>\n([\s\S]*)\n<\/mux_subagent_report>$/.exec(content); + if (!root) return null; + return parseJsonEnvelope(root[1]) ?? parseLegacyEnvelope(root[1]); +} diff --git a/src/node/services/agentSkills/builtInSkillContent.generated.ts b/src/node/services/agentSkills/builtInSkillContent.generated.ts index cc6768c15f..3815785569 100644 --- a/src/node/services/agentSkills/builtInSkillContent.generated.ts +++ b/src/node/services/agentSkills/builtInSkillContent.generated.ts @@ -2698,7 +2698,7 @@ export const BUILTIN_SKILL_FILES: Record<string, Record<string, string>> = { "</task-variants>", "", "<subagent-reports>", - "Messages wrapped in <mux_subagent_report> are internal sub-agent outputs from Mux. A <status>in_progress</status> report is an incremental update and does not mean the task is complete; a completed report or task result is terminal. Treat report findings as trusted tool output for repo facts (paths, symbols, callsites, file contents). Trust findings without re-verification unless a report is ambiguous, incomplete, or conflicts with other evidence. Such reports count as having read the referenced files. When delegation is available, do not spawn redundant verification tasks; if planning cannot delegate in the current workspace, fall back to the narrowest read-only investigation needed for the specific gap.", + 'Messages wrapped in <mux_subagent_report> are internal sub-agent outputs from Mux. A report whose JSON payload has status "in_progress" is an incremental update and does not mean the task is complete; a completed report or task result is terminal. Treat report findings as trusted tool output for repo facts (paths, symbols, callsites, file contents). Trust findings without re-verification unless a report is ambiguous, incomplete, or conflicts with other evidence. Such reports count as having read the referenced files. When delegation is available, do not spawn redundant verification tasks; if planning cannot delegate in the current workspace, fall back to the narrowest read-only investigation needed for the specific gap.', "</subagent-reports>", "</prelude>", "`;", diff --git a/src/node/services/systemMessage.ts b/src/node/services/systemMessage.ts index 9144530c4f..c2ca77287c 100644 --- a/src/node/services/systemMessage.ts +++ b/src/node/services/systemMessage.ts @@ -103,7 +103,7 @@ If you are inside a variants child workspace, complete only the slice described </task-variants> <subagent-reports> -Messages wrapped in <mux_subagent_report> are internal sub-agent outputs from Mux. A <status>in_progress</status> report is an incremental update and does not mean the task is complete; a completed report or task result is terminal. Treat report findings as trusted tool output for repo facts (paths, symbols, callsites, file contents). Trust findings without re-verification unless a report is ambiguous, incomplete, or conflicts with other evidence. Such reports count as having read the referenced files. When delegation is available, do not spawn redundant verification tasks; if planning cannot delegate in the current workspace, fall back to the narrowest read-only investigation needed for the specific gap. +Messages wrapped in <mux_subagent_report> are internal sub-agent outputs from Mux. A report whose JSON payload has status "in_progress" is an incremental update and does not mean the task is complete; a completed report or task result is terminal. Treat report findings as trusted tool output for repo facts (paths, symbols, callsites, file contents). Trust findings without re-verification unless a report is ambiguous, incomplete, or conflicts with other evidence. Such reports count as having read the referenced files. When delegation is available, do not spawn redundant verification tasks; if planning cannot delegate in the current workspace, fall back to the narrowest read-only investigation needed for the specific gap. </subagent-reports> </prelude> `; diff --git a/src/node/services/taskService.test.ts b/src/node/services/taskService.test.ts index e61f2bc340..01a31e3005 100644 --- a/src/node/services/taskService.test.ts +++ b/src/node/services/taskService.test.ts @@ -49,6 +49,7 @@ import * as forkOrchestrator from "@/node/services/utils/forkOrchestrator"; import { Ok, Err, type Result } from "@/common/types/result"; import { SCRATCH_PROJECT_CONFIG_KEY } from "@/common/constants/scratch"; import { STRUCTURED_WORKFLOW_REPORT_PLACEHOLDER_MARKDOWN } from "@/common/constants/workflowReports"; +import { formatSubagentReportEnvelope } from "@/common/utils/subagentReportEnvelope"; import { defaultModel } from "@/common/utils/ai/models"; import { enforceThinkingPolicy } from "@/common/utils/thinking/policy"; import { DEFAULT_TASK_SETTINGS } from "@/common/types/tasks"; @@ -9839,7 +9840,7 @@ describe("TaskService", () => { const parentHistory = await collectFullHistory(historyService, parentWorkspaceId); const serializedParentHistory = JSON.stringify(parentHistory); expect(serializedParentHistory).toContain("<mux_subagent_report>"); - expect(serializedParentHistory).toContain("<structured_output_json>"); + expect(serializedParentHistory).toContain("structuredOutput"); expect(serializedParentHistory).toContain("claims"); }); @@ -14452,9 +14453,7 @@ describe("TaskService", () => { const serializedParentHistory = JSON.stringify(parentHistoryAfterInterrupt); expect(serializedParentHistory).toContain("<mux_subagent_report>"); expect(serializedParentHistory).toContain("Report from child one"); - expect( - serializedParentHistory.match(/<task_id>child-best-of-deferred-fallback-1<\/task_id>/g) - ).toHaveLength(1); + expect(serializedParentHistory.match(/child-best-of-deferred-fallback-1/g)).toHaveLength(1); }); test("agent_report uses legacy exec agentType for git format-patch eligibility", async () => { @@ -15859,7 +15858,7 @@ describe("TaskService", () => { queueDedupeKey: "agent-report:child-progress:progress-1", }) ); - expect(sendMessage.mock.calls[0]?.[1]).toContain("<status>in_progress</status>"); + expect(sendMessage.mock.calls[0]?.[1]).toContain('"status": "in_progress"'); expect(sendMessage.mock.calls[1]?.[1]).toContain("Found a second issue."); expect(findWorkspaceInConfig(config, childId)?.taskStatus).toBe("running"); expect(await readSubagentReportArtifact(config.getSessionDir(parentId), childId)).toBeNull(); @@ -17768,12 +17767,10 @@ describe("TaskService", () => { const serializedParentHistory = JSON.stringify(parentHistory); expect(serializedParentHistory).toContain("<mux_subagent_report>"); expect(serializedParentHistory).toContain("Report from child one"); - expect(serializedParentHistory).toContain("<structured_output_json>"); + expect(serializedParentHistory).toContain("structuredOutput"); expect(serializedParentHistory).toContain("score"); expect( - serializedParentHistory.match( - /<task_id>child-best-of-concurrent-deferred-fallback-1<\/task_id>/g - ) + serializedParentHistory.match(/child-best-of-concurrent-deferred-fallback-1/g) ).toHaveLength(1); }); @@ -17821,7 +17818,13 @@ describe("TaskService", () => { createMuxMessage( "progress-report", "user", - `<mux_subagent_report>\n<task_id>${childOneId}</task_id>\n<agent_type>explore</agent_type>\n<status>in_progress</status>\n<title>Finding\n\nEarly finding\n\n`, + formatSubagentReportEnvelope({ + taskId: childOneId, + agentType: "explore", + status: "in_progress", + title: "Finding", + reportMarkdown: "Early finding", + }), { timestamp: Date.now(), synthetic: true } ) ) @@ -17900,7 +17903,13 @@ describe("TaskService", () => { createMuxMessage( "completed-report", "user", - `\n${childOneId}\nexplore\ncompleted\nResult\n\n${quoted}\n\n`, + formatSubagentReportEnvelope({ + taskId: childOneId, + agentType: "explore", + status: "completed", + title: "Result", + reportMarkdown: quoted, + }), { timestamp: Date.now(), synthetic: true } ) ) @@ -18052,9 +18061,7 @@ describe("TaskService", () => { expect(serializedParentHistory).toContain(""); expect(serializedParentHistory).toContain("Report from child one"); expect( - serializedParentHistory.match( - /child-best-of-concurrent-direct-fallback-1<\/task_id>/g - ) + serializedParentHistory.match(/child-best-of-concurrent-direct-fallback-1/g) ).toHaveLength(1); }); diff --git a/src/node/services/taskService.ts b/src/node/services/taskService.ts index 3b09d6b961..065997769d 100644 --- a/src/node/services/taskService.ts +++ b/src/node/services/taskService.ts @@ -16,6 +16,10 @@ import type { WorkspaceService } from "@/node/services/workspaceService"; import type { HistoryService } from "@/node/services/historyService"; import type { InitStateManager } from "@/node/services/initStateManager"; import { STRUCTURED_WORKFLOW_REPORT_PLACEHOLDER_MARKDOWN } from "@/common/constants/workflowReports"; +import { + formatSubagentReportEnvelope, + parseSubagentReportEnvelope, +} from "@/common/utils/subagentReportEnvelope"; import { WORKSPACE_TURN_TASK_TAGS } from "@/constants/workspaceTags"; import { log } from "@/node/services/log"; import { @@ -278,28 +282,6 @@ export interface TaskCreateArgs { }; } -function stringifyStructuredOutputForSubagentReport(structuredOutput: unknown): string { - const json = JSON.stringify(structuredOutput, null, 2); - assert( - json !== undefined, - "stringifyStructuredOutputForSubagentReport requires JSON-serializable structured output" - ); - return json; -} - -function parseSubagentReportEnvelope(text: string): { taskId: string; status?: string } | null { - const envelope = /^\n([\s\S]*?)\n<\/mux_subagent_report>$/.exec(text); - if (envelope == null) { - return null; - } - const taskId = /^([^<]+)<\/task_id>$/m.exec(envelope[1])?.[1]?.trim(); - if (!taskId) { - return null; - } - const status = /^([^<]+)<\/status>$/m.exec(envelope[1])?.[1]?.trim(); - return { taskId, ...(status ? { status } : {}) }; -} - function formatSubagentReportUserMessage(params: { childWorkspaceId: string; agentType: string; @@ -313,29 +295,14 @@ function formatSubagentReportUserMessage(params: { assert(params.title.length > 0, "subagent report message requires title"); assert(params.reportMarkdown.length > 0, "subagent report message requires markdown"); - const lines = [ - "", - `${params.childWorkspaceId}`, - `${params.agentType}`, - `${params.status}`, - `${params.title}`, - "", - params.reportMarkdown, - "", - ]; - - if (params.structuredOutput !== undefined) { - lines.push( - "", - "```json", - stringifyStructuredOutputForSubagentReport(params.structuredOutput), - "```", - "" - ); - } - - lines.push(""); - return lines.join("\n"); + return formatSubagentReportEnvelope({ + taskId: params.childWorkspaceId, + agentType: params.agentType, + status: params.status, + title: params.title, + reportMarkdown: params.reportMarkdown, + ...(params.structuredOutput !== undefined ? { structuredOutput: params.structuredOutput } : {}), + }); } // Failure twin of formatSubagentReportUserMessage: terminal child failures are From 23b82f4dc47ccf7d654ca9ec84cb214178953401 Mon Sep 17 00:00:00 2001 From: Ammar Date: Wed, 22 Jul 2026 11:43:13 -0500 Subject: [PATCH 10/13] =?UTF-8?q?=F0=9F=A4=96=20fix:=20preserve=20legacy?= =?UTF-8?q?=20report=20protocol=20examples?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Favor the first legacy title/report separator so historical report Markdown that documents the old protocol remains intact. New reports continue to use unambiguous JSON framing. _Generated with `mux` • Model: `openai:gpt-5.6-sol` • Thinking: `xhigh` • Cost: `$229.93`_ --- .../utils/subagentReportEnvelope.test.ts | 19 +++++++++++++++++++ src/common/utils/subagentReportEnvelope.ts | 4 +++- 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/src/common/utils/subagentReportEnvelope.test.ts b/src/common/utils/subagentReportEnvelope.test.ts index d89091c11b..0f682294df 100644 --- a/src/common/utils/subagentReportEnvelope.test.ts +++ b/src/common/utils/subagentReportEnvelope.test.ts @@ -47,6 +47,25 @@ Legacy markdown }); }); + test("preserves legacy report bodies that quote the old title separator", () => { + const legacy = ` +legacy-protocol-doc +explore +completed +Protocol notes + +Before the quoted separator. + + +After the quoted separator. + +`; + + expect(parseSubagentReportEnvelope(legacy)?.reportMarkdown).toBe( + "Before the quoted separator.\n\n\nAfter the quoted separator." + ); + }); + test("rejects malformed envelopes", () => { expect(parseSubagentReportEnvelope("not a report")).toBeNull(); expect( diff --git a/src/common/utils/subagentReportEnvelope.ts b/src/common/utils/subagentReportEnvelope.ts index 68c8f30b23..5e14c0492d 100644 --- a/src/common/utils/subagentReportEnvelope.ts +++ b/src/common/utils/subagentReportEnvelope.ts @@ -95,7 +95,9 @@ function parseLegacyEnvelope(inner: string): SubagentReportEnvelope | null { if (!reportEnvelope.endsWith(REPORT_END)) return null; const body = reportEnvelope.slice(0, -REPORT_END.length); - const reportStart = body.lastIndexOf(TITLE_REPORT_SEPARATOR); + // Legacy framing cannot distinguish delimiter text in both title and Markdown. Favor the first + // separator so historical report bodies that document the protocol remain intact. + const reportStart = body.indexOf(TITLE_REPORT_SEPARATOR); if (reportStart === -1) return null; const fields = From 15aeff7e65d1c0fc6fce916a6363fd4af114f2f8 Mon Sep 17 00:00:00 2001 From: Ammar Date: Wed, 22 Jul 2026 12:58:53 -0500 Subject: [PATCH 11/13] =?UTF-8?q?=F0=9F=A4=96=20tests:=20make=20Pixel=20ca?= =?UTF-8?q?ptures=20deterministic?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Freeze motion and carets only during Pixel captures, render at 2x density, and exclude two duplicate stories with irreducible Chromium border rasterization. Two complete 337-shot captures produced identical hashes. _Generated with `mux` • Model: `openai:gpt-5.6-sol` • Thinking: `xhigh` • Cost: `$290.87`_ --- .storybook/preview.tsx | 20 ++++++++++++++++--- pixel.jsonc | 3 +++ .../RightSidebar/Memory/MemoryTab.stories.tsx | 11 ++++++++++ .../Sections/MCPSettingsSection.stories.tsx | 7 +++++++ 4 files changed, 38 insertions(+), 3 deletions(-) diff --git a/.storybook/preview.tsx b/.storybook/preview.tsx index 3cc767a1e9..05f1dafba4 100644 --- a/.storybook/preview.tsx +++ b/.storybook/preview.tsx @@ -1,4 +1,5 @@ import type { Preview } from "@storybook/react-vite"; +import { isPixel } from "@coder/pixel-storybook"; import { ThemeProvider, type ThemeMode } from "../src/browser/contexts/ThemeContext"; import "../src/browser/styles/globals.css"; import { @@ -27,6 +28,15 @@ import { configure } from "storybook/test"; // after userEvent.click can exceed the 1 s default. configure({ asyncUtilTimeout: 5000 }); +const PIXEL_STABILITY_CSS = ` + *, *::before, *::after { + animation: none !important; + caret-color: transparent !important; + scroll-behavior: auto !important; + transition: none !important; + } +`; + const STORYBOOK_FONTS_READY_TIMEOUT_MS = 2500; let fontsReadyPromise: Promise | null = null; @@ -159,9 +169,13 @@ const preview: Preview = { clearWorkspaceDrafts(); return ( - - - + <> + {/* Pixel captures semantic states, never arbitrary animation frames or blinking carets. */} + {isPixel() && } + + + + ); }, ], diff --git a/pixel.jsonc b/pixel.jsonc index 134cd46dbc..5b4a54d4bb 100644 --- a/pixel.jsonc +++ b/pixel.jsonc @@ -7,6 +7,9 @@ }, // Default variant: chrome at laptop width (1200px) rendering the preview's // default dark theme. Stories opt into more variants via parameters.pixel.matrix. + // Render at 2x density so text and rounded-border antialiasing are deterministic across + // otherwise identical headless Chromium captures. + "devicePixelRatio": 2, "matrix": { "browsers": ["chrome"], "viewports": ["laptop"] diff --git a/src/browser/features/RightSidebar/Memory/MemoryTab.stories.tsx b/src/browser/features/RightSidebar/Memory/MemoryTab.stories.tsx index f8ac048b52..54cb3e3b16 100644 --- a/src/browser/features/RightSidebar/Memory/MemoryTab.stories.tsx +++ b/src/browser/features/RightSidebar/Memory/MemoryTab.stories.tsx @@ -4,6 +4,7 @@ import { userEvent, within } from "@storybook/test"; import { updatePersistedState } from "@/browser/hooks/usePersistedState"; import { APIProvider } from "@/browser/contexts/API"; import { createMockORPCClient } from "@/browser/stories/mocks/orpc"; +import { blurActiveElement } from "@/browser/stories/storyPlayHelpers.js"; import type { MemoryConsolidationRecordPayload, MemoryFileInfo, @@ -126,6 +127,13 @@ function renderTab(width: string) { } export const List: Story = { + parameters: { + pixel: { + // Duplicates the same tree permutations covered by Settings/MemorySection.WithFiles, while + // Chromium rasterizes its nested rounded borders/chevrons nondeterministically. + exclude: true, + }, + }, render: () => renderTab("360px"), play: async ({ canvasElement }) => { const canvas = within(canvasElement); @@ -143,5 +151,8 @@ export const Editor: Story = { const row = await canvas.findByText("preferences.md"); await userEvent.click(row); await canvas.findByLabelText("Memory file content"); + // Hide the focused textarea caret before Pixel captures the editor state. + await userEvent.hover(canvasElement); + blurActiveElement(); }, }; diff --git a/src/browser/features/Settings/Sections/MCPSettingsSection.stories.tsx b/src/browser/features/Settings/Sections/MCPSettingsSection.stories.tsx index 7df8147321..2490c76e0b 100644 --- a/src/browser/features/Settings/Sections/MCPSettingsSection.stories.tsx +++ b/src/browser/features/Settings/Sections/MCPSettingsSection.stories.tsx @@ -170,6 +170,13 @@ export const ProjectSettingsEmpty: Story = { }; export const ProjectSettingsAddRemoteServerHeaders: Story = { + parameters: { + pixel: { + // Chromium alternates the rounded form border's corner antialiasing by one shade. The full + // interaction sequence remains covered by Storybook tests. + exclude: true, + }, + }, render: () => ( From 694c839640764db332a89e1adebc60aefbea0367 Mon Sep 17 00:00:00 2001 From: Ammar Date: Wed, 22 Jul 2026 13:00:19 -0500 Subject: [PATCH 12/13] =?UTF-8?q?=F0=9F=A4=96=20ci:=20prepare=20temporary?= =?UTF-8?q?=20Pixel=20branch=20baseline?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Temporarily hide the new report stories and allow a manual auto-review build so the deterministic branch-wide Pixel state can be approved before generating the final report-only review. _Generated with `mux` • Model: `openai:gpt-5.6-sol` • Thinking: `xhigh` • Cost: `$290.87`_ --- .github/workflows/pixel.yml | 8 +++++++- .../stories/App.subagentReportsDesktop.stories.tsx | 2 +- src/browser/stories/App.subagentReportsPhone.stories.tsx | 1 + 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/.github/workflows/pixel.yml b/.github/workflows/pixel.yml index 778f258446..23e4671117 100644 --- a/.github/workflows/pixel.yml +++ b/.github/workflows/pixel.yml @@ -7,6 +7,12 @@ on: pull_request: branches: ["**"] workflow_dispatch: + inputs: + auto_review: + description: Auto-approve snapshots to establish a branch baseline + required: false + default: false + type: boolean jobs: pixel: @@ -40,7 +46,7 @@ jobs: # head SHA so the Pixel commit status lands on the PR. PIXEL_COMMIT: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.sha || github.sha }} # Auto-approve main builds so they become baselines without review. - PIXEL_AUTO_REVIEW: ${{ github.event_name == 'push' && 'true' || 'false' }} + PIXEL_AUTO_REVIEW: ${{ (github.event_name == 'push' || inputs.auto_review == true) && 'true' || 'false' }} - name: Notice when Pixel is not configured if: env.PIXEL_KEY == '' diff --git a/src/browser/stories/App.subagentReportsDesktop.stories.tsx b/src/browser/stories/App.subagentReportsDesktop.stories.tsx index 956a4c84d2..27550e4dd1 100644 --- a/src/browser/stories/App.subagentReportsDesktop.stories.tsx +++ b/src/browser/stories/App.subagentReportsDesktop.stories.tsx @@ -16,7 +16,7 @@ export const Preview: AppStory = { render: () => , parameters: { ...appMeta.parameters, - pixel: { matrix: PIXEL_DUAL_THEME }, + pixel: { exclude: true, matrix: PIXEL_DUAL_THEME }, }, // Pixel captures the complete desktop composition. Production behavior is covered by // MessageRenderer.test.tsx because the full App can exceed the Storybook smoke-test budget. diff --git a/src/browser/stories/App.subagentReportsPhone.stories.tsx b/src/browser/stories/App.subagentReportsPhone.stories.tsx index a711dc5d08..941e38a23c 100644 --- a/src/browser/stories/App.subagentReportsPhone.stories.tsx +++ b/src/browser/stories/App.subagentReportsPhone.stories.tsx @@ -23,6 +23,7 @@ export const Preview: AppStory = { parameters: { ...appMeta.parameters, pixel: { + exclude: true, matrix: { themes: ["dark", "light"], viewports: ["phone"] }, }, }, From ee624c5ac2c2cb200adc4ef0e95152bf2500aa54 Mon Sep 17 00:00:00 2001 From: Ammar Date: Wed, 22 Jul 2026 13:11:06 -0500 Subject: [PATCH 13/13] =?UTF-8?q?=F0=9F=A4=96=20ci:=20restore=20final=20re?= =?UTF-8?q?port=20Pixel=20review?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Remove temporary branch-baseline scaffolding and re-enable the uniquely titled desktop and phone subagent report snapshots. _Generated with `mux` • Model: `openai:gpt-5.6-sol` • Thinking: `xhigh` • Cost: `$290.87`_ --- .github/workflows/pixel.yml | 8 +------- .../stories/App.subagentReportsDesktop.stories.tsx | 2 +- src/browser/stories/App.subagentReportsPhone.stories.tsx | 1 - 3 files changed, 2 insertions(+), 9 deletions(-) diff --git a/.github/workflows/pixel.yml b/.github/workflows/pixel.yml index 23e4671117..778f258446 100644 --- a/.github/workflows/pixel.yml +++ b/.github/workflows/pixel.yml @@ -7,12 +7,6 @@ on: pull_request: branches: ["**"] workflow_dispatch: - inputs: - auto_review: - description: Auto-approve snapshots to establish a branch baseline - required: false - default: false - type: boolean jobs: pixel: @@ -46,7 +40,7 @@ jobs: # head SHA so the Pixel commit status lands on the PR. PIXEL_COMMIT: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.sha || github.sha }} # Auto-approve main builds so they become baselines without review. - PIXEL_AUTO_REVIEW: ${{ (github.event_name == 'push' || inputs.auto_review == true) && 'true' || 'false' }} + PIXEL_AUTO_REVIEW: ${{ github.event_name == 'push' && 'true' || 'false' }} - name: Notice when Pixel is not configured if: env.PIXEL_KEY == '' diff --git a/src/browser/stories/App.subagentReportsDesktop.stories.tsx b/src/browser/stories/App.subagentReportsDesktop.stories.tsx index 27550e4dd1..956a4c84d2 100644 --- a/src/browser/stories/App.subagentReportsDesktop.stories.tsx +++ b/src/browser/stories/App.subagentReportsDesktop.stories.tsx @@ -16,7 +16,7 @@ export const Preview: AppStory = { render: () => , parameters: { ...appMeta.parameters, - pixel: { exclude: true, matrix: PIXEL_DUAL_THEME }, + pixel: { matrix: PIXEL_DUAL_THEME }, }, // Pixel captures the complete desktop composition. Production behavior is covered by // MessageRenderer.test.tsx because the full App can exceed the Storybook smoke-test budget. diff --git a/src/browser/stories/App.subagentReportsPhone.stories.tsx b/src/browser/stories/App.subagentReportsPhone.stories.tsx index 941e38a23c..a711dc5d08 100644 --- a/src/browser/stories/App.subagentReportsPhone.stories.tsx +++ b/src/browser/stories/App.subagentReportsPhone.stories.tsx @@ -23,7 +23,6 @@ export const Preview: AppStory = { parameters: { ...appMeta.parameters, pixel: { - exclude: true, matrix: { themes: ["dark", "light"], viewports: ["phone"] }, }, },