Publish Claude Code Binaries / Stage and publish binaries (push) Canceled after 0s
Publish Tutti App Runtime / Build darwin-arm64 (push) Canceled after 0s
Publish Tutti App Runtime / Build darwin-amd64 (push) Canceled after 0s
Publish Tutti App Runtime / Build linux-amd64 (push) Canceled after 0s
Publish Tutti App Runtime / Build windows-amd64 (push) Canceled after 0s
Sync Tutti Agent Skills / sync (push) Canceled after 0s
Windows Agent Adapters / Test Windows agent adapters (push) Canceled after 0s
Windows Daemon Adapters / Test Windows daemon adapters (push) Canceled after 0s
Publish Tutti App Runtime / Publish runtime catalog (push) Canceled after 0s
385 lines
12 KiB
JavaScript
385 lines
12 KiB
JavaScript
import { writeFile } from "node:fs/promises";
|
|
import { join } from "node:path";
|
|
import {
|
|
evaluate,
|
|
finishRendererScenario,
|
|
markRenderer,
|
|
startRendererScenario,
|
|
waitForEvaluation
|
|
} from "./agent-gui-performance-helpers.mjs";
|
|
import {
|
|
requiredScenarioData,
|
|
sqlString,
|
|
startupWorkspaceID
|
|
} from "./agent-gui-performance-snapshot-helpers.mjs";
|
|
import {
|
|
prepareAgentGUIWindowStressSnapshot,
|
|
waitForWorkbenchRendererIdle,
|
|
waitForWorkbenchRendererQuiet
|
|
} from "./agent-gui-window-performance-scenarios.mjs";
|
|
|
|
const dockPopupPreviewWindowCount = 50;
|
|
const dockPopupPreviewScreenshot = "workbench-dock-popup-preview.png";
|
|
const dockPopupPreviewMarkers = {
|
|
start: "tutti-perf:workbench-dock-popup-preview:start",
|
|
opened: "tutti-perf:workbench-dock-popup-preview:opened-observed",
|
|
ready: "tutti-perf:workbench-dock-popup-preview:ready-observed",
|
|
captured: "tutti-perf:workbench-dock-popup-preview:captured-observed",
|
|
end: "tutti-perf:workbench-dock-popup-preview:end"
|
|
};
|
|
|
|
export const workbenchDockPopupPreviewScenario = {
|
|
id: "workbench-dock-popup-preview",
|
|
markers: dockPopupPreviewMarkers,
|
|
milestones: [
|
|
{
|
|
key: "opened",
|
|
label: "Dock popup opened",
|
|
marker: dockPopupPreviewMarkers.opened
|
|
},
|
|
{
|
|
key: "ready",
|
|
label: "all preview images ready",
|
|
marker: dockPopupPreviewMarkers.ready
|
|
},
|
|
{
|
|
key: "captured",
|
|
label: "Dock popup pixels captured",
|
|
marker: dockPopupPreviewMarkers.captured
|
|
}
|
|
],
|
|
prepareSnapshot: prepareWorkbenchDockPopupPreviewDatabaseSnapshot,
|
|
prepare: prepareWorkbenchDockPopupPreview,
|
|
execute: executeWorkbenchDockPopupPreview,
|
|
describe(prepared) {
|
|
return `${prepared.expectedNodeIDs.length} mounted AgentGUI windows; fresh Dock preview cache`;
|
|
},
|
|
summarize(prepared, result) {
|
|
return scenarioSummary(
|
|
[
|
|
{
|
|
name: `${dockPopupPreviewWindowCount} Dock popup cards rendered`,
|
|
passed: result.cardCount === dockPopupPreviewWindowCount
|
|
},
|
|
{
|
|
name: "foreground preview rendered",
|
|
passed: result.foregroundReadyCount === 1
|
|
},
|
|
{
|
|
name: "background previews rendered",
|
|
passed:
|
|
result.backgroundReadyCount === dockPopupPreviewWindowCount - 1
|
|
},
|
|
{
|
|
name: "preview PNGs contain visible pixels",
|
|
passed: result.pixelReadyCount === dockPopupPreviewWindowCount
|
|
},
|
|
{
|
|
name: "preview titles rendered",
|
|
passed: result.titledCardCount === dockPopupPreviewWindowCount
|
|
},
|
|
{
|
|
name: "no loading or terminal fallback remained",
|
|
passed: result.unreadyCount === 0
|
|
},
|
|
{ name: "pixel screenshot written", passed: result.captured }
|
|
],
|
|
[
|
|
{ label: "Workspace", value: prepared.workspaceID },
|
|
{ label: "Dock popup cards", value: result.cardCount },
|
|
{
|
|
label: "Foreground/background previews",
|
|
value: `${result.foregroundReadyCount}/${result.backgroundReadyCount}`
|
|
},
|
|
{ label: "Pixel-ready previews", value: result.pixelReadyCount },
|
|
{ label: "Titled cards", value: result.titledCardCount },
|
|
{ label: "Screenshot", value: dockPopupPreviewScreenshot }
|
|
],
|
|
"an isolated empty preview cache forces 49 background AgentGUI cards through the serialized DOM-clone fallback; every resulting PNG must decode with visible, non-uniform pixels"
|
|
);
|
|
},
|
|
assessTrace: assessWorkbenchDockPopupPreviewTrace
|
|
};
|
|
|
|
export function prepareWorkbenchDockPopupPreviewSnapshot(snapshot) {
|
|
if (
|
|
!snapshot ||
|
|
typeof snapshot !== "object" ||
|
|
!Array.isArray(snapshot.nodes)
|
|
) {
|
|
throw new Error("invalid Workbench Dock popup preview snapshot input");
|
|
}
|
|
const stressedSnapshot = prepareAgentGUIWindowStressSnapshot(
|
|
snapshot,
|
|
dockPopupPreviewWindowCount
|
|
);
|
|
const agentGuiNodes = stressedSnapshot.nodes
|
|
.filter((node) => node?.data?.typeId === "agent-gui")
|
|
.map((node, index) => ({
|
|
...node,
|
|
title: `Dock Preview ${index + 1}`
|
|
}));
|
|
const agentGuiNodeByID = new Map(
|
|
agentGuiNodes.map((node) => [node.id, node])
|
|
);
|
|
return {
|
|
snapshot: {
|
|
...stressedSnapshot,
|
|
nodes: stressedSnapshot.nodes.map(
|
|
(node) => agentGuiNodeByID.get(node.id) ?? node
|
|
)
|
|
},
|
|
expectedNodeIDs: agentGuiNodes.map((node) => node.id),
|
|
expectedTitles: agentGuiNodes.map((node) => node.title)
|
|
};
|
|
}
|
|
|
|
export function assessWorkbenchDockPopupPreviewTrace(traceSummary) {
|
|
const maximumRendererTaskMs = 50;
|
|
const maximumObservedRendererTaskMs = traceSummary.timing.maxLongTaskMs;
|
|
return {
|
|
assertions: [
|
|
{
|
|
name: `renderer task <= ${maximumRendererTaskMs} ms`,
|
|
passed: maximumObservedRendererTaskMs <= maximumRendererTaskMs
|
|
}
|
|
],
|
|
details: [
|
|
{
|
|
label: "Maximum renderer task",
|
|
value: `${maximumObservedRendererTaskMs} ms`
|
|
},
|
|
{
|
|
label: "Renderer task budget",
|
|
value: `${maximumRendererTaskMs} ms`
|
|
}
|
|
]
|
|
};
|
|
}
|
|
|
|
async function prepareWorkbenchDockPopupPreviewDatabaseSnapshot(context) {
|
|
const workspaceID = await startupWorkspaceID(context);
|
|
const rows = await context.sqliteJSON(
|
|
context.databasePath,
|
|
`
|
|
SELECT snapshot_json AS snapshotJSON
|
|
FROM workspace_workbench_snapshots
|
|
WHERE workspace_id = '${sqlString(workspaceID)}'
|
|
LIMIT 1;
|
|
`
|
|
);
|
|
const prepared = prepareWorkbenchDockPopupPreviewSnapshot(
|
|
JSON.parse(rows[0]?.snapshotJSON ?? "null")
|
|
);
|
|
await context.sqliteExec(
|
|
context.databasePath,
|
|
`
|
|
UPDATE workspace_workbench_snapshots
|
|
SET snapshot_json = '${sqlString(JSON.stringify(prepared.snapshot))}'
|
|
WHERE workspace_id = '${sqlString(workspaceID)}';
|
|
`
|
|
);
|
|
return {
|
|
data: {
|
|
expectedNodeIDs: prepared.expectedNodeIDs,
|
|
workspaceID
|
|
}
|
|
};
|
|
}
|
|
|
|
async function prepareWorkbenchDockPopupPreview(context, options) {
|
|
const scenarioData = requiredScenarioData(
|
|
context,
|
|
"workbench-dock-popup-preview"
|
|
);
|
|
const expectedNodeIDs = scenarioData.expectedNodeIDs;
|
|
await waitForEvaluation(
|
|
context.pageClient,
|
|
`(() => {
|
|
const expectedNodeIDs = ${JSON.stringify(expectedNodeIDs)};
|
|
const shells = expectedNodeIDs.map((nodeID) =>
|
|
document.querySelector('[data-workbench-window-id="' + CSS.escape(nodeID) + '"]')
|
|
);
|
|
const mountedShellCount = shells.filter((shell) =>
|
|
shell instanceof HTMLElement &&
|
|
shell.dataset.minimizedMount === 'visible' &&
|
|
shell.dataset.presentationVisibility === 'visible'
|
|
).length;
|
|
const pendingHydrationCount = document.querySelectorAll(
|
|
'[data-agent-gui-workbench-hydration="pending"]'
|
|
).length;
|
|
const slot = document.querySelector(
|
|
'[data-desktop-dock-anchor-key="agent-gui:unified"]'
|
|
);
|
|
const button = slot?.querySelector('button');
|
|
if (!(button instanceof HTMLButtonElement)) {
|
|
return { ready: false, mountedShellCount, pendingHydrationCount };
|
|
}
|
|
const rect = button.getBoundingClientRect();
|
|
return {
|
|
ready:
|
|
mountedShellCount === expectedNodeIDs.length &&
|
|
pendingHydrationCount === 0 &&
|
|
rect.width > 0 &&
|
|
rect.height > 0,
|
|
mountedShellCount,
|
|
pendingHydrationCount,
|
|
expectedWindowCount: expectedNodeIDs.length
|
|
};
|
|
})()`,
|
|
options.timeoutMs,
|
|
"50 mounted AgentGUI windows and the unified Agent Dock button"
|
|
);
|
|
await waitForWorkbenchRendererQuiet(context.pageClient);
|
|
await waitForWorkbenchRendererIdle(context.pageClient);
|
|
return {
|
|
expectedNodeIDs,
|
|
workspaceID: scenarioData.workspaceID
|
|
};
|
|
}
|
|
|
|
async function executeWorkbenchDockPopupPreview(context, prepared, options) {
|
|
const { pageClient } = context;
|
|
await startRendererScenario(pageClient, dockPopupPreviewMarkers.start);
|
|
await evaluate(
|
|
pageClient,
|
|
`(() => {
|
|
const slot = document.querySelector(
|
|
'[data-desktop-dock-anchor-key="agent-gui:unified"]'
|
|
);
|
|
const button = slot?.querySelector('button');
|
|
if (!(button instanceof HTMLButtonElement)) {
|
|
throw new Error('Agent Dock button is unavailable');
|
|
}
|
|
button.click();
|
|
return true;
|
|
})()`
|
|
);
|
|
await waitForEvaluation(
|
|
pageClient,
|
|
`(() => {
|
|
const popup = document.querySelector(
|
|
'[data-desktop-dock-popup-root][data-popup-variant="default"]'
|
|
);
|
|
const cards = popup?.querySelectorAll('[data-desktop-dock-popup-card]');
|
|
return { ready: cards?.length === ${dockPopupPreviewWindowCount} };
|
|
})()`,
|
|
options.timeoutMs,
|
|
"Agent Dock popup with 50 cards"
|
|
);
|
|
await markRenderer(pageClient, dockPopupPreviewMarkers.opened);
|
|
await waitForEvaluation(
|
|
pageClient,
|
|
`(() => {
|
|
const cards = [...document.querySelectorAll(
|
|
'[data-desktop-dock-popup-root][data-popup-variant="default"] [data-desktop-dock-popup-card]'
|
|
)];
|
|
const images = cards.map((card) =>
|
|
card.querySelector('[data-preview-state="ready"][data-preview-kind="image"] img')
|
|
);
|
|
return {
|
|
ready:
|
|
cards.length === ${dockPopupPreviewWindowCount} &&
|
|
images.every((image) =>
|
|
image instanceof HTMLImageElement &&
|
|
image.complete &&
|
|
image.naturalWidth > 0 &&
|
|
image.naturalHeight > 0
|
|
)
|
|
};
|
|
})()`,
|
|
options.timeoutMs,
|
|
"decoded foreground and background Dock preview images"
|
|
);
|
|
const previewState = await evaluate(
|
|
pageClient,
|
|
`Promise.all([...document.querySelectorAll(
|
|
'[data-desktop-dock-popup-root][data-popup-variant="default"] [data-desktop-dock-popup-card]'
|
|
)].map(async (card) => {
|
|
const image = card.querySelector(
|
|
'[data-preview-state="ready"][data-preview-kind="image"] img'
|
|
);
|
|
if (!(image instanceof HTMLImageElement)) {
|
|
return {
|
|
active: card.getAttribute('data-active') === 'true',
|
|
pixelReady: false,
|
|
ready: false,
|
|
title: card.querySelector('[role="button"]')?.getAttribute('aria-label') ?? ''
|
|
};
|
|
}
|
|
await image.decode().catch(() => undefined);
|
|
const canvas = document.createElement('canvas');
|
|
canvas.width = Math.min(64, image.naturalWidth);
|
|
canvas.height = Math.min(64, image.naturalHeight);
|
|
const context = canvas.getContext('2d', { willReadFrequently: true });
|
|
if (!context) throw new Error('Dock preview pixel canvas unavailable');
|
|
context.drawImage(image, 0, 0, canvas.width, canvas.height);
|
|
const pixels = context.getImageData(0, 0, canvas.width, canvas.height).data;
|
|
let visiblePixels = 0;
|
|
const colorBuckets = new Set();
|
|
for (let index = 0; index < pixels.length; index += 4) {
|
|
if (pixels[index + 3] <= 8) continue;
|
|
visiblePixels += 1;
|
|
colorBuckets.add(
|
|
(pixels[index] >> 5) + ':' +
|
|
(pixels[index + 1] >> 5) + ':' +
|
|
(pixels[index + 2] >> 5)
|
|
);
|
|
}
|
|
const totalPixels = canvas.width * canvas.height;
|
|
return {
|
|
active: card.getAttribute('data-active') === 'true',
|
|
pixelReady:
|
|
totalPixels > 0 &&
|
|
visiblePixels / totalPixels >= 0.05 &&
|
|
colorBuckets.size >= 2,
|
|
ready: true,
|
|
title: card.querySelector('[role="button"]')?.getAttribute('aria-label') ?? ''
|
|
};
|
|
}))`,
|
|
true
|
|
);
|
|
await markRenderer(pageClient, dockPopupPreviewMarkers.ready);
|
|
await capturePageScreenshot(
|
|
context,
|
|
join(context.outputDirectory, dockPopupPreviewScreenshot)
|
|
);
|
|
await markRenderer(pageClient, dockPopupPreviewMarkers.captured);
|
|
await finishRendererScenario(pageClient, dockPopupPreviewMarkers.end);
|
|
return {
|
|
backgroundReadyCount: previewState.filter(
|
|
(preview) => !preview.active && preview.ready
|
|
).length,
|
|
captured: true,
|
|
cardCount: previewState.length,
|
|
foregroundReadyCount: previewState.filter(
|
|
(preview) => preview.active && preview.ready
|
|
).length,
|
|
pixelReadyCount: previewState.filter((preview) => preview.pixelReady)
|
|
.length,
|
|
titledCardCount: previewState.filter((preview) => preview.title.trim())
|
|
.length,
|
|
unreadyCount: previewState.filter((preview) => !preview.ready).length
|
|
};
|
|
}
|
|
|
|
async function capturePageScreenshot(context, outputPath) {
|
|
const screenshot = await context.pageClient.send("Page.captureScreenshot", {
|
|
captureBeyondViewport: false,
|
|
format: "png",
|
|
fromSurface: true
|
|
});
|
|
await writeFile(outputPath, Buffer.from(screenshot.data, "base64"));
|
|
}
|
|
|
|
function scenarioSummary(assertions, details, stabilityCriterion) {
|
|
return {
|
|
outcome: assertions.every((assertion) => assertion.passed)
|
|
? "passed"
|
|
: "failed",
|
|
assertions,
|
|
details,
|
|
stabilityCriterion
|
|
};
|
|
}
|