mirror of
https://github.com/stablyai/orca.git
synced 2026-09-22 16:02:32 +00:00
* fix(native-chat): mirror multi-line launch drafts into the chat composer
seedNativeChatLaunchDraftForAgentTab rejected any text containing a newline,
so every Linear launch ("Linked Linear issue: X\n<url>") and any GitHub launch
with a typed note was invisible in chat. The rejection existed because the send
path pre-cleared the TUI with a single Ctrl+U, which cannot clear a buffer with
embedded newlines.
Orca injects the draft itself, so when the composer still holds exactly what was
injected the buffer already IS the message: the send becomes the submit key
alone — no clear, no paste, nothing that can concatenate, and multi-line submits
as one turn for free. Only the edited case needs real buffer replacement, and
that now clears every line and verifies against the agent's rendered input line
instead of firing blind.
Measured on real PTYs against Claude Code and codex (both agree exactly):
clearing N logical lines costs 2N-1 Ctrl+U. See src/shared/agent-tui-input-clear.ts
for the law, the sequences that do NOT work, and why an upper bound is safe.
* fix(native-chat): send the mobile clear burst as its own write
Live QA caught the bundled form failing: a multi-line burst prefixed onto the
body in the SAME terminal.send reached the agent as LITERAL Ctrl+U characters,
so the parked draft survived and the message arrived as
draft + 21x \x15 + body. Sending the burst as its own non-submitting write —
the shape the image paste has always used — clears as intended.
The body write's own single-Ctrl+U prefix is dropped once that dedicated clear
ran, for the same reason: a Ctrl+U immediately followed by body text in one
write lands as a literal control character and headed the received message.
Re-verified live end to end: received prompt is exactly the draft, one turn,
zero control characters.
* test(native-chat): invert the multi-line Linear launch-draft mirror expectation
The Linear work-item launch seeds `Linked Linear issue: ENG-42\n<url>\n`.
This test pinned the pre-relaxation rule (multi-line drafts withheld), which
the send path no longer needs now that it submits the TUI buffer in place or
clears every line first — so it asserted the exact behavior the fix removes.
Assert the seeded payload instead of absence, so the test fails if the mirror
regresses to single-line-only.
* fix(native-chat): preserve launch draft send contents
* fix(native-chat): preserve confirmed send queue ordering
* fix(native-chat): preserve send pacing after renderer stalls
* test(native-chat): align activation with multiline draft mirroring
* fix(native-chat): clear launch drafts from any cursor
* fix(native-chat): retire mobile-consumed launch drafts
* test(mobile): stabilize QR capacity boundary fixture
46 lines
2.0 KiB
TypeScript
46 lines
2.0 KiB
TypeScript
// Clearing an agent TUI's input buffer when it may hold MORE THAN ONE line.
|
|
//
|
|
// Shared by desktop native chat and mobile: the law below is a property of the
|
|
// agent TUIs (Claude Code, codex), not of either client.
|
|
|
|
/** Ctrl+U — clears toward the start of the input buffer. */
|
|
export const AGENT_TUI_CLEAR_INPUT_LINE = '\x15'
|
|
|
|
/** Ctrl+K — clears toward the end of the input buffer. */
|
|
export const AGENT_TUI_CLEAR_INPUT_FORWARD = '\x0b'
|
|
|
|
/** Clear up to `lineCount` logical lines from any cursor position. */
|
|
export function buildAgentTuiClearInput(lineCount: number): string {
|
|
const lines = Math.max(1, Math.min(AGENT_TUI_CLEAR_MAX_LINES, Math.floor(lineCount)))
|
|
const repetitions = 2 * lines - 1
|
|
return (
|
|
AGENT_TUI_CLEAR_INPUT_LINE.repeat(repetitions) +
|
|
AGENT_TUI_CLEAR_INPUT_FORWARD.repeat(repetitions)
|
|
)
|
|
}
|
|
|
|
/**
|
|
* Headroom over the line count Orca knows about. The text Orca injected is a
|
|
* LOWER BOUND on what the buffer holds — the user can also type straight into
|
|
* the TUI line — so the count is deliberately biased upward. Overshoot is free:
|
|
* 41 Ctrl+U against a 1-line buffer measured perfectly clean on both agents,
|
|
* and an undershoot is what leaves residue to glue onto the next message.
|
|
*/
|
|
export const AGENT_TUI_CLEAR_LINE_SLACK = 8
|
|
|
|
/** Bounds the burst so a pathological draft cannot emit an unbounded write. */
|
|
export const AGENT_TUI_CLEAR_MAX_LINES = 40
|
|
|
|
/** Widest burst we ever send — the remedy when a clear is not observed to land. */
|
|
export const AGENT_TUI_CLEAR_INPUT_MAX = buildAgentTuiClearInput(AGENT_TUI_CLEAR_MAX_LINES)
|
|
|
|
/** Logical lines in `text`. Visual wrapping is irrelevant to the clear cost. */
|
|
export function countAgentTuiInputLines(text: string): number {
|
|
return text.split(/\r\n|\r|\n/).length
|
|
}
|
|
|
|
/** Clear bytes for a buffer believed to hold `text`, with slack for TUI-side edits. */
|
|
export function buildAgentTuiClearInputForText(text: string): string {
|
|
return buildAgentTuiClearInput(countAgentTuiInputLines(text) + AGENT_TUI_CLEAR_LINE_SLACK)
|
|
}
|