mirror of
https://github.com/stablyai/orca.git
synced 2026-11-03 02:27:43 +00:00
* fix(mobile): preserve long terminal and chat pastes Agents can keep only the last ~1 KiB read of unframed input (#13332). Frame long single-line chat messages like multi-line ones, and frame Live input appends over 1 KiB (an OS paste) with the terminal's paste rule, including held text flushed before a terminal click. Co-authored-by: Carlos Baraza Haro <carlos.baraza@ieee.org> * fix(mobile): end the live input session after a framed paste Agents such as Codex collapse a framed paste into one token, so later per-character erases from the field mirror desynced the line. A long append to a terminal in bracketed paste mode now commits the whole field in one frame and clears the field, recording it like other session ends so a late native echo cannot send the paste twice; without that mode the paste stays unframed and editable. Chat sends measure the sanitized bytes when deciding to frame. --------- Co-authored-by: Carlos Baraza Haro <carlos.baraza@ieee.org> Co-authored-by: Kelvin Amoaba <97001695+AmoabaKelvin@users.noreply.github.com>
36 lines
1.3 KiB
TypeScript
36 lines
1.3 KiB
TypeScript
/** Keep renderer pastes and host launch prompts framed identically. */
|
|
|
|
const ESCAPE = '\u001b'
|
|
export const BRACKETED_PASTE_START = `${ESCAPE}[200~`
|
|
export const BRACKETED_PASTE_END = `${ESCAPE}[201~`
|
|
// Agents can keep only the last read of unframed input longer than this (#13332).
|
|
export const UNFRAMED_AGENT_INPUT_MAX_BYTES = 1024
|
|
|
|
// Replace embedded ESC with printable ␛ (U+241B) so pasted framing bytes cannot close the frame and execute its tail.
|
|
export function sanitizeBracketedPasteText(text: string): string {
|
|
let escapeIndex = text.indexOf(ESCAPE)
|
|
if (escapeIndex === -1) {
|
|
return text
|
|
}
|
|
|
|
let sanitized = ''
|
|
let start = 0
|
|
while (escapeIndex !== -1) {
|
|
sanitized += `${text.slice(start, escapeIndex)}␛`
|
|
start = escapeIndex + ESCAPE.length
|
|
escapeIndex = text.indexOf(ESCAPE, start)
|
|
}
|
|
return sanitized + text.slice(start)
|
|
}
|
|
|
|
export function normalizeTerminalPasteLineEndings(text: string): string {
|
|
// Why: xterm's native paste path converts every clipboard newline to CR.
|
|
// Direct frames must match it or ConPTY TUIs can treat raw LF as submit.
|
|
return text.replace(/\r?\n/g, '\r')
|
|
}
|
|
|
|
export function wrapTerminalBracketedPasteText(text: string): string {
|
|
const normalizedText = normalizeTerminalPasteLineEndings(text)
|
|
return `${BRACKETED_PASTE_START}${sanitizeBracketedPasteText(normalizedText)}${BRACKETED_PASTE_END}`
|
|
}
|