Files
orca/src/main/native-chat/transcript-stream-lines.ts
T
BingZandJinwoo Hong 96d1fa1d62 fix(grok): clipboard, native chat, hooks, sessions, ConPTY KKP (#7944)
* fix(grok): restore clipboard and native-chat parity

Grok CLI already supports argv prompts, OSC 52 copy, and image paste chips.
Orca was blocking those paths: stdin-after-start keystroke injection, OSC 52
writes default-off, image-attachment denylist, and native-chat allowlist.

- Launch Grok with positional argv prompts
- Default OSC 52 TUI clipboard writes on (still user-toggleable)
- Treat Grok as image-attachment capable
- Parse ~/.grok/.../chat_history.jsonl for native chat

OSC 52 clipboard *query* remains ignored by design (host clipboard exfil risk);
xAI docs only require OSC 52 write for remote copy.

* fix(grok): sync OSC 52 docs and locale catalog with default-on

Update terminalAllowOsc52Clipboard type docs for the true default, and
refresh locale strings so settings UI mentions Grok alongside other TUIs.

* fix(grok): tool hook matcher, StopFailure, previews, AskUser waiting

Grok tool-event matchers are real regexes; bare `*` failed as match-all.
Install `.*` for Pre/Post tool hooks, add StopFailure for API-error ends,
recognize Grok-native tool input keys, and map ask_user_question PreToolUse
to waiting with interactivePrompt (Kimi-style live card path).

* fix(grok): resolve chat_history under GROK_HOME and long-cwd layouts

Centralize Grok session path helpers so hooks and native-chat honor
GROK_HOME and find chat_history.jsonl by session id when the cwd group
is slug-encoded (encoded name > 255 bytes) instead of only
encodeURIComponent(cwd).

* fix(terminal): keep Kitty keyboard for Grok on Windows ConPTY

Local Windows ConPTY withholds KKP so CSI-u-blind CLIs (e.g. Antigravity)
keep Enter/nav working (#2434). Grok needs KKP for Ctrl+Enter interject and
modified-Enter newline chords; blanking the advertisement for Orca-launched
Grok left those actions broken.

- Prefer KKP when tuiAgent is grok despite ConPTY withhold
- Wire launchAgent from tab/startup into keyboard protocol options

* fix(grok): restore OSC52 default-off, split decoders, honor GROK_HOME hooks

- Keep terminalAllowOsc52Clipboard default false (clipboard exfil risk)
- Split transcript-line-decoders under max-lines without suppressions
- Install local Grok hooks under resolveGrokHomeDir() / GROK_HOME

* refactor(grok): share CLI home resolution

* fix(grok): harden terminal and native chat integration

* test(grok): align CI coverage with native chat support

---------

Co-authored-by: Jinwoo Hong <73622457+Jinwoo-H@users.noreply.github.com>
2026-07-10 13:16:55 -07:00

48 lines
1.5 KiB
TypeScript

import type { Readable } from 'node:stream'
import type { NativeChatMessage } from '../../shared/native-chat-types'
import { transcriptFallbackId } from './transcript-fallback-id'
type TranscriptDecoder = (line: string, fallbackId: string) => NativeChatMessage | null
export async function decodeTranscriptStream(
stream: Readable,
filePath: string,
start: number,
decode: TranscriptDecoder,
includeTrailingLine: boolean
): Promise<{ messages: NativeChatMessage[]; consumedBytes: number }> {
const messages: NativeChatMessage[] = []
let pending = ''
let consumedBytes = 0
for await (const chunk of stream) {
pending += typeof chunk === 'string' ? chunk : Buffer.from(chunk).toString('utf8')
let newlineIndex = pending.indexOf('\n')
while (newlineIndex !== -1) {
const segment = pending.slice(0, newlineIndex + 1)
decodeLine(segment.slice(0, -1), consumedBytes)
consumedBytes += Buffer.byteLength(segment, 'utf8')
pending = pending.slice(newlineIndex + 1)
newlineIndex = pending.indexOf('\n')
}
}
if (includeTrailingLine && pending.length > 0) {
decodeLine(pending, consumedBytes)
consumedBytes += Buffer.byteLength(pending, 'utf8')
}
return { messages, consumedBytes }
function decodeLine(rawLine: string, relativeOffset: number): void {
const line = rawLine.endsWith('\r') ? rawLine.slice(0, -1) : rawLine
if (!line) {
return
}
const message = decode(line, transcriptFallbackId(filePath, start + relativeOffset))
if (message) {
messages.push(message)
}
}
}