fix(tui-v2): skip ChoiceList when ask_user has no candidates#616
Open
ChaiRZy wants to merge 740 commits into
Open
fix(tui-v2): skip ChoiceList when ask_user has no candidates#616ChaiRZy wants to merge 740 commits into
ChaiRZy wants to merge 740 commits into
Conversation
refactor(langfuse/chatapp):
…t actions, remove web/pet CLI commands
…sdefine#389) - Pasting non-image files (.docx/.pdf/.py …) was folded as `[Image #N]` because clipboard helper returned any file path. Split into image vs file by extension; non-images now fold as `[File #N]` and expand to the path on submit, same flow as images. - Backspace/Delete used to nibble placeholders one char at a time. Override `action_delete_left/right`: when the caret sits flush against a `[Pasted text #N +X lines]` / `[Image #N]` / `[File #N]` placeholder on the matching side, remove the whole span and drop its dict entry. - Ctrl+C inside the input was clearing the whole field. Root cause: `screen.get_selected_text()` doesn't see TextArea's internal selection, so the handler fell through to the input-reset branch. Fixed by preferring the focused InputArea's `selected_text` before screen-level drag selection. - Input text/history/pastes were globally shared across agent sessions (single InputArea widget). Moved the four state slots (text/history/pastes/paste_counter) onto `AgentSession`, with a `_swap_input_for_session` snapshot/restore on session switch.
…sdefine#390) - Per-fold click-to-expand: assistant messages render as a sequence of text/fold-header/fold-body widgets; FoldHeader is a clickable Static that toggles m._toggled_folds and remounts just that message - Ctrl+O is authoritative: clears all per-fold overrides so the state is uniformly all-collapsed or all-expanded - Streaming spinner: dedicated tiny Static (`m._spinner_widget`) after segment widgets, driven by an independent 100ms timer. Bypasses Markdown completely so unclosed code fences / paragraph whitespace trimming can't eat it - Hide `**LLM Running (Turn N) ...**` markers at render time only; fold_turns still uses them for splitting - Ctrl+B sidebar toggle: invalidate width-keyed cache and reuse the resize debounce path so the chat reflows reliably after toggling - #input min-width: 10 — guards Textual TextArea.render_lines against `range() arg 3 must not be zero` when sidebar is shown on a very narrow window and #main collapses to ≤ 0 cols
…one prefix from fold headers
* feat(tui): improve message scrolling and rewind picker * feat(tui): add double-escape rewind shortcut
Squashed PR lsdefine#393: update README.md and add installation docs.
…e support - app.js: triple-guard for CJK IME composition (compositionstart/end + keyCode 229) - lib.rs + Cargo.toml: add tauri-plugin-single-instance to prevent zombie processes - tauri.conf.json: add "dmg" target and png icons for macOS bundling
# Conflicts: # frontends/desktop/src-tauri/Cargo.lock # frontends/desktop/src-tauri/src/lib.rs
…evtools by default
… instead of _turn_parts
… tips Adds to frontends/tuiapp_v2.py: - /rename <name>: rename current session; collision-checked across in-memory sessions and the persistent registry. Topbar and sidebar refresh in place. - /continue <name>: resume by stored name in addition to the existing index form (/continue 1). The picker (/continue with no arg) labels each entry with its stored name when available. - /cost [all]: per-session token usage (input / output / cache_create / cache_read), elapsed time, request count, and context-window %-left. The "all" form lists every tracked thread, including ended sessions. - ask_user picker upgrades: free-text escape hatch on every list, Esc-back navigation, single/multi-select switch from a [多选]/[multi] question hint, candidate sanitizer that handles JSON-envelope debris. - Runtime tip footer (`└ Tip: ...`) that rotates short usage hints. - Topbar status chip showing the active model + idle/busy state. - Cross-platform launcher hardening: auto-install of rich/textual on first run, and a hint when running under git-bash/mintty. New modules: - frontends/session_names.py: JSON sidecar at temp/model_responses/ session_names.json keyed by log-file basename; touched only by /rename and /continue <name>. continue_cmd is not modified. - frontends/cost_tracker.py: per-thread TokenStats, captured via monkey-patches on llmcore._record_usage and llmcore.print (Anthropic SSE's final output count only arrives via the latter). Idempotent install(). No core files touched.
…ine#397) feat(tui): /rename, /cost, /continue <name>, ask_user picker, runtime…
Adds Ctrl+T theme cycling across six palettes: a custom ga-default matching the existing github-dark scheme plus Textual built-ins (nord, gruvbox, dracula, tokyo-night, textual-light). Themes flow through Textual's native register_theme + get_css_variables, so CSS references $ga-bg / $ga-fg / etc. that resolve from the active theme; ga-default uses our exact palette while built-ins derive the missing slots (border/dim/muted/border_hi) from resolved bg/fg via a blend. watch_theme keeps the C_* module globals (Rich Text colors) and the role-color dict in sync with the active theme, invalidates message caches, and remounts the current session. Rich Markdown rendering also passes a per-theme RichTheme so code blocks, links, headings, and quotes follow the active palette instead of Rich's frozen defaults. text-muted / text-disabled are deliberately not inherited from Textual: those resolve to 'auto NN%', a Textual-only syntax Rich Text rejects.
…sdefine#399) Rephrased docstring in cost_tracker.py to clarify API-mode tracking semantics.
Display a concise startup banner with model name when running in interactive TTY mode.
The five topbar chips (session name / model / effort / tasks / clock)
were hardcoded github-dark hex values introduced in the upstream
topbar redesign, so they stayed cyan/amber/lavender on every theme.
Add five chip_* slots to _DEFAULT_PALETTE and derive them for
built-in themes from primary / secondary / warning / accent / success,
keeping five distinguishable hues across themes. watch_theme syncs
the C_CHIP_* globals on switch.
Also move the '▾ fold' indicator from the LEFT column to the RIGHT,
just before the clock. The left column packs identity + session +
running status pill into a ratio=1 ellipsis column, so a long status
('running 2m 15s') was eating the trailing fold glyph as '...'.
The clock chip was github-dark's brighter success green (#56d364), which read as more vibrant than the rest of the muted palette and clashed with C_GREEN. Use the same #7ec27e the sidebar uses for the active session marker, so all 'positive accent' greens in the chrome sit at one saturation level.
These are agent-internal metadata: <summary> already feeds the sidebar preview and the fold title, <thinking> is meant to be hidden. They were leaking into the chat body, and worse, CommonMark parses '<summary>X</summary>\n<body>' (no blank line between) as a single HTML block that swallows the following body line — so the model's actual reply disappeared from view whenever it wrote the summary tag immediately before its answer with only a single newline. Strip both tags in _render_md before passing to the Markdown parser.
…ovement, standard-upgrade report)
…Driver logging fix - README (EN): clone+uv as recommended install; one-line installer demoted to convenience with script source link; drop unpolished exe section; recommend frontends/tui_v3.py (prompt_toolkit+rich, not Textual); note agent core's single dependency (requests); TMWebdriver naming - assets: add ga_install.ps1/.sh (GLOBAL mode uses GitHub/astral-sh sources) - TMWebDriver.py: don't let logging failure mask successful remote JS result
容器化部署时个人微信前端无法首次扫码登录,定位并修复 4 处问题: 1. isatty 闸门顺序错误:__main__ 先把 stdout 重定向到日志文件,再判 sys.stdout.isatty()——文件句柄恒为 false,导致无 token 时必然退出。 改为始终走 QR 登录、二维码打到真实 stdout(docker logs 可见), 无需 TTY 即可扫码。 2. get_bot_qrcode 无容错:被限流时响应缺 'qrcode' 字段直接 KeyError 崩溃, 配合 restart 策略形成死亡螺旋。改为退避重试,等 qrcode + img_content 都就绪。 3. get_qrcode_status 轮询只 catch ReadTimeout:限流返回非 JSON 时 .json() 抛错崩溃。改为捕获所有 RequestException + ValueError 容错重试。 4. PNG 兜底依赖 PIL,缺失时 qrcode.make().save() 抛 ModuleNotFoundError, 且 traceback 被 finally 重定向到日志文件而难以发现。改为先打 ASCII 二维码(扫码真正所需,无依赖),PNG 保存包 try 失败不崩。 均为通用的容器化健壮性改进。 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
tuiapp_v2 的两项增强,拆成两个 commit。 ## 1. 工具显示卡片(file_write / file_patch / file_read / code_run) 把 agent_loop verbose 的 `🛠️ Tool` 块替换为紧凑卡片: - **file_write / file_patch**:行号 + 上下文的主题化 diff;append/prepend 走整文件 diff 拿真实上下文。 - **file_read**:读到的内容带行号 gutter,剥离 LLM-facing 噪音(`[FILE]`/show_linenos 前言/截断提示),超长折叠;行号在不可知处(keyword 窗口 / 挖洞后)留空。 - **code_run**:参考 Claude Code / Codex 的 exec 渲染,无边框、`│` 命令 gutter + `└` 输出 gutter,输出 dim、**保尾截断**(traceback 关键在末尾);代码来自正文 ```块``` 时不重复展示。 - 三卡共用 `_CardWriter` / `_card_status_row` / `_emit_gutter`;2 格左 margin 只进显示流、不进复制源。 - capture 机制:live 走 `tool_before/after` 钩子,`/continue` 走 `iter_write_captures` 从历史 tool_result 重建,按 `hash(get_pretty_json(args))` 内容寻址。 - 双流渲染对齐(`_align_md_renders`)支持 margin 映射,鼠标复制不含视觉缩进。 ## 2. /model 与 /effort 渠道内运行时切换 - **/model**:在线拉模型列表的 searchable picker;搜索框兼作自定义模型名输入(无匹配时 Enter 直设)。 - **/effort**:reasoning effort 档位切换,协议差异在 `effort_note` 单点编码——Claude(`output_config.effort`) low/med/high 原样、xhigh→max、none/minimal 忽略;OpenAI(`reasoning_effort` / `reasoning.effort`) 透传;mixin 经 `_BROADCAST_ATTRS` 广播到全部子渠道。 - 逻辑收在 `frontends/model_cmd.py`(agent 无关、可单测)。 ## 测试 - 卡片:两份真实会话日志全链路重放,51 个 code_run + 23 read + 20 write 全部渲染、双流 parity、复制不含 margin。 - /model /effort:拦截真实 llmcore 请求构造验证 effort 进 payload(含 mixin 广播、各协议字段);picker 自定义输入走 Textual pilot 端到端。
…ine#599) * feat(tui): /continue 恢复 plan 卡片 + Ctrl+S/Ctrl+C 路由修复 plan-card 恢复:plan 模式判定改用日志中结构化的 enter_plan_mode tool_use(continue_cmd.find_plan_entry),不再扫聊天文本——用户打字 伪造不出。is_active/resolve_path 新增 restored_path 信号,AgentSession 新增 restored_plan_path 字段,plan_scan_baseline 退化为仅供 current_step 的 📌 扫描用。 Ctrl+S restore 卡死:_stash_cleanup_restore 由 self.text= 改为 clear()+_insert_via_keyboard,走编辑管线避免长会话全屏 relayout,与 stash 清空路径对称。 Ctrl+C 弹回收紧:_cmd_stop 仅在该轮未被消费(agent 尚未产出回复)时 才把 user 文本弹回输入框,避免已答轮次污染输入框 / 重发导致历史重复。 * feat(tui): plan 卡片可滚动(4 行任务窗口)+ TODO 渲染为 [ ]/[x] planbar 拆为固定表头/步骤行(#planbar-head)+ 任务滚动区 (#planbar-tasks,max-height:4)。最多露 4 个 TODO,其余滚轮/PageUp 翻看,去掉旧的 5 行预算与 ⋮ +N more 截断;表头与当前步骤行固定不滚。 任务标记 ☐/✔ → [ ]/[x],与 plan.md 源文件写法对齐。
The tool-cards change (lsdefine#593) tightened _md_line_has_box_drawing to exempt pure-horizontal '─' runs as markdown hr. But a SIMPLE-box Markdown table's only box glyph is its header rule, so that exemption dropped whole tables out of the passthrough copy path and into the wide-render aligner, which maps selection cell-columns to character indices — misaligning copy of any table with CJK (wide) cells. Remove the pure-horizontal exemption so a table's header rule is detected again and the table run stays passthrough. Card gutters (└─ / │ / └) keep their own prefix-based exemptions, so tool cards are unaffected. The only cost is a real markdown hr copying as a dash run — the pre-lsdefine#593 behavior.
…owser (lsdefine#598) web driver init nudges the browser by opening a page when no session appears; os.startfile raises AttributeError on Linux/macOS, killing the retry loop on headless servers. webbrowser.open is stdlib and cross-platform with the same effect.
修复个人微信前端在 headless 容器内无法登录
* feat(tui): workspace 项目模式 + @ 文件引用补全(v2/v3) - workspace: /workspace 设/off/picker、junction 复用 project_mode 记忆、 per-session(v2)/进程级(v3)、/continue 恢复、session map 持久化绑定状态 - @ 补全(completion-only): 子序列模糊 + path-like 目录补全(~/ / ./ C:\)、 提交期 @路径作为普通文本交 agent - v3 filterable 焦点链 picker(输入框作焦点环对象、free_input) - 新增共享模块 at_complete.py / workspace_cmd.py;核心零改动 * feat(tui): @ 路径绝对化 + 默认根=temp + 未绑显示完整路径 - 默认根 os.getcwd() → agent 工作目录 <GA根>/temp(与 file_read/code_run 一致、不随启动 cwd 飘) - 提交期 @相对 → @绝对(display 保留相对短路径),让 agent file_read(相对自身 cwd)找得到 - 未绑 workspace 时候选显示完整路径(根不直观);索引忽略 model_responses 会话日志噪音 - 启动预热 temp 索引 + candidates_for 惰性兜底(任何根首次访问自动建)
…lsdefine#606) - macljqCtrl.py: macOS implementation of mouse/keyboard/screenshot/window enumeration mirroring ljqCtrl API, plus AX accessibility control tree (AXElements/AXFind/AXPress) as the macOS equivalent of UIA - computer_use.md: add section 3 documenting the macOS platform branch - .gitignore: whitelist memory/macljqCtrl.py
… prevent UI freeze ESC cancel sent session/cancel RPC but never cleared the busy flag, leaving the UI stuck if the server-side cancelled event never arrived. Also clear busy on all sessions when the bridge closes so pending poll loops exit cleanly. Co-authored-by: dilong888 <dilong888@users.noreply.github.com>
v3 的 _do_restore 此前只把历史载入内存 backend.history,不碰当前进程日志。native 后端逐轮只把新增消息 append 到 model_responses_{pid}.txt,恢复出的旧历史从不写入当前日志——续聊只追加增量,下次 /continue 扫到的本进程文件就只剩续聊部分,旧历史丢失。
对齐 v2 _do_continue_restore:恢复前 reset_conversation(快照+清空当前日志),恢复后 copyfile(源日志 → 当前日志),续聊便接在完整历史之后。
…d, AXClick)Feat/macos ax enhancements (lsdefine#611) * feat(memory): enhance macOS AX control (resolve_pid fix, enabled field, AXClick) - _resolve_pid: fix bundle_id branch (used un-imported AppKit), 3-tier match (bundle id > app name exact > substring) - AXElements: collect 'enabled' field (SOP: check disabled before click) - AXClick: AXPress-first then fallback to physical-coord Click, honest success判定 via pixel diff - AXFind: add enabled_only filter, refactor if-forest into _hit helper * docs(ljqCtrl): tighten macOS AX guidance, drop boilerplate & app-specific detail - collapse 5-line cross-platform import boilerplate to one line - generalize control-identifier tip (remove app-specific identifiers)
Annotate config block to guide new GA instances: enumerate candidate mykey.py variable names and experimentally probe which config works; only print var/field names, model, apibase host/path, status codes and error types (never full dict or apikey/token); include a config format example. Also flag that apibase/endpoint may differ per provider/proxy at the two request-building sites.
When candidates is empty/absent, the picker would still create a ChoiceList with only a free-text option, stealing focus from the input. Check candidates early and return if there are none. - 0 net line change, no comment needed - v3 (tui_v3.py) already has the same guard (dfab299) - v2 parity with v3
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.
When candidates is empty/absent, the picker would still create a ChoiceList with only a free-text option, stealing focus from the input. Check candidates early and return if there are none.