Merge remote-tracking branch 'origin/master' into fix/pty-handoff-grace
This commit is contained in:
@@ -41,7 +41,7 @@ function stubAgent(ctx: Context, rawId: string): Agent {
|
||||
|
||||
async function harness(
|
||||
mode: 'danger-full-access' | 'workspace-write',
|
||||
overrides: { idleSilenceMs?: number; handoffGraceMs?: number; timeoutMs?: number } = {},
|
||||
timing: { idleSilenceMs?: number; handoffGraceMs?: number; timeoutMs?: number } = {},
|
||||
) {
|
||||
const root = mkdtempSync(join(tmpdir(), 'dsh-pty-local-'))
|
||||
roots.push(root)
|
||||
@@ -54,9 +54,9 @@ async function harness(
|
||||
const fiber = await ctx.plugin(ptyLocal, {
|
||||
pollIntervalMs: 10,
|
||||
exactProbeAfterMs: 20,
|
||||
idleSilenceMs: overrides.idleSilenceMs ?? 250,
|
||||
handoffGraceMs: overrides.handoffGraceMs ?? 250,
|
||||
timeoutMs: overrides.timeoutMs ?? 2_000,
|
||||
idleSilenceMs: timing.idleSilenceMs ?? 250,
|
||||
handoffGraceMs: timing.handoffGraceMs ?? 250,
|
||||
timeoutMs: timing.timeoutMs ?? 2_000,
|
||||
disposeGraceMs: 500,
|
||||
scrollbackLines: 100,
|
||||
scrollbackMaxBytes: 32_768,
|
||||
@@ -69,8 +69,8 @@ async function harness(
|
||||
|
||||
// PtySendOperation.append drops output once the operation settles, so this only
|
||||
// observes a marker the child prints while the send is still active.
|
||||
async function waitForOutput(operation: PtySendOperation, expected: string): Promise<void> {
|
||||
const deadline = Date.now() + 5_000
|
||||
async function waitForOutput(operation: PtySendOperation, expected: string, timeoutMs = 2_000): Promise<void> {
|
||||
const deadline = Date.now() + timeoutMs
|
||||
let output = ''
|
||||
while (!output.includes(expected) && Date.now() < deadline) {
|
||||
output += operation.readOutput().delta
|
||||
@@ -146,26 +146,25 @@ describe('pty-local real shell', () => {
|
||||
expect(() => process.kill(pid, 0)).toThrow()
|
||||
}, 10_000)
|
||||
|
||||
it('cancels a raw-mode foreground process with a real SIGINT', async () => {
|
||||
// A cold `python3` start can stay silent for longer than the 250 ms default
|
||||
// this harness uses, which settles the send as inferred_idle before the
|
||||
// interpreter prints its readiness marker; the marker then reaches only the
|
||||
// scrollback and waitForOutput sees the echoed command line alone. Raise the
|
||||
// silence bound, and the absolute bound above it, so process startup cannot
|
||||
// end the send it belongs to.
|
||||
const { ctx, agent } = await harness('danger-full-access', { idleSilenceMs: 4_000, timeoutMs: 6_000 })
|
||||
it('cancels a slow-starting raw-mode foreground process with a real SIGINT', async () => {
|
||||
const { ctx, agent } = await harness('danger-full-access', {
|
||||
idleSilenceMs: 10_000,
|
||||
timeoutMs: 15_000,
|
||||
})
|
||||
const created = await ctx.pty.spawn(agent, { type: 'shell' })
|
||||
const controller = new AbortController()
|
||||
const ready = 'RAW_READY'
|
||||
// Delay readiness beyond the shared harness's short send bound so this
|
||||
// process test owns enough slack for loaded macOS startup and shell echo.
|
||||
// The interactive shell echoes the command, so only child output may contain the readiness marker.
|
||||
const command = 'python3 -c \'import signal,sys,termios,time; signal.signal(signal.SIGINT, lambda *_: (print("SIGINT_SEEN", flush=True), sys.exit(0))); attrs=termios.tcgetattr(0); attrs[3] &= ~termios.ISIG; termios.tcsetattr(0, termios.TCSANOW, attrs); print("RAW_" + "READY", flush=True); time.sleep(60)\''
|
||||
const command = 'python3 -c \'import signal,sys,termios,time; signal.signal(signal.SIGINT, lambda *_: (print("SIGINT_SEEN", flush=True), sys.exit(0))); attrs=termios.tcgetattr(0); attrs[3] &= ~termios.ISIG; termios.tcsetattr(0, termios.TCSANOW, attrs); time.sleep(2.1); print("RAW_" + "READY", flush=True); time.sleep(60)\''
|
||||
expect(command).not.toContain(ready)
|
||||
const foreground = ctx.pty.startSend(agent, created.sessionId, {
|
||||
text: command,
|
||||
submit: true,
|
||||
signal: controller.signal,
|
||||
})
|
||||
await waitForOutput(foreground, ready)
|
||||
await waitForOutput(foreground, ready, 15_000)
|
||||
controller.abort()
|
||||
const result = await foreground.done
|
||||
expectReadyForNextSend(result.waitReason)
|
||||
|
||||
Reference in New Issue
Block a user