mirror of
https://github.com/stablyai/orca.git
synced 2026-10-20 08:03:28 +00:00
fix(terminal): serialize bracketed paste operations
This commit is contained in:
@@ -6,6 +6,7 @@ import {
|
||||
TERMINAL_REMOTE_PASTE_OPERATION_TIMEOUT_MS
|
||||
} from './terminal-paste-limits'
|
||||
import { runTerminalPasteOperationWithTimeout } from './terminal-paste-operation-timeout'
|
||||
import { runTerminalPtyInputTransaction } from './terminal-pty-input-transaction'
|
||||
import type {
|
||||
TerminalPasteExecutionReason,
|
||||
TerminalPasteExecutionResult,
|
||||
@@ -24,6 +25,15 @@ type ExecuteTerminalPastePlanArgs = {
|
||||
}
|
||||
|
||||
export async function executeTerminalPastePlan(
|
||||
plan: TerminalPastePlan,
|
||||
args: ExecuteTerminalPastePlanArgs
|
||||
): Promise<TerminalPasteExecutionResult> {
|
||||
return await runTerminalPtyInputTransaction(plan.target.ptyId, () =>
|
||||
executeTerminalPastePlanNow(plan, args)
|
||||
)
|
||||
}
|
||||
|
||||
async function executeTerminalPastePlanNow(
|
||||
plan: TerminalPastePlan,
|
||||
{
|
||||
pasteText,
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
import { describe, expect, it } from 'vitest'
|
||||
|
||||
import { sendAgentDraftPasteContent } from '@/lib/agent-draft-paste-content'
|
||||
import {
|
||||
BRACKETED_PASTE_END,
|
||||
BRACKETED_PASTE_START,
|
||||
wrapTerminalBracketedPasteText
|
||||
} from './terminal-bracketed-paste'
|
||||
import { executeTerminalPastePlan, planTerminalPaste } from './terminal-paste-coordinator'
|
||||
|
||||
describe('terminal paste operation ordering', () => {
|
||||
it('keeps startup context outside an active chunked user paste frame', async () => {
|
||||
const writes: string[] = []
|
||||
let startupDraft: Promise<boolean> | null = null
|
||||
const writePty = async (data: string): Promise<boolean> => {
|
||||
writes.push(data)
|
||||
return true
|
||||
}
|
||||
const userPaste = planTerminalPaste({
|
||||
text: 'user-line-1\nuser-line-2',
|
||||
source: 'keyboard',
|
||||
target: {
|
||||
kind: 'terminal',
|
||||
paneId: 1,
|
||||
leafId: 'leaf-1',
|
||||
ptyId: 'pty-1',
|
||||
runtime: {
|
||||
platform: 'win32',
|
||||
runtimeKey: 'local:win32',
|
||||
kind: 'local',
|
||||
isWindowsConpty: true
|
||||
}
|
||||
},
|
||||
terminalBracketedPasteMode: true,
|
||||
maxDirectBytes: 4,
|
||||
maxChunkBytes: 12
|
||||
})
|
||||
|
||||
const userPasteResult = await executeTerminalPastePlan(userPaste, {
|
||||
pasteText: () => {},
|
||||
writePty,
|
||||
yieldToEventLoop: async () => {
|
||||
startupDraft ??= sendAgentDraftPasteContent(null, 'pty-1', 'GENERATED_CONTEXT', writePty)
|
||||
}
|
||||
})
|
||||
await startupDraft
|
||||
|
||||
expect(userPasteResult.status).toBe('pasted')
|
||||
expect(writes).toEqual([
|
||||
BRACKETED_PASTE_START,
|
||||
'user-line-1\r',
|
||||
'user-line-2',
|
||||
BRACKETED_PASTE_END,
|
||||
wrapTerminalBracketedPasteText('GENERATED_CONTEXT')
|
||||
])
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,27 @@
|
||||
const transactionTails = new Map<string, Promise<void>>()
|
||||
|
||||
export async function runTerminalPtyInputTransaction<T>(
|
||||
ptyId: string | null | undefined,
|
||||
operation: () => Promise<T>
|
||||
): Promise<T> {
|
||||
if (!ptyId) {
|
||||
return await operation()
|
||||
}
|
||||
|
||||
const previous = transactionTails.get(ptyId) ?? Promise.resolve()
|
||||
let release!: () => void
|
||||
const current = new Promise<void>((resolve) => {
|
||||
release = resolve
|
||||
})
|
||||
transactionTails.set(ptyId, current)
|
||||
|
||||
await previous
|
||||
try {
|
||||
return await operation()
|
||||
} finally {
|
||||
release()
|
||||
if (transactionTails.get(ptyId) === current) {
|
||||
transactionTails.delete(ptyId)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6,6 +6,7 @@ import {
|
||||
normalizeTerminalPasteLineEndings,
|
||||
wrapTerminalBracketedPasteText
|
||||
} from '@/components/terminal-pane/terminal-bracketed-paste'
|
||||
import { runTerminalPtyInputTransaction } from '@/components/terminal-pane/terminal-pty-input-transaction'
|
||||
import { sendRuntimePtyInputVerified } from '@/runtime/runtime-terminal-inspection'
|
||||
|
||||
// Why: bracketed paste markers let supported TUIs treat generated prompt text
|
||||
@@ -26,6 +27,18 @@ export async function sendAgentDraftPasteContent(
|
||||
content: string,
|
||||
writePty?: AgentDraftPtyInputWriter,
|
||||
canContinue?: () => boolean
|
||||
): Promise<boolean> {
|
||||
return await runTerminalPtyInputTransaction(ptyId, () =>
|
||||
sendAgentDraftPasteContentNow(settings, ptyId, content, writePty, canContinue)
|
||||
)
|
||||
}
|
||||
|
||||
async function sendAgentDraftPasteContentNow(
|
||||
settings: Pick<GlobalSettings, 'activeRuntimeEnvironmentId'> | null | undefined,
|
||||
ptyId: string,
|
||||
content: string,
|
||||
writePty?: AgentDraftPtyInputWriter,
|
||||
canContinue?: () => boolean
|
||||
): Promise<boolean> {
|
||||
if (content.length > AGENT_DRAFT_PASTE_MAX_BYTES || canContinue?.() === false) {
|
||||
return false
|
||||
|
||||
Reference in New Issue
Block a user