文件最后提交记录最后更新时间
fix(tui): anchor splitReasoning unclosed-tag regex to start of input (#29426) splitReasoning() strips paired <think>…</think> blocks first, then runs an unclosed-trailing regex to catch reasoning that hasn't yet streamed its closer. That second regex was unanchored and greedy: new RegExp(<${tag}>([\\s\\S]*)$, 'i') So any literal <think> somewhere in prose — a model quoting the tag, a code example, or a stream-mid-tag before the closer arrives — consumed every paragraph after it to EOF. User-visible symptom: "TUI eats last paragraph of output," both during streaming and on settled turns. Real reasoning streams always lead the message (that's the only place an unclosed opener can legitimately appear during streaming). Anchor the regex to ^\s* so mid-prose mentions of the tag are preserved. Empirical repro before the fix: splitReasoning('final answer paragraph one.\n\n<think>internal note\n\nfinal answer paragraph two.') → text: 'final answer paragraph one.' ← paragraph two GONE After: → text: 'final answer paragraph one.\n\n<think>internal note\n\nfinal answer paragraph two.' Updated the existing trailing-unclosed test to lead with <think> (the real-world shape) and added a regression test pinning the mid-text case. ui-tui type-check clean, 808/808 vitest pass.15 天前
fix(tui): clipboard copy on linux/wayland probeLinuxCopy and copyNative in osc.ts await execFileNoThrow for wl-copy / xclip / xsel. Those tools double-fork a daemon that holds the system selection live, and the daemon inherits stdio pipes from spawn(stdio: 'pipe'). Node's 'close' event only fires when stdio is fully closed → the daemon keeps the pipes open → 'close' never fires → the await leaks past the timeout (kill(SIGTERM) on an already-exited child is a no-op, daemon survives). Result: linuxCopy cache stays undefined permanently, the actual copy never runs, ctrl-c silently does nothing on wayland/x11. Reproduced in isolation, confirmed across wl-copy and a daemonization-shaped fixture. Fix: add resolveOnExit option to execFileNoThrow. When set, the promise settles on the immediate child's 'exit' event instead of waiting for stdio drainage. Wired into both the probe and the actual copy spawns for every clipboard tool (pbcopy, wl-copy, xclip, xsel, clip). Tests: 5 new vitest cases covering daemon-style child handling, non-zero exit propagation, timeout behavior, and double-resolve guard. The forever-hang case is committed as it.skip with documentation so a reviewer can verify the bug by hand. 14 天前
Merge pull request #28829 from NousResearch/bb/tui-no-history-truncation fix(tui): render full assistant text in scrollback (no history truncation)16 天前
fix(tui): termux-gate scrollback preservation, touch-friendly defaults Adds a Termux runtime detection helper and gates three TUI defaults on it: - Skip the startup scrollback clear on Termux so users can review/copy earlier output after reopening the app. Desktop keeps the existing \x1b[2J\x1b[H\x1b[3J slate (AlternateScreen takes over there anyway). - Default INLINE_MODE on under Termux: primary-buffer rendering makes long-thread review and copy/paste much less fragile when users background/foreground the app. Override with HERMES_TUI_INLINE=0/1. - Default mouse tracking off under Termux so touch selection isn't intercepted by terminal mouse protocols. Explicit override via HERMES_TUI_MOUSE_TRACKING=0/1; legacy HERMES_TUI_DISABLE_MOUSE still works on desktop. Detection is purely env-based (TERMUX_VERSION or PREFIX path) with an explicit opt-out HERMES_TUI_TERMUX_MODE=0 for debugging. Non-Termux platforms keep every existing default. Co-authored-by: adybag14-cyber <252811164+adybag14-cyber@users.noreply.github.com> 16 天前
fix(tui): copilot review on #16707 — naming, label consistency, esc priority - Rename removeAtremoveAtInPlace and document the mutation contract; the old name read like a non-mutating helper. - Hotkey table + queue header: use Ctrl+X / Esc to match the rest of the UI (was ⌃X / esc). - Render the queued header as a single template literal so JSX text-node whitespace can't sneak into the rendered line. - Make Esc while editing beat the terminal.hasSelection clear: the header promises 'Esc cancel', so an active selection shouldn't silently consume the keystroke. 1 个月前
fix(tui): stabilize sticky prompt tracking Keep the latest prompt sticky while the viewport is in live assistant output beyond history, and clear stale sticky state at the real bottom using fresh scroll height. 1 个月前
fix(tui): refresh virtual offsets after row resize (#20898)29 天前
fix(tui): anchor splitReasoning unclosed-tag regex to start of input (#29426) splitReasoning() strips paired <think>…</think> blocks first, then runs an unclosed-trailing regex to catch reasoning that hasn't yet streamed its closer. That second regex was unanchored and greedy: new RegExp(<${tag}>([\\s\\S]*)$, 'i') So any literal <think> somewhere in prose — a model quoting the tag, a code example, or a stream-mid-tag before the closer arrives — consumed every paragraph after it to EOF. User-visible symptom: "TUI eats last paragraph of output," both during streaming and on settled turns. Real reasoning streams always lead the message (that's the only place an unclosed opener can legitimately appear during streaming). Anchor the regex to ^\s* so mid-prose mentions of the tag are preserved. Empirical repro before the fix: splitReasoning('final answer paragraph one.\n\n<think>internal note\n\nfinal answer paragraph two.') → text: 'final answer paragraph one.' ← paragraph two GONE After: → text: 'final answer paragraph one.\n\n<think>internal note\n\nfinal answer paragraph two.' Updated the existing trailing-unclosed test to lead with <think> (the real-world shape) and added a regression test pinning the mid-text case. ui-tui type-check clean, 808/808 vitest pass.15 天前
refactor(tui): /clean pass across ui-tui — 49 files, −217 LOC Full codebase pass using the /clean doctrine (KISS/DRY, no one-off helpers, no variables-used-once, pure functional where natural, inlined obvious one-liners, killed dead exports, narrowed types, spaced JSX). All contracts preserved — no RPC method, event name, or exported type shape changed. app/ — 15 files, -134 LOC - inlined 4 one-off helpers (titleCase, isLong, statusToneFrom, focusOutside predicate) - stores to arrow-const style (buildUiState, buildTurnState, buildOverlayState plus get/patch/reset triplets) - functional slash/registry byName map (flatMap over for-loops) - dropped dead param live in cancelOverlayFromCtrlC - DRY'd duplicate shift() call in scrollWithSelection - consolidated sections.push calls in /help components/ — 12 files, -40 LOC - extracted inline prop types to interfaces at file bottom (13×) - inlined 6 one-off vars (pctLabel, logoW, heroW, cwd, title, hint) - promoted HEART_COLORS + OPTS/LABELS to module scope - JSX sibling spacing across 9 files - un-shadowed raw in textInput - components/thinking.tsx + components/markdown.tsx untouched (structurally load-bearing / edge-case-heavy) config content domain protocol/ — 8 files, -77 LOC - tightened 3 regexes (MOUSE_TRACKING, looksLikeSlashCommand, hasInterpolation — dropped stateful lastIndex dance) - dead export ParsedSlashCommand removed - MODES narrowed to as const, .find(m => m === s) replaces .includes() ? (as cast) : null - fortunes.ts hash via reduce - fmtDuration ternary chain - inlined aboveViewport predicate in viewport.ts hooks/ + lib/ — 9 files, -38 LOC - ANSI_RE via String.fromCharCode(27) + WS_RE lifted to module scope (no more eslint-disable no-control-regex) - compactPreview/edgePreview/thinkingPreview → ternary arrows - useCompletion: hoisted pathReplace, moved stale-ref guard earlier - useInputHistory: dropped useCallback wrapper (append is stable) - useVirtualHistory: replaced 4× any with unknown + narrow MeasuredNode interface + one cast site root TS — 3 files, -63 LOC - banner.ts: parseRichMarkup via matchAll instead of exec/lastIndex, artWidth via reduce - gatewayClient.ts: resolvePython candidate list collapse, inlined one-branch guards in dispatch/pushLog/drain/request - types.ts: alpha-sorted ActiveTool / Msg / SudoReq / SecretReq members eslint config - disabled react-hooks/exhaustive-deps on packages/hermes-ink/** (compiled by react/compiler, deps live in $[N] memo arrays that eslint can't introspect) and removed the now-orphan in-file disable directive in ScrollBox.tsx fixes (not from the cleaner pass) - useComposerState: unlinkSync(file) + try/catch → rmSync(file, { force: true }) — kills the no-empty lint error and is more idiomatic - useConfigSync: added setBellOnComplete + setVoiceEnabled to the two useEffect dep arrays (they're stable React setState setters; adding is safe and silences exhaustive-deps) verification - npx eslint src/ packages/ → 0 errors, 0 warnings - npm run type-check → clean - npm test → 50/50 - npm run build → 394.8kb ink-bundle.js, 11ms esbuild - pytest tests/tui_gateway/ tests/test_tui_gateway_server.py tests/hermes_cli/test_tui_resume_flow.py tests/hermes_cli/test_tui_npm_install.py → 57/57 1 个月前
fix(tui): keep Ink displayCursor in sync with fast-echo writes so cursor stops drifting (#26717) * fix(tui): keep Ink displayCursor in sync with fast-echo writes so cursor stops drifting TextInput's fast-echo bypass writes characters directly to stdout to avoid waiting on a React re-render for each keystroke. The hardware cursor advances by text.length cells, but Ink's cached displayCursor (the basis for the next frame's relative cursor-move preamble in log-update) stayed unchanged. When ANY unrelated component re-rendered between the fast-echo write and the deferred composer setCur/setParent flush — status bar timer, streaming reasoning, etc. — the next frame's preamble emitted a relative cursor move from a stale parked position and the hardware cursor parked N cells offset from the actual caret. Visible symptom: extra whitespace between the just-typed character and the cursor block, intermittent, worse on long sessions during streaming. Alt-screen was immune because frames begin with absolute CSI H. This adds a small API in @hermes/ink: - Ink.noteExternalCursorAdvance(dx, dy?) — bumps displayCursor if set, otherwise seeds from frontFrame.cursor so the next preamble's relative move correctly cancels the external advance. No-op on alt-screen. - CursorAdvanceContext + useCursorAdvance() hook to expose it. TextInput then calls noteCursorAdvance(text.length) after the fast-echo stdout.write(text) append, and noteCursorAdvance(-1) after the fast-backspace \b \b sequence. Tests: 4 new vitest cases pin the API contract (bumps when set, seeds from frontFrame.cursor when null, alt-screen no-op, zero-delta no-op). All 751 ui-tui tests pass; tests/test_tui_gateway_server.py (177) pass. * fix(tui): also advance cursorDeclaration so fast-echo survives deferred React state Copilot review on PR #26717 flagged a gap in the original fix: TextInput's fast-echo path defers the React cur state update by 16ms (perf optimization that batches re-renders during heavy typing). Inside that window, useDeclaredCursor still publishes a target computed from the PRE-keystroke cur — `cursorLayout(display, cur, columns). Advancing only displayCursor` would let any unrelated re-render in that 16ms window run onRender's cursor-park branch with the stale declaration and visually undo the fast-echo's advance. The fix is symmetric: noteExternalCursorAdvance now bumps BOTH displayCursor (the log-update relative-move basis) AND, if non-null, cursorDeclaration.relativeX/Y (the target the cursor parks at after every frame). When React finally flushes setCur, useDeclaredCursor publishes a fresh declaration that supersedes our bumped one — exactly what we want. Adds two new vitest cases covering both halves: - active declaration advances in lock-step with displayCursor - null declaration stays null (no spurious bump) All 753 ui-tui tests pass; tests/test_tui_gateway_server.py (177) pass. Closes review threads: PRRT_kwDOPRF1G86ChKtD (textInput.tsx:1016 fast-echo append) PRRT_kwDOPRF1G86ChKtF (textInput.tsx:924 fast-backspace) PRRT_kwDOPRF1G86ChKtG (ink-cursor-advance.test.ts:57 missing coverage) * fix(tui): make fast-echo survive TextInput rerenders + alt-screen (Copilot round 2) Round 2 of PR #26717 review. Three real holes Copilot flagged after the initial cursorDeclaration bump: 1. alt-screen early-return skipped BOTH halves of the notifier. But the default TUI wraps the composer in <AlternateScreen> — that IS the production path. CSI H resets log-update's relative-move basis, but the alt-screen park branch uses absolute CUP = rect.x + decl.relativeX, so a stale declaration there still parks the cursor at the pre-keystroke caret. Fix: skip ONLY the displayCursor half on alt-screen; still bump cursorDeclaration. 2. TextInput's own rerender could clobber the Ink-level bump. The fast- echo path defers setCur by 16ms; if a parent state change rerenders TextInput in that window, the layout effect inside useDeclaredCursor reads the stale React cur state and re-publishes a declaration at the OLD column. Fix: cursorLayout(display, curRef.current, columns) — read the always- up-to-date ref, not the deferred state. useMemo dropped (compute is cheap, single-line wrap-text in the common case). 3. Tests bypassed the production wiring. Added two structural tests: - still advances cursorDeclaration on alt-screen in the Ink-level suite, asserting displayCursor stays put but the declaration advances by the delta. - textInputCursorSourceOfTruth.test.ts pins three structural invariants: layout reads curRef.current, never the bare cur state, and the fast-echo stdout.write calls remain paired with noteCursorAdvance(±N). Source-grep invariants > flaky Ink mount tests for this kind of regression. 757/757 ui-tui tests pass (+3 over round 1). type-check clean. lint introduces zero new errors on touched files. tests/test_tui_gateway_server.py (177) pass. Closes review threads: PRRT_kwDOPRF1G86ChOG2 (ink.tsx alt-screen guard) PRRT_kwDOPRF1G86ChOG9 (textInput.tsx fast-backspace rerender window) PRRT_kwDOPRF1G86ChOHC (textInput.tsx fast-append rerender window) PRRT_kwDOPRF1G86ChOHJ (alt-screen test asserts wrong invariant) PRRT_kwDOPRF1G86ChOHP (missing integration-style coverage) * fix(tui): reject fast-backspace at soft-wrap boundary (Copilot round 3) PR #26717 round 3. Copilot caught two real things: 1. \b \b cannot move the terminal cursor onto the previous visual row across a soft-wrap boundary. When the caret sits at visual column 0 of a wrapped row (e.g. value 'hello ' at width 6 → cursorLayout produces (line 1, col 0)), backspace would leave the physical cursor in place while the logical caret moves up to the end of the previous visual line. noteCursorAdvance(-1) would then feed Ink a wrong delta. Fix: canFastBackspaceShape now takes the composer width and rejects when cursorLayout(value, cursor, columns).column === 0. The fast path falls through to the normal Ink render, which correctly lays out the new caret position. The PR-description inconsistency about alt-screen is fixed in a separate gh pr edit. Adds 4 new tests in textInputFastEcho.test.ts pinning the rejection at exact-multiple wrap boundaries plus a positive control inside a wrapped line and a back-compat case where columns is omitted. 761/761 ui-tui tests pass. type-check / lint clean. 177/177 Python tests/test_tui_gateway_server.py pass. Closes review threads: PRRT_kwDOPRF1G86ChxE5 (textInput.tsx:933 wrap-boundary regression) * fix(tui): polish doc + tests after Copilot round 4 Three polish points Copilot raised: 1. canFastBackspaceShape doc comment overstated the legacy contract — said it conservatively rejects potential wrap boundaries when columns is omitted, but the implementation actually skips the wrap-boundary check entirely. Reworded to make the legacy behavior explicit and warn callers not to rely on protection they don't get. 2. ink-cursor-advance.test.ts rationale comment for the 'advances cursorDeclaration in lock-step' case still referenced the pre-fix cursorLayout(display, cur, columns) expression. Now accurately describes the current source of truth — curRef.current in textInput.tsx — and explains the window the bump is bridging. 3. Removed the three __get*ForTest accessors from Ink. The test file already cast the instance to inspect private state in the couple of tests that needed declaration mutation; the rest now use a small peek(ink) helper that does the same cast for reads. No test-only API surface ships in production. 761/761 ui-tui tests pass. type-check clean. lint introduces zero new errors on touched files. 177/177 tests/test_tui_gateway_server.py pass. Closes review threads: PRRT_kwDOPRF1G86Ch23W (canFastBackspaceShape doc accuracy) PRRT_kwDOPRF1G86Ch23f (stale test rationale) PRRT_kwDOPRF1G86Ch23p (test-only API surface in production) * fix(tui): tighten doc + add dy test coverage (Copilot round 5) Two polish points from round 5: 1. canFastBackspaceShape doc had two paragraphs that conflicted — the main 'Additionally rejects when the physical cursor sits at visual column 0' was stated unconditionally, then the columns-param paragraph qualified that it only happens when columns is passed. Reworked into clear 'When supplied / When omitted' branches with a concrete example value ('hello ' returns true without columns even though it would be unsafe at width 6). No more inconsistency. 2. Added a test asserting cursorDeclaration.relativeY advances when dy is non-zero. Existing tests exercised dy on displayCursor only. Newlines in fast-echoed text don't currently hit the bypass (canFastAppendShape rejects '\n'), but dy is part of the public notifier contract and must propagate symmetrically with dx so future callers get a fully-implemented contract. 762/762 ui-tui tests pass (+1). type-check / lint / build clean. Closes review threads: PRRT_kwDOPRF1G86Ch6Sz (doc inconsistency) PRRT_kwDOPRF1G86Ch6TE (missing dy coverage on declaration) * fix(tui): doc polish (Copilot round 6) Four small but valid points: 1. textInputCursorSourceOfTruth.test.ts used bare 'fs'/'path'/'url' imports; the rest of ui-tui consistently uses the 'node:' prefix (see src/__tests__/useSessionLifecycle.test.ts, src/lib/editor.test.ts). Switched to node:fs / node:path / node:url to match convention. 2. CursorAdvanceContext.ts type-level doc described only displayCursor. The notifier intentionally also mutates the active cursorDeclaration and that's the only part that matters on alt-screen. Reworked the doc into a two-part 'updates both' summary with the alt-screen asymmetry called out explicitly. 3. use-cursor-advance.ts hook doc had the same problem. Same fix — document both pieces of state, both screen modes. 4. App.tsx onCursorAdvance prop comment was incomplete. Same fix — describe both state updates and the screen-mode asymmetry. No behavior change. 762/762 ui-tui tests pass. type-check / lint / build clean. Closes review threads (auto-resolved on PR but valid critiques): PRRT_kwDOPRF1G86Ch926 (node: prefix on built-in imports) PRRT_kwDOPRF1G86Ch92_ (use-cursor-advance.ts doc) PRRT_kwDOPRF1G86Ch93H (CursorAdvanceContext.ts type doc) PRRT_kwDOPRF1G86Ch93J (App.tsx prop comment)19 天前
fix(tui): apply ui-tui fix pass and restore type-check - run the requested ui-tui lint+format pass and include resulting formatting updates - guard text-measure cache eviction key in hermes-ink so ui-tui type-check stays green 1 个月前
fix(tui): restore macOS copy behavior and theme polish (#17131) This PR groups the TUI fixes that restore macOS Terminal usability and clean up the theme/composer regressions: - copy transcript selections on macOS drag-release so Terminal.app users can copy while mouse tracking is enabled - copy composer selections on macOS drag-release; composer selection is internal to TextInput and does not use the global Ink selection bus - keep IDE Cmd+C forwarding setup macOS-only, and make keybinding conflict checks respect simple when-clause overlap/negation - force truecolor before chalk initializes (unless NO_COLOR / FORCE_COLOR / HERMES_TUI_TRUECOLOR opt-outs apply) so the default banner keeps its gold/amber/bronze gradient in Terminal.app - move TUI surfaces onto semantic theme tokens and preserve skin prompt symbols as bare tokens with renderer-owned spacing - render focused placeholders as dim hint text in TTY mode instead of inverse/selected-looking synthetic cursor text1 个月前
fix(tui): termux-gate scrollback preservation, touch-friendly defaults Adds a Termux runtime detection helper and gates three TUI defaults on it: - Skip the startup scrollback clear on Termux so users can review/copy earlier output after reopening the app. Desktop keeps the existing \x1b[2J\x1b[H\x1b[3J slate (AlternateScreen takes over there anyway). - Default INLINE_MODE on under Termux: primary-buffer rendering makes long-thread review and copy/paste much less fragile when users background/foreground the app. Override with HERMES_TUI_INLINE=0/1. - Default mouse tracking off under Termux so touch selection isn't intercepted by terminal mouse protocols. Explicit override via HERMES_TUI_MOUSE_TRACKING=0/1; legacy HERMES_TUI_DISABLE_MOUSE still works on desktop. Detection is purely env-based (TERMUX_VERSION or PREFIX path) with an explicit opt-out HERMES_TUI_TERMUX_MODE=0 for debugging. Non-Termux platforms keep every existing default. Co-authored-by: adybag14-cyber <252811164+adybag14-cyber@users.noreply.github.com> 16 天前
feat(tui): support attaching to an existing gateway (#21978) * feat(tui): support attaching to an existing gateway Allow the TUI gateway client to connect via HERMES_TUI_GATEWAY_URL while preserving spawned gateway fallback, and mirror event frames to sidecar feeds so dashboard tool activity remains visible. * review(copilot): redact attach URLs and gate stale transport exits Strip query strings (and any user info) from gateway / sidecar URLs before logging or surfacing them in gateway.start_timeout, so attach tokens never leak into the TUI log tail or activity feed. Also gate the spawned-proc and websocket close handlers on transport identity so a stale child or socket cannot clear a freshly-started ready timer or reject newly-issued pending requests during reconnect. * review(copilot): tighten transport restart and shutdown lifecycle Reject any in-flight RPCs in resetStartupState so callers do not hang on promises issued to the previous transport when start() swaps a child or socket. Have kill() explicitly reject pending so attach-mode promises drain after an intentional shutdown, and reattach when HERMES_TUI_GATEWAY_URL rotates between requests instead of silently keeping the old session. Fold the spawned child error path through handleTransportExit so a failed spawn clears the startup timer and emits a single exit event. Also null the websocket reference before calling close so the identity guard correctly tags stale close events on real WebSocket timing. Locks the new behaviors in with regression tests for kill, URL rotation, and stale-pending cleanup. * review(copilot): swallow stray ws connect rejection and isolate test env Attach a no-op catch handler on the websocket connect promise so an unobserved connect-error / early-close rejection cannot surface as an unhandled promise rejection in Node when no request is currently racing the open. Snapshot HERMES_TUI_GATEWAY_URL / HERMES_TUI_SIDECAR_URL in beforeEach and restore them in afterEach so vitest runs that set those env vars beforehand do not get permanently cleared. * Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> * review(copilot): hoist wire decoder and harden redact fallback Reuse a single module-level TextDecoder for binary websocket frames so high-frequency attach-mode traffic does not allocate one per message. Strengthen the redactUrl fallback so embedded user:pass@ credentials are also masked when the WHATWG URL parser rejects the input, and pin the new behavior with a regression test that drives a malformed bearer URL through the gateway-stderr publish path. * Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> * review(copilot): force redact fallback path with deterministic fixture Replace the "%zz" user-info fixture, which WHATWG URL actually accepts in recent Node and silently routed the test back through the structured-URL branch, with a port-99999 fixture that the parser rejects across Node versions. Add a pre-flight expect(() => new URL(fixture)).toThrow() assertion so a future URL-parser change can never silently bypass redactUrl()'s fallback again. * review(copilot): sanitize websocket constructor failures Avoid logging raw WebSocket constructor error messages because some implementations include the full input URL, including token-bearing query strings. Log the redacted gateway or sidecar URL with the error class instead, and add regression coverage for constructor-throw paths on both attach and sidecar sockets. * review(self): restart transport on attach-mode transition Route runtime HERMES_TUI_GATEWAY_URL changes through start() so switching from spawned-gateway mode to attach mode also tears down the previously spawned Python child instead of leaving it alive. Keep the existing fast-fail behavior for pending RPCs. Also make constructor-failure logging fully generic after the redacted URL, avoiding even implementation-specific error class text in the log tail. * review(copilot): use websocket wording for attach close errors When the attached websocket closes, reject pending RPCs with an explicit websocket-closed reason instead of the spawned-process oriented gateway exited wording. Add coverage to ensure close code 1011 surfaces as gateway websocket closed (1011). --------- Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>27 天前
fix(tui): handle timeout/error subagent statuses in /agents (#26687) Accept delegation timeout/error statuses in the TUI subagent model, normalize unknown status strings defensively, and harden /agents overlay rendering/sorting so unknown statuses cannot crash glyph/color lookup. Add regression tests for live event normalization and disk snapshot replay.19 天前
fix(tui): honor skin highlight colors (#20895)29 天前
fix(cli): show active profile in TUI prompt 16 天前