mirror of
https://github.com/stablyai/orca.git
synced 2026-10-30 16:47:34 +00:00
fix(orchestration): re-press Enter when dsh's launchpad refuses a worker's first dispatch (#33499)
dsh worker dispatches (and other dsh agent-prompt sends) now get #33316's bounded Enter retry: dsh paints its launchpad before its session opens and refuses the first Enter with "still starting", so a fresh worker's task sat unsent. The retry only re-presses while the launchpad still holds the same prompt and stops once a turn starts. Fixes #30778 Co-authored-by: Jinwoo Hong <73622457+Jinwoo-H@users.noreply.github.com>
This commit is contained in:
@@ -3,6 +3,7 @@ import { join } from 'node:path'
|
||||
import { afterEach, describe, expect, it, vi } from 'vitest'
|
||||
import { AGENT_PROMPT_BRACKETED_PASTE_END } from '../../shared/agent-prompt-injection'
|
||||
import { createAgentPromptSubmissionRuntime } from './agent-prompt-submission-runtime-test-fixture'
|
||||
import { dispatchPreambleSendOptions } from './orchestration/preamble'
|
||||
|
||||
vi.mock('../git/worktree', () => {
|
||||
const worktrees = [
|
||||
@@ -30,7 +31,7 @@ const DSH_TURN_STARTED = '\x1b]0;⠂ 🐋 \x07\x1b]7501;state=working:app=dsh-tu
|
||||
describe('a dsh launch prompt whose first Enter the launchpad refused', () => {
|
||||
afterEach(() => vi.useRealTimers())
|
||||
|
||||
async function launchDsh(screen: string, acceptingEnter: number) {
|
||||
async function launchDsh(screen: string, acceptingEnter: number, dispatch = false) {
|
||||
vi.useFakeTimers()
|
||||
const { runtime, handle, writes } = await createAgentPromptSubmissionRuntime(
|
||||
(runtime, data) => {
|
||||
@@ -45,13 +46,19 @@ describe('a dsh launch prompt whose first Enter the launchpad refused', () => {
|
||||
)
|
||||
// The captures were painted on a 120x40 grid.
|
||||
vi.spyOn(runtime, 'getTerminalSize').mockReturnValue({ cols: 120, rows: 40 })
|
||||
const submission = runtime.sendTerminalAgentPrompt(handle, 'say hi in one word', {
|
||||
inputKind: 'launch',
|
||||
composerReady: true,
|
||||
acceptQueued: true,
|
||||
requestId: 'dsh-launch',
|
||||
observationTimeoutMs: 0
|
||||
})
|
||||
const submission = runtime.sendTerminalAgentPrompt(
|
||||
handle,
|
||||
'say hi in one word',
|
||||
dispatch
|
||||
? dispatchPreambleSendOptions('dsh-dispatch')
|
||||
: {
|
||||
inputKind: 'launch',
|
||||
composerReady: true,
|
||||
acceptQueued: true,
|
||||
requestId: 'dsh-launch',
|
||||
observationTimeoutMs: 0
|
||||
}
|
||||
)
|
||||
await vi.advanceTimersByTimeAsync(40_000)
|
||||
await expect(submission).resolves.toMatchObject({ accepted: true })
|
||||
return {
|
||||
@@ -80,4 +87,12 @@ describe('a dsh launch prompt whose first Enter the launchpad refused', () => {
|
||||
pastes: 1
|
||||
})
|
||||
})
|
||||
|
||||
// #30778: a fresh worker's tui-idle settles on dsh 0.17's boot `OSC 7501 idle`, before its session opens.
|
||||
it('presses Enter again for a worker dispatch the launchpad refused', async () => {
|
||||
await expect(launchDsh(fixture('launchpad-draft-starting'), 3, true)).resolves.toEqual({
|
||||
enters: 3,
|
||||
pastes: 1
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -182,8 +182,7 @@ export abstract class OrcaRuntimeWithWriteTerminalAgentPrompt extends OrcaRuntim
|
||||
heldBudgetMs: getAgentPromptHeldSubmitBudgetMs(
|
||||
writeHostPlatform,
|
||||
this.getPtyAgent(ptyId),
|
||||
pastePayload,
|
||||
options.composerReady === true
|
||||
pastePayload
|
||||
)
|
||||
}))
|
||||
const effectTimeoutMs = resolveAgentPromptEffectTimeoutMs(this.getPtyAgent(ptyId))
|
||||
|
||||
@@ -76,18 +76,17 @@ export function getMaxTerminalPasteBytesForIngestMs(
|
||||
// Codex 0.162.1 on a low-spec Win11 host took 5.1 s for 1,849 chars and 21.1 s for 9,249 (~2.3 ms/char).
|
||||
const WINDOWS_INPUT_RECORD_PASTE_MS_PER_CHAR = 3
|
||||
|
||||
/** How long a held prompt may still need Enter: an input-record TUI's paste burst, or a launch composer not yet submit-ready. */
|
||||
/** How long a held prompt may still need Enter: an input-record TUI's paste burst, or a composer not yet submit-ready. */
|
||||
export function getAgentPromptHeldSubmitBudgetMs(
|
||||
platform: NodeJS.Platform,
|
||||
agent: TerminalAgent | null | undefined,
|
||||
pastePayload: string,
|
||||
composerReady: boolean
|
||||
pastePayload: string
|
||||
): number {
|
||||
if (!isTuiAgent(agent)) {
|
||||
return 0
|
||||
}
|
||||
const config = TUI_AGENT_CONFIG[agent]
|
||||
const launchMs = composerReady ? (config.submitRetryBudgetMs ?? 0) : 0
|
||||
const startupMs = config.submitRetryBudgetMs ?? 0
|
||||
const pasteBurstMs =
|
||||
platform === 'win32' && config.windowsInputRecordPasteNewline
|
||||
? pastePayload.length * WINDOWS_INPUT_RECORD_PASTE_MS_PER_CHAR
|
||||
@@ -97,7 +96,7 @@ export function getAgentPromptHeldSubmitBudgetMs(
|
||||
const capMs =
|
||||
AGENT_PROMPT_EFFECT_TIMEOUT_MS -
|
||||
2 * getTerminalPasteIngestMs(platform, Buffer.byteLength(pastePayload, 'utf8'))
|
||||
return Math.max(0, Math.min(Math.max(launchMs, pasteBurstMs), capMs))
|
||||
return Math.max(0, Math.min(Math.max(startupMs, pasteBurstMs), capMs))
|
||||
}
|
||||
|
||||
// Codex dedupes a repeated placeholder label as `[Pasted Content N chars] #2`.
|
||||
|
||||
@@ -64,7 +64,7 @@ export type TuiAgentConfig = {
|
||||
composerReadyCaptures?: readonly string[]
|
||||
/** Delay before one extra blind submit Enter, for agents that render their composer before Enter is live (codex); a no-op if the first Enter landed. */
|
||||
submitRetryDelayMs?: number
|
||||
/** How long a launch keeps re-pressing Enter at `submitRetryDelayMs` while its composer holds the prompt, for TUIs that paint it before their session can submit (dsh). */
|
||||
/** How long an agent-prompt send keeps re-pressing Enter at `submitRetryDelayMs` while its composer holds the prompt, for TUIs that paint it before their session can submit (dsh). */
|
||||
submitRetryBudgetMs?: number
|
||||
/** Extra ms per logical prompt line before Enter, for TUIs that expand multiline paste slowly (antigravity). */
|
||||
submitLineSettleMsPerLine?: number
|
||||
|
||||
Reference in New Issue
Block a user