fix(control-ui): prevent inflated run times and overlapping tasks (#107229)

* fix(control-ui): use wall clock for run timer

* fix(control-ui): prevent background task overlap
This commit is contained in:
Jason (Json)
2026-07-14 09:44:54 -06:00
committed by GitHub
parent 2ecc6b6b6b
commit c93b7ca130
4 changed files with 78 additions and 1 deletions
+2 -1
View File
@@ -77,7 +77,8 @@ export function resolveWorkingStartedAt(
streamStartedAt,
...queue
.filter(shouldRenderQueuedSendInThread)
.map((item) => item.sendRequestStartedAtMs ?? item.sendSubmittedAtMs ?? item.createdAt),
// Send performance fields use performance.now(); the elapsed timer renders against Date.now().
.map((item) => item.createdAt),
...streamSegments.map((segment) => segment.ts),
...toolMessages.map((message) => {
const receivedAt = (message as Record<string, unknown> | null)?.[
@@ -1466,6 +1466,59 @@ describeBrowserLayout.concurrent("chat responsive browser layout", () => {
}
});
it("keeps crowded task sections independently scrollable in the side rail", async () => {
const page = await openBrowserPage(1000, 700);
try {
const taskRows = Array.from(
{ length: 10 },
(_, index) => `<div class="chat-tasks-rail__task">Task ${index + 1}</div>`,
).join("");
await page.setContent(
`<!doctype html><html><head><style>${readUiCss()}</style></head><body>
<div style="width: 760px; height: 320px; display: flex;">
<div class="chat-workbench chat-workbench--tasks-open chat-workbench--workspace-collapsed">
<div class="chat-workbench__main">thread</div>
<aside class="chat-tasks-rail">
<div class="chat-tasks-rail__scroll">
<section class="chat-tasks-rail__section">
<div class="chat-tasks-rail__section-title">Running</div>
<div class="chat-tasks-rail__list">${taskRows}</div>
</section>
<section class="chat-tasks-rail__section">
<div class="chat-tasks-rail__section-title">Finished</div>
<div class="chat-tasks-rail__list">${taskRows}</div>
</section>
</div>
</aside>
</div>
</div>
</body></html>`,
);
const sections = await page.$$eval(".chat-tasks-rail__section", (nodes) =>
nodes.map((node) => {
const section = node as HTMLElement;
section.scrollTop = 100;
return {
clientHeight: section.clientHeight,
overflowY: getComputedStyle(section).overflowY,
scrollHeight: section.scrollHeight,
scrollTop: section.scrollTop,
};
}),
);
expect(sections).toHaveLength(2);
for (const section of sections) {
expect(section.overflowY).toBe("auto");
expect(section.scrollHeight).toBeGreaterThan(section.clientHeight);
expect(section.scrollTop).toBeGreaterThan(0);
}
} finally {
await closeBrowserPage(page);
}
});
it("docks the tasks rail as a full-width bottom strip in a narrow pane", async () => {
const page = await openBrowserPage(1200, 700);
try {
+22
View File
@@ -285,6 +285,28 @@ describe("buildCachedChatItems working spark", () => {
expect(indicator).toMatchObject({ kind: "reading-indicator", startedAt: 42_000 });
});
it("keeps monotonic send timing out of wall-clock elapsed time", () => {
const submittedAt = 1_784_000_000_000;
const indicator = buildCachedChatItems(
createProps({
sessionKey: "agent:main:elapsed-clock-domain",
runWorking: true,
queue: [
{
id: "queued-send-1",
text: "keep working",
createdAt: submittedAt,
sendSubmittedAtMs: 5_000,
sendRequestStartedAtMs: 5_010,
sendState: "sending",
},
],
}),
).find((item) => item.kind === "reading-indicator");
expect(indicator).toMatchObject({ kind: "reading-indicator", startedAt: submittedAt });
});
it("keeps the elapsed start and trailing position after a tool flush", () => {
const items = buildCachedChatItems(
createProps({
+1
View File
@@ -703,6 +703,7 @@
flex: 0 1 auto;
flex-direction: column;
min-height: 0;
overflow-y: auto;
padding-top: 8px;
}