fix(pty): make the foreground-handoff grace a config field
The local PTY readiness poll held its inferred_idle fallback for exactly one pollIntervalMs after a prompt marker, so a bash foreground handoff that lands on the silence boundary only wins the exact stdin_read attribution when the kernel publishes it inside that single poll. On a slow or loaded host it does not, and the attribution flips. handoffGraceMs replaces the hardcoded one-poll window as a validated, deployment-owned config field defaulting to 500ms, rejected at load when it cannot contain one readiness poll. Real-shell tests that interrupt a send now assert the session is usable again rather than which readiness tier observed the handoff, because no fixed grace removes the race.
This commit is contained in:
@@ -88,7 +88,7 @@ function config(overrides: Partial<ResolvedConfig> = {}): ResolvedConfig {
|
||||
return {
|
||||
backendType: 'shell', shellPath: '/bin/bash', shellArgs: [], rows: 24, cols: 80,
|
||||
scrollbackLines: 10, scrollbackMaxBytes: 128, maxReadBytes: 64,
|
||||
pollIntervalMs: 10, exactProbeAfterMs: 20, idleSilenceMs: 50, timeoutMs: 100,
|
||||
pollIntervalMs: 10, exactProbeAfterMs: 20, idleSilenceMs: 50, handoffGraceMs: 10, timeoutMs: 100,
|
||||
disposeGraceMs: 20,
|
||||
...overrides,
|
||||
}
|
||||
@@ -307,6 +307,27 @@ describe('LocalPtySession readiness and output', () => {
|
||||
expect((await operation.done).waitReason).toBe('stdin_read')
|
||||
})
|
||||
|
||||
it('holds the idle fallback for the configured handoff grace, not one poll', async () => {
|
||||
vi.useFakeTimers()
|
||||
const terminal = new FakeTerminal()
|
||||
const inspector = new FakeInspector()
|
||||
const session = new LocalPtySession(terminal.asPty(), inspector, config({ handoffGraceMs: 40 }))
|
||||
await initialize(session, terminal)
|
||||
|
||||
const operation = session.startSend({ text: 'run', submit: true })
|
||||
let settled = false
|
||||
void operation.done.then(() => { settled = true })
|
||||
inspector.pgid = 789
|
||||
terminal.emitData('\x1b]133;D;0\x07dsh> ')
|
||||
// One poll past the silence bound would already have settled inferred_idle.
|
||||
await vi.advanceTimersByTimeAsync(70)
|
||||
expect(settled).toBe(false)
|
||||
|
||||
inspector.pgid = 456
|
||||
await vi.advanceTimersByTimeAsync(10)
|
||||
expect((await operation.done).waitReason).toBe('stdin_read')
|
||||
})
|
||||
|
||||
it('falls back to inferred idle when a foreground child emits an inherited prompt marker', async () => {
|
||||
vi.useFakeTimers()
|
||||
const terminal = new FakeTerminal()
|
||||
|
||||
Reference in New Issue
Block a user