Skip to content

Commit 124f2a8

Browse files
committed
chore: address review comments
1 parent 4a644dc commit 124f2a8

4 files changed

Lines changed: 48 additions & 9 deletions

File tree

packages/ui/src/hooks/useUserPresence.test.ts

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,21 @@ describe("useUserPresence", () => {
8282
expect(result.current).toBe(false);
8383
});
8484

85+
it("stays present under a sub-throttle idle threshold while interacting", () => {
86+
// idleMs below the 15s activity throttle: the throttle must scale down or
87+
// active input would be dropped and the user pinned "away".
88+
const { result } = renderHook(() => useUserPresence(10_000));
89+
90+
act(() => {
91+
for (let i = 0; i < 20; i++) {
92+
vi.advanceTimersByTime(4_000);
93+
window.dispatchEvent(new Event("keydown"));
94+
}
95+
});
96+
97+
expect(result.current).toBe(true);
98+
});
99+
85100
it("removes listeners on unmount", () => {
86101
const removeSpy = vi.spyOn(window, "removeEventListener");
87102
const { unmount } = renderHook(() => useUserPresence());
@@ -90,7 +105,13 @@ describe("useUserPresence", () => {
90105

91106
const removed = removeSpy.mock.calls.map(([event]) => event);
92107
expect(removed).toEqual(
93-
expect.arrayContaining(["pointerdown", "keydown", "wheel", "focus"]),
108+
expect.arrayContaining([
109+
"pointerdown",
110+
"pointermove",
111+
"keydown",
112+
"wheel",
113+
"focus",
114+
]),
94115
);
95116
});
96117
});

packages/ui/src/hooks/useUserPresence.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,11 +46,15 @@ export function useUserPresence(
4646
useEffect(() => {
4747
let lastActivityAt = Date.now();
4848
let lastRecordedAt = lastActivityAt;
49+
// Scale the throttle down for small idleMs (test knobs): a fixed 15s
50+
// throttle with idleMs <= 15s would drop every input and pin the user
51+
// "away" while they actively interact.
52+
const throttleMs = Math.min(ACTIVITY_THROTTLE_MS, idleMs / 4);
4953

5054
const onActivity = (event: Event) => {
5155
if (event.type !== "focus" && !document.hasFocus()) return;
5256
const now = Date.now();
53-
if (now - lastRecordedAt < ACTIVITY_THROTTLE_MS) return;
57+
if (now - lastRecordedAt < throttleMs) return;
5458
lastRecordedAt = now;
5559
lastActivityAt = now;
5660
setPresent(true);

scripts/bench-memory-run.mjs

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,21 +15,33 @@
1515
* Usage:
1616
* node scripts/bench-memory-run.mjs [--port 9223] [--messages 2]
1717
* [--label <label>] [--out results.jsonl] [--idle-only]
18+
* [--scenario thread|switch|longout]
19+
* [--thread-task-id <id>] [--thread-title <title>]
1820
*
1921
* Prints a JSON report; final line is `TOTAL_RSS_MB=<post-workflow median>`
2022
* (idle median with --idle-only) for predicate extraction via `tail -1`.
2123
*
2224
* NOTE: workflow turns hit the real agent backend with trivial prompts
2325
* ("reply with exactly <token>") to keep token cost minimal while exercising
2426
* the real agent-session memory path.
27+
*
28+
* IMPORTANT: the `thread` and `longout` scenarios send turns into a dedicated
29+
* throwaway task so benchmark chatter never lands in real work. The default
30+
* --thread-task-id/--thread-title point at the original author's local bench
31+
* task — in any other environment, create a scratch task once and pass its id
32+
* and title explicitly, or the run aborts with "refusing to send benchmark
33+
* turns".
2534
*/
2635

2736
import { execFileSync, spawn } from "node:child_process";
2837
import { appendFileSync } from "node:fs";
2938
import path from "node:path";
3039
import { fileURLToPath } from "node:url";
3140

32-
const repoRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
41+
const repoRoot = path.resolve(
42+
path.dirname(fileURLToPath(import.meta.url)),
43+
"..",
44+
);
3345

3446
const args = process.argv.slice(2);
3547
function arg(name, fallback) {
@@ -144,7 +156,8 @@ async function openBenchTask(page) {
144156
.catch(() => "");
145157
if (!header?.includes(title)) {
146158
throw new Error(
147-
`refusing to send benchmark turns: open task is "${header}", expected "${title}"`,
159+
`refusing to send benchmark turns: open task is "${header}", expected "${title}". ` +
160+
`Create a throwaway task in your environment and pass --thread-task-id/--thread-title.`,
148161
);
149162
}
150163
}

scripts/bench-memory.mjs

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,9 @@ async function rendererHeapMb() {
122122
signal: AbortSignal.timeout(2000),
123123
});
124124
const targets = await res.json();
125-
const page = targets.find((t) => t.type === "page" && t.webSocketDebuggerUrl);
125+
const page = targets.find(
126+
(t) => t.type === "page" && t.webSocketDebuggerUrl,
127+
);
126128
if (!page) return null;
127129
const ws = new WebSocket(page.webSocketDebuggerUrl);
128130
const heap = await new Promise((resolve) => {
@@ -162,9 +164,7 @@ function median(values) {
162164
if (!values.length) return 0;
163165
const sorted = [...values].sort((a, b) => a - b);
164166
const mid = Math.floor(sorted.length / 2);
165-
return sorted.length % 2
166-
? sorted[mid]
167-
: (sorted[mid - 1] + sorted[mid]) / 2;
167+
return sorted.length % 2 ? sorted[mid] : (sorted[mid - 1] + sorted[mid]) / 2;
168168
}
169169

170170
const rootPid = rootPidFromCdpPort();
@@ -198,7 +198,8 @@ const heapSamples = samples.map((s) => s.heapMb).filter((h) => h != null);
198198
const categories = {};
199199
for (const s of samples) {
200200
for (const [k, v] of Object.entries(s.byCategoryMb)) {
201-
(categories[k] ??= []).push(v);
201+
categories[k] ??= [];
202+
categories[k].push(v);
202203
}
203204
}
204205

0 commit comments

Comments
 (0)