From 4852935e8ef218a1a92da7c742138e8c3be43416 Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Sat, 18 Apr 2026 16:22:11 +0100 Subject: [PATCH] perf: speed exec event test waits --- src/agents/bash-tools.exec-runtime.ts | 8 ++++-- src/agents/bash-tools.test.ts | 38 ++++++++++++++++++++++++--- src/gateway/server-node-events.ts | 4 ++- 3 files changed, 44 insertions(+), 6 deletions(-) diff --git a/src/agents/bash-tools.exec-runtime.ts b/src/agents/bash-tools.exec-runtime.ts index 6e5694a5b88..81554ceb17b 100644 --- a/src/agents/bash-tools.exec-runtime.ts +++ b/src/agents/bash-tools.exec-runtime.ts @@ -302,7 +302,9 @@ function maybeNotifyOnExit(session: ProcessSession, status: "completed" | "faile deliveryContext: session.notifyDeliveryContext, trusted: false, }); - requestHeartbeatNow(scopedHeartbeatWakeOptions(sessionKey, { reason: "exec-event" })); + requestHeartbeatNow( + scopedHeartbeatWakeOptions(sessionKey, { reason: "exec-event", coalesceMs: 0 }), + ); } export function createApprovalSlug(id: string) { @@ -378,7 +380,9 @@ export function emitExecSystemEvent( contextKey: opts.contextKey, deliveryContext: opts.deliveryContext, }); - requestHeartbeatNow(scopedHeartbeatWakeOptions(sessionKey, { reason: "exec-event" })); + requestHeartbeatNow( + scopedHeartbeatWakeOptions(sessionKey, { reason: "exec-event", coalesceMs: 0 }), + ); } function joinExecFailureOutput(aggregated: string, reason: string) { diff --git a/src/agents/bash-tools.test.ts b/src/agents/bash-tools.test.ts index 1ac7208ff0e..86668fe5913 100644 --- a/src/agents/bash-tools.test.ts +++ b/src/agents/bash-tools.test.ts @@ -13,7 +13,15 @@ import { resetSystemEventsForTest, } from "../infra/system-events.js"; import { captureEnv } from "../test-utils/env.js"; -import { getFinishedSession, resetProcessRegistryForTests } from "./bash-process-registry.js"; +import { + addSession, + appendOutput, + getFinishedSession, + markBackgrounded, + markExited, + resetProcessRegistryForTests, + type ProcessSession, +} from "./bash-process-registry.js"; import { createExecTool, createProcessTool } from "./bash-tools.js"; import { resolveShellFromPath, sanitizeBinaryOutput } from "./shell-utils.js"; @@ -99,7 +107,6 @@ const withLabel = (label: string, fields: T): T & LabeledCase // Both PowerShell and bash use ; for command separation const joinCommands = (commands: string[]) => commands.join("; "); const echoAfterDelay = (message: string) => joinCommands([shortDelayCmd, shellEcho(message)]); -const echoLines = (lines: string[]) => joinCommands(lines.map((line) => shellEcho(line))); const normalizeText = (value?: string) => sanitizeBinaryOutput(value ?? "") .replace(/\r\n/g, "\n") @@ -401,7 +408,7 @@ const readBackgroundLogSnapshot = async ( lines: string[], options: ProcessLogWindow = {}, ): Promise => { - const { sessionId } = await runBackgroundCommandToCompletion(execTool, echoLines(lines)); + const sessionId = seedFinishedLogSession(lines); const log = await readProcessLog(sessionId, options); return { text: readTextContent(log.content) ?? "", @@ -410,6 +417,31 @@ const readBackgroundLogSnapshot = async ( totalLines: readTotalLines(log.details), }; }; +const seedFinishedLogSession = (lines: string[]) => { + const session: ProcessSession = { + id: `seeded-log-${nextCallId()}`, + command: "seeded log", + startedAt: Date.now(), + maxOutputChars: 100_000, + pendingMaxOutputChars: 100_000, + pendingStdout: [], + pendingStderr: [], + pendingStdoutChars: 0, + pendingStderrChars: 0, + totalOutputChars: 0, + aggregated: "", + tail: "", + exited: false, + truncated: false, + backgrounded: false, + cursorKeyMode: "unknown", + }; + addSession(session); + appendOutput(session, "stdout", lines.join("\n")); + markBackgrounded(session); + markExited(session, 0, null, PROCESS_STATUS_COMPLETED); + return session.id; +}; const runLongLogExpectationCase = async ({ options, firstLine, diff --git a/src/gateway/server-node-events.ts b/src/gateway/server-node-events.ts index 545f0d26a72..51f2c098c5a 100644 --- a/src/gateway/server-node-events.ts +++ b/src/gateway/server-node-events.ts @@ -693,7 +693,9 @@ export const handleNodeEvent = async (ctx: NodeEventContext, nodeId: string, evt // Scope wakes only for canonical agent sessions. Synthetic node-* fallback // keys should keep legacy unscoped behavior so enabled non-main heartbeat // agents still run when no explicit agent session is provided. - requestHeartbeatNow(scopedHeartbeatWakeOptions(sessionKey, { reason: "exec-event" })); + requestHeartbeatNow( + scopedHeartbeatWakeOptions(sessionKey, { reason: "exec-event", coalesceMs: 0 }), + ); } return; }