Files
orca/src/shared/terminal-bell-detector.ts
Jinjing 1def694e80 Add native chat skill picker with host-aware discovery (#9480)
* Add native chat skill and command picker with host-aware discovery

Adds a unified, keyboard-first skill and command picker to native chat that:

- Uses agent-native invocation syntax (slash for Claude/OpenClaude/Grok, dollar for Codex)
- Discovers skills only on the pane's execution host (local, WSL, SSH-unavailable, or runtime)
- Groups or separates commands and skills per agent configuration
- Deduplicates by canonical path but preserves visibility through all contributing roots
- Handles IME composition, loading states, and errors without claiming PTY-level control
- Records picker telemetry (open, item accepted, send classification, discovery outcomes)
- Extends shared agent profiles to define per-agent skill grammars and source ownership

* Remove obsolete reference and design documentation

Clean up stale design specs, implementation plans, and investigation notes from
docs/reference/. These documents predate the current implementation and are no
longer actively maintained or referenced by the codebase.

* Extract shared skill discovery utilities and add skill invocation envelo

- Move skill comparison and source classification to shared module for native/WSL reuse
- Extract display text sanitization to prevent control/zero-width character spoofing
- Add native-chat command envelope parser and surfacer for skill invocations
- Extend discovery timeout backstop to account for WSL metadata read sequence

* Localize skill picker UI for Spanish, Japanese, Korean, Chinese

Translate skill picker UI strings including commands, skills, loading
states, error messages, and scope labels for the new skill picker feature
across four language locales.

* Fix skill picker bugs and improve code robustness

- Fix i18n plural handling: rename `count` to `sourceCount` to prevent unintended plural-key resolution in localized strings
- Fix skill discovery array mutations: copy `root.providers` to prevent bugs during dedup merge
- Fix image attachments being silently dropped when message text starts with /skill or agent prefix
- Extract `quoteBashString` utility for WSL command code reuse across builders
- Add line-separator safety characters (0x2028/0x2029) to skill display filter
- Remove stale doc reference links and clarify inline comments

* Add reference docs for git compatibility and headless Linux server setup

Track previously untracked operational guides in `docs/reference/` that
explain Git binary compatibility requirements across host types and how to
run `orca serve` on headless Linux. Update AGENTS.md and README.md to link
to these references.
2026-07-19 17:31:34 -07:00

117 lines
3.8 KiB
TypeScript

/**
* Stateful BEL detector that correctly ignores BEL (0x07) bytes
* occurring inside OSC escape sequences.
*
* Shared between the renderer transport processor and main's per-PTY
* side-effect tracker: bell semantics must not drift between the two parsing
* authorities.
*
* Why stateful: PTY data arrives in arbitrary chunks, so an OSC sequence
* may span multiple calls. The detector tracks in-progress escape state
* across invocations so a BEL used as an OSC terminator is never
* misinterpreted as a terminal bell.
*
* CAN (0x18) / SUB (0x1A) handling: per ECMA-48, these bytes cancel any
* in-progress escape sequence. Without this, a malformed or truncated OSC
* string (e.g. from a crashed TUI) would pin `inOsc = true` indefinitely
* and silently drop the next real BEL as if it were an OSC terminator.
*
* reset(): callers should invoke this whenever the underlying byte stream
* is replaced (e.g. on PTY detach/attach) so state from a previous stream
* that ended mid-escape does not leak into the next stream.
*/
export type BellDetector = {
/** `hints.containsOscIntroducer` lets a caller that already scanned for
* `\x1b]` (the title-extraction gate) share the result instead of paying
* a second includes() pass per chunk on the hot path. */
chunkContainsBell(data: string, hints?: { containsOscIntroducer?: boolean }): boolean
reset(): void
}
export function createBellDetector(): BellDetector {
let pendingEscape = false
let inOsc = false
let pendingOscEscape = false
return {
chunkContainsBell(data: string, hints: { containsOscIntroducer?: boolean } = {}): boolean {
if (!inOsc && !pendingEscape && !data.includes('\x07')) {
// Why: CSI/plain chunks with no BEL and no OSC start cannot affect
// bell state; avoid walking every byte of normal terminal output.
if (!(hints.containsOscIntroducer ?? data.includes('\x1b]'))) {
pendingEscape = data.endsWith('\x1b')
return false
}
}
for (let i = 0; i < data.length; i += 1) {
const char = data[i]
if (inOsc) {
if (char === '\x18' || char === '\x1a') {
// ECMA-48 escape-cancel codes — abort the in-progress OSC string
// so a malformed/truncated OSC does not swallow the next BEL.
inOsc = false
pendingOscEscape = false
continue
}
if (pendingOscEscape) {
pendingOscEscape = char === '\x1b'
if (char === '\\') {
inOsc = false
pendingOscEscape = false
}
continue
}
if (char === '\x07') {
inOsc = false
continue
}
pendingOscEscape = char === '\x1b'
continue
}
if (pendingEscape) {
if (char === '\x18' || char === '\x1a') {
// ECMA-48 escape-cancel codes also abort a pending ESC.
pendingEscape = false
continue
}
pendingEscape = false
if (char === ']') {
inOsc = true
pendingOscEscape = false
} else if (char === '\x1b') {
pendingEscape = true
} else if (char === '\x07') {
// A bare ESC is not a valid introducer for any sequence that
// consumes a following BEL. Treat the BEL as a real terminal
// bell rather than silently swallowing it with the orphan ESC.
return true
}
continue
}
if (char === '\x1b') {
pendingEscape = true
continue
}
if (char === '\x07') {
return true
}
}
return false
},
reset(): void {
pendingEscape = false
inOsc = false
pendingOscEscape = false
}
}
}