fix: replace domain-specific exit handler with generic signal handler registry (#299)

* fix: replace domain-specific exit handler with generic signal handler registry

Rewrite exitHandler.ts as a generic, domain-agnostic exit signal module
that exports onExitSignal(handler) returning a dispose function.

- subprocess.ts now registers via onExitSignal instead of direct
  process.on(SIGINT/SIGTERM) calls
- resolveTokens registers a signal handler that captures tokens by
  closure, fixing the race condition where the exit handler would
  read the wrong token after disposal
- Remove setupExitHandler and runCleanup — domain cleanup is handled
  by post.ts + await using

Co-authored-by: Cursor <cursoragent@cursor.com>

* tweaks

* simplify handler installation

* extract to util

* fix race in dispose

* wrap dispose body in try/finally to ensure disposingRef always settles

---------

Co-authored-by: Mateusz Burzyński <mateuszburzynski@gmail.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: pullfrog[bot] <226033991+pullfrog[bot]@users.noreply.github.com>
This commit is contained in:
pullfrog[bot]
2026-02-20 10:23:56 +00:00
committed by pullfrog[bot]
parent 70f1c47a28
commit ee100354da
10 changed files with 198 additions and 317 deletions
+1 -6
View File
@@ -5,11 +5,7 @@ import { config } from "dotenv";
import { runInDocker } from "../utils/docker.ts";
import { ensureGitHubToken } from "../utils/github.ts";
import { isInsideDocker } from "../utils/globals.ts";
import {
installSignalHandlers,
killTrackedChildren,
setSignalHandler,
} from "../utils/subprocess.ts";
import { killTrackedChildren, setSignalHandler } from "../utils/subprocess.ts";
import {
type AgentResult,
agents,
@@ -482,7 +478,6 @@ async function main(): Promise<void> {
}
setSignalHandler(handleCancel);
installSignalHandlers();
// run tests with limited concurrency to avoid overwhelming agent APIs
const maxConcurrency = 5;
+1 -3
View File
@@ -5,7 +5,7 @@ import { dirname, join } from "node:path";
import { fileURLToPath } from "node:url";
import { agentsManifest } from "../external.ts";
import type { Inputs } from "../main.ts";
import { installSignalHandlers, trackChild, untrackChild } from "../utils/subprocess.ts";
import { trackChild, untrackChild } from "../utils/subprocess.ts";
const __dirname = dirname(fileURLToPath(import.meta.url));
@@ -165,8 +165,6 @@ const DEFAULT_TEST_TIMEOUT = "10m";
// run agent and stream output with prefix labels
// note: activity timeout is enforced in action main and subprocess utils
export async function runAgentStreaming(options: RunStreamingOptions): Promise<AgentResult> {
installSignalHandlers();
return new Promise((resolve) => {
const chunks: Buffer[] = [];
const prefix = getPrefix({ test: options.test, agent: options.agent });