diff --git a/hooks/ponytail-activate.js b/hooks/ponytail-activate.js index d54fbe4a..c065590e 100644 --- a/hooks/ponytail-activate.js +++ b/hooks/ponytail-activate.js @@ -94,3 +94,6 @@ try { } catch (e) { // Silent fail — stdout closed/EPIPE at hook exit must not surface as a hook failure } + +process.stdin.resume(); +setTimeout(() => process.exit(0), 1000).unref(); diff --git a/tests/hooks-windows.test.js b/tests/hooks-windows.test.js index 0ec878e1..7d029a23 100644 --- a/tests/hooks-windows.test.js +++ b/tests/hooks-windows.test.js @@ -8,6 +8,7 @@ const test = require('node:test'); const assert = require('node:assert/strict'); const fs = require('fs'); +const os = require('os'); const path = require('path'); const { spawn } = require('child_process'); @@ -106,6 +107,32 @@ test('ponytail-mode-tracker self-exits when stdin never closes (no freeze)', asy assert.equal(code, 0, 'hook must exit cleanly when stdin never closes'); }); +test('ponytail-activate drains large Codex SessionStart stdin without EPIPE', async () => { + const hook = path.join(root, 'hooks', 'ponytail-activate.js'); + const pluginData = fs.mkdtempSync(path.join(os.tmpdir(), 'ponytail-stdin-')); + const child = spawn(process.execPath, [hook], { + env: { + ...process.env, + PLUGIN_DATA: pluginData, + PONYTAIL_DEFAULT_MODE: 'full', + }, + stdio: ['pipe', 'ignore', 'ignore'], + }); + const exitCode = new Promise((resolve) => child.once('close', resolve)); + await new Promise((resolve) => setTimeout(resolve, 50)); + const writeError = await new Promise((resolve) => { + child.stdin.once('error', resolve); + child.stdin.end( + JSON.stringify({ transcript: 'x'.repeat(2_000_000) }), + (error) => resolve(error), + ); + }); + + assert.equal(writeError, null); + assert.equal(await exitCode, 0); + fs.rmSync(pluginData, { recursive: true, force: true }); +}); + test('Claude and Codex manifests point at the shared host-specific hook config', () => { for (const rel of HOST_PLUGIN_MANIFESTS) { const manifest = JSON.parse(fs.readFileSync(path.join(root, rel), 'utf8'));