fix: persist TUI branch sessions#590
Open
HamsteRider-m wants to merge 705 commits into
Open
Conversation
Co-authored-by: Kailigithub <Kailigithub@users.noreply.github.com>
Mirrors Claude Code's /btw: ask the agent a quick question without interrupting the in-flight main task. - frontends/btw_cmd.py: snapshot backend.history under lock + deepcopy → background-thread backend.raw_ask single-turn answer; never writes backend.history; never touches task_queue. Reuses backend.raw_ask + make_messages, no new LLM instance. - frontends/chatapp_common.py: register /btw in HELP_COMMANDS; handle /btw in AgentChatMixin (sync via asyncio.to_thread); install() at module bottom so the slash hook is wired for any frontend that imports chatapp_common. - frontends/stapp.py: persist display_queue + partial_response in st.session_state so the streaming bubble survives Streamlit reruns; new render_main_stream() helper covers both new tasks and resume; /btw branch uses plain st.rerun() (preserves dq) so the main task's remaining output renders in a follow-up bubble; non-/btw prompts explicitly call agent.abort() to keep the original "submit cancels" UX. Strip <summary> meta tags from text segments and drop dangling "LLM Running (Turn N) ..." markers in resumed partials to clean up the rendered chat. Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…es to non-native paths
Keep Telegram streaming overflow messages tied to the primary live message, reuse them across edits, and delete stale overflow replies when streamed content shrinks.
新增 command/ 包和 ga.cmd/ga 入口脚本,用户可通过 `ga <command>` 启动各前端。
PPT/Word copies include both image/png and text/plain items. Only intercept paste when no text item is present (pure screenshot/image).
新版 TUI 与 tuiapp.py 并存,共享 GenericAgent 调度/队列/线程协议, 重做视觉与交互: - 多行 InputArea:≤3 行自适应、>3 行三行滑动窗口、粘贴占位符 - 命令面板(OptionList + 模糊匹配 + Tab 补全) - /llm、command 选择走 ChoiceList,选完区域收缩 - Markdown HardBreak 渲染 + ANSI 缓存 - 响应式布局(窄屏隐藏侧栏 / 宽屏按比例放大) - selection-aware 自动滚动(选文本时不抢滚动) - 新增 /btw、/continue、/export、/restore 不改 cli / hub / README / tuiapp.py 入口。
Handle paste events directly for both short and multi-line input instead of mixing TextArea's default paste path with the custom placeholder path. This keeps single-line paste from being inserted twice while preserving the existing multi-line placeholder behavior.
When an assistant message already has a mounted body widget, streaming updates only refreshed the widget content. Scroll the messages container after those in-place updates so both streaming chunks and final responses remain visible at the bottom.
…lsdefine#360) Co-authored-by: AI Agent Review
PR lsdefine#360 disabled Textual mouse support on darwin (run(mouse=False)) to keep terminal-native selection working. Side effect: ?1006 SGR mouse was never enabled, while macOS Terminal.app / iTerm2 default ?1007 (alternate scroll mode) translated wheel events into Up/Down keys — which then triggered the newly added input-history navigation. Re-enable mouse globally and explicitly disable ?1007 on mount so wheel events flow through the SGR channel without spawning duplicate arrow keys. Keyboard Up/Down history navigation is unaffected. The ?1007 disable is a no-op on Linux / Windows Terminal where it defaults off.
* fix: tighten tui input history edge navigation * 回退一些注释的修改
…upervisor_sop, goal_mode constraints, gitignore whitelist
* fix(tui): debounce v2 layout reflow on resize Same-size resize events are deduped, layout reflow is debounced to 80ms, and message remount is deferred to the next frame so #messages.content_region reports the post-resize width instead of the pre-resize one. * feat(tui): rebind v2 keys (Ctrl+C dual, Ctrl+↑↓ sessions, Ctrl+O fold) + help overlay - Ctrl+C is the single stop/quit key: stops a running task; otherwise clears non-empty input; otherwise arms quit (press again within 2s to exit). Removes Ctrl+S, Ctrl+Q, and InputArea's prior Ctrl+C copy-or-clear binding. Any non-Ctrl+C key disarms the pending quit. - Ctrl+O toggles turn fold (was Ctrl+F, which collides with terminal app search shortcuts on macOS). Topbar gains a small "▾ fold" indicator when fold mode is on. - Ctrl+Up / Ctrl+Down switches sessions (was Ctrl+Left / Ctrl+Right) to match the vertical session-list mental model. - Esc is the single one-press cancel for ChoiceList — drops the prior two-step "Left to leave, Left to cancel" flow. Esc also continues to close the command palette and now closes the help overlay. - Adds a Ctrl+? help overlay listing every keybinding. Bound on both ctrl+? and ctrl+question_mark to survive terminal reporting quirks. - Newline keeps Ctrl+J / Ctrl+Enter / Shift+Enter as cross-terminal fallbacks (Ctrl+J is the portable one for macOS Terminal.app); drops Alt+Enter. - Bottom bar simplifies to four essentials + Ctrl+? and / hints, and shows a transient "再按 Ctrl+C 退出" while quit is armed. * fix(tui): switch help shortcut from Ctrl+? to Ctrl+/ Ctrl+? (= Ctrl+Shift+/) is unreliable across terminals — many emulators either swallow it or report it as something Textual can't parse. Ctrl+/ is the conventional VS Code / command-line help shortcut and ships with the legacy Ctrl+_ fallback (ASCII 0x1F is what older terminals send for Ctrl+/). All three forms are bound (ctrl+slash, ctrl+/, ctrl+underscore) so the binding survives terminal-reporting differences. Bottom hint bar collapses to a single left-aligned line carrying the command-palette and help hints alongside the action keys, since the prior right column repeated information the left column already had room for. The help overlay header reads "快捷键帮助" instead of "快捷键". * fix(tui): ctrl+c dual-press with non-empty input, centered help modal - Ctrl+C with non-empty input now clears AND arms quit; a second Ctrl+C within 2s exits. Previously typing in the input let one Ctrl+C exit silently after the clear, which was easy to trigger by accident. - Help is now a ModalScreen pushed over the main screen so opening it no longer shifts the input box. Centered with a lighter background (#21262d on #30363d border) to read as a lifted panel. - Esc-cancelling a ChoiceList removes the prompt cleanly with no "已取消" output — the canceled prompt simply disappears from the chat, matching expectations for in-flight tool prompts. * fix(tui): keep Ctrl+C copy semantics and update choice hints When there is selected text, Ctrl+C now copies the selection and returns without triggering stop, input clear, or quit-arming. This preserves the TUI selection/copy path after Ctrl+C became the global stop/quit key. Choice prompts now advertise the current Esc-cancel behavior instead of the removed Left-arrow flow, and stale comments/docs mentioning the old left-arrow return path were cleaned up.
/continue list previews showed JSON debris (e.g. `{`, `" } ]} === Response ===`)
for some sessions. Two causes in _preview_from_file:
1. Models sometimes emit an unclosed <summary>, so the non-greedy DOTALL regex
pairs it with a far-away </summary> and captures === block headers / JSON
across rounds. Now only the latest <summary> is used, and it is rejected if
it looks like such a cross-segment capture (contains `=== `/`"role"` or
>200 chars) — falling through instead of digging for older summaries.
2. The fallback scanned for the first non-=== line, returning JSON structure
lines. It now uses the last real user prompt via a new _last_user(), which
scans Prompt blocks directly (so response-less/aborted sessions still
preview) and reuses _user_text() to skip tool_result continuations and all
_INJECT_MARKERS (WORKING MEMORY / SYSTEM TIPS / DANGER ...).
Scanned 261 local logs: garbage previews 26 -> 0.
…ew-garbage fix(continue): robust session preview for malformed logs
…loop, log name collision fix, checklist poll hard cap
- conductor_im_plugins: 新增 lark.py(飞书收消息插件,无状态轮询)、email.py、_TEMPLATE.py - wechat.py/config.local.json 经gitignore排除(含个人会话ID) - conductor.html/py: UI重构(grid/可折叠卡片/console/approvals)、action与pool化重构 - stapp.py: 移除_js_scroll_fix - ljqCtrl_sop: 补充Click点歪诊断/DPI坐标陷阱/文本输入经验
Rename tracked email.py/lark.py to _email_example.py/_lark_example.py so the poller (which skips _-prefixed files) no longer polls them on a fresh clone. Activate by copying to email.py/lark.py (now gitignored). Also nudge manager to reuse existing subagents on IM signals.
…ar (lsdefine#572) - _term_write(): route OSC/control sequences through Textual's driver writer queue instead of direct sys.__stdout__. A title/OSC write landing mid-frame during streaming used to split Textual's own escape sequences, rendering as flashing ANSI garbage; serializing on the same writer queue lands it cleanly. - sidebar: wrap the session list in a VerticalScroll (#sidebar-scroll) so it scrolls when overflowing (scrollbar-gutter reserved to avoid row reflow); inner #sidebar Static keeps padding so the click hit-test math is unchanged. Keyboard session-switch scrolls the active session into view.
…e#581) Ctrl+C / selection copy fell back to Textual's OSC 52 escape on Windows/Linux. Some terminals (notably VSCode's integrated terminal) decode the OSC 52 base64 payload as latin-1 instead of UTF-8, so non-ASCII copied text lands in the clipboard as mojibake (e.g. 需要 -> éè¦). Classic Windows conhost ignores OSC 52 clipboard writes entirely, so copy was a silent no-op there (incl. Windows Server). Prefer the OS-native clipboard (_copy_to_clipboard: win32 CF_UNICODETEXT / pbcopy / wl-copy / xclip), which writes real Unicode and bypasses the terminal. Keep OSC 52 only as a fallback, and force it under SSH (SSH_CONNECTION / SSH_TTY) where the native call would write the remote host's clipboard instead of the user's local one.
Slash commands returned from _on_enter before _expand ran, so a command argument carried the literal [Pasted text #N] / [File #N] / [Image #N] placeholder instead of the real content — e.g. `/morphling <pasted multi-line target>` reached the agent as the marker string, not the paste. Expand placeholders at the top of the command branch (mirroring the normal submit path) and clear the placeholder stores afterward. No-op for commands without placeholders.
fix(tui-v3): expand pasted placeholders before slash-command dispatch
…inking false timeout fix(frontend): force chat_input pinned to bottom (iframe wrapper fallback); drop details scroll-fix JS
… NativeClaudeSession
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
/branchsnapshots intotemp/model_responsesusing the existing/continueraw log format.session_namesso named branches are discoverable.Why
Before this change,
/branchonly copied in-memory UI/backend state. If the branched session was later searched via/continue, there was no standalone log for that branch, and branch names could resolve to the wrong/current session log.Testing
python -m py_compile frontends/tuiapp_v2.py_persist_branch_snapshot()output is parsed bycontinue_cmd._pairs()+_parse_native_history()and matches the expected restored history.git diff --check --cached -- frontends/tuiapp_v2.pyNote: local commit used
--no-verifybecause this checkout's pre-commit hook points to a missingscripts/plantree_pre_commit.py; the checks above were run manually before committing.