Skip to content

fix(tui_v3): deps, CPR warning, free-text ask_user, and NameError fixes#661

Open
dracpet wants to merge 789 commits into
lsdefine:mainfrom
dracpet:main
Open

fix(tui_v3): deps, CPR warning, free-text ask_user, and NameError fixes#661
dracpet wants to merge 789 commits into
lsdefine:mainfrom
dracpet:main

Conversation

@dracpet

@dracpet dracpet commented Jul 4, 2026

Copy link
Copy Markdown

Changes (stacked on upstream/main)

1. Deps & CPR warning (f656038, 807ab60, 6e06490)

  • Promote rich and prompt-toolkit to direct dependencies (fixes fresh uv sync)
  • Bump minimum versions for both
  • Silence CPR "not supported" warning via cpr_not_supported_callback instead of disabling CPR entirely

2. Evolved CPR approach (4e0f985)

  • Keep CPR probing enabled — needed for dynamic-height regions like the ask_user picker card
  • Suppress only the warning, not the probe

3. Free-text ask_user support (59370f5, 856226a)

  • Allow ask_user calls without candidates to render a free-text input card
  • Fix NameError crash: multi variable was defined inside if ae.candidates: block but referenced outside — causing crash on any free-text ask_user

4. Other fixes

  • /status context no longer shows "n/a" (7f50db5, 4de1941)

Closes #659. Co-Authored-By: GenericAgent bot@gaagent.ai

JinyiHan99 and others added 30 commits May 18, 2026 08:24
…sdefine#398)

When setup window starts the bridge, the main window was already created
with URL http://127.0.0.1:14168/ but failed to load (bridge not running yet).
After bridge becomes ready, we now navigate the main window to reload the
bridge URL before showing it, preventing the white screen issue.
…lsdefine#342) (lsdefine#414)

When a single LLM provider goes down mid-task (auth, network, quota,
etc.), the conductor falls completely silent: MixinSession yields
errors as text chunks (not Python exceptions), so the existing
try/except guard in conductor_loop never triggers, and the conductor's
only way to talk to the user (LLM-decided POST /chat tool call) is dead.

Add a tiny watchdog: monitor_conductor_queue now returns the conductor
agent's final done text, and conductor_loop scans the tail-1000-char
window for "!!!Error:" — the prefix llmcore uses for unrecoverable
errors. Tail window (not whole text) avoids false positives from
earlier turns; using a character window (not last-line) matches
ga.do_no_tool's content[-100:] style and tolerates the decoration
(fence + "[Info] Final response to user.") that do_no_tool appends
after the error chunk when its own short window check misses. When
detected, push the matched error line as a system chat message so the
user knows what happened. A dedup check prevents repeated warnings
if multiple events fail in succession.

+16 -4 lines. Only frontends/conductor.py touched. No agent_loop /
llmcore changes; other frontends unaffected.
Co-authored-by: GA Agent <ga-agent@users.noreply.github.com>
Eight independent fixes accumulated since the last batch, all landing in
tuiapp_v2.py:

1. Code-block selection offset: _align_md_renders's is_centered heuristic
   tripped on any code line indented >=4 spaces (Python's default), stripping
   the indent from the source string so the visible selection and the copied
   text disagreed. Add wide_lead >= 2*nt0_lead guard — only Rich's H1 centering
   (wide_lead ~ width/2) crosses it, code lines never do.

2. Blockquote-continuation selection offset: Rich re-emits `▌ ` on every
   wrapped visual line of a quote, but the wide-render carries the marker
   only once. Strip the marker as a decoration prefix (counts as indent, not
   content) and switch the wrap-accumulator to account for the per-break
   eaten space — both pre-existing code-block wraps and blockquotes now map
   correctly.

3. Streaming ANSI garbage: while a turn is streaming, the tail text segment
   was rendered with `Text(content, style=C_FG)` instead of `Text.from_ansi`.
   SGR codes from the model/tool output would render as literal `[31m`
   glyphs and only resolve after `m.done` flipped or a resize forced a
   remount via the Markdown path. Switch the two streaming fast paths to
   Text.from_ansi — O(n) scan, no Markdown parse, colors honored live.

4. continue picker LIMIT removed: tuiapp's own `LIMIT = 20` truncated the
   /continue picker even though list_sessions() always builds the full list.
   Show everything; header now reads "选择要恢复的会话 (N 条)".

5. Long-paste rescue + user echo elision: Windows Terminal yanks window
   focus when its >=5KiB paste-warning dialog pops, leaving focused=None
   after the user confirms; Textual then routes the Paste event to the App
   bubble instead of InputArea, and the paste appears to do nothing. Add a
   GenericAgentTUI._on_paste forwarder that refocuses #input and replays the
   event into InputArea._on_paste. Separately, on submit, elide user-side
   display of very long pastes (head 10 / tail 5 / `⋯ 省略 N 行 ⋯`); the
   agent still receives the full content via expand_placeholders.

6. mykey.py hot reload: load_llm_sessions() already had mtime-gated reload
   plumbing, but it only ran on Agent.__init__ / next_llm / list_llms — so
   editing mykey.py mid-session required restart. Two triggers added:
   on_input_area_submitted now calls load_llm_sessions() before sending
   (cheap mtime check; rebuilds on change), and a new /reload-keys command
   forces a sweep across every session (resetting llmcore._mykey_mtime
   per-iteration so the cache doesn't short-circuit later sessions).

7. Resize / sidebar toggle no longer jumps to bottom: _remount_current_session
   used to unconditionally scroll_end after rebuilding. Capture scroll_y +
   "was at bottom?" first; stick to bottom only if the user was already there,
   otherwise restore the prior scroll_y so resizes don't yank the user away
   from what they were reading.

8. Markdown table cells wrap instead of ellipsizing: Rich's Markdown
   TableElement passes no `overflow` to Table.add_column, so the default
   "ellipsis" hides long cell content with `…` on narrow widths. Patch
   TableElement.__rich_console__ on import to pass overflow="fold".
…auth-396-416

fix: 微信 /stop 中止检测 + AuthExpired 处理 + 配置向导改进
…nline-selection

[codex] feat(tg): add inline selection menus
Replace _history_lines (pre-rendered) with _blocks (source + kind) so
_repaint_screen can re-render every chip, banner, user panel at the
current width. \x1b[3J wipes scrollback on resize/Ctrl+L so the entire
conversation reflows, not just the visible viewport.

SIGWINCH delivery via self-pipe + select() — PEP 475 auto-retry on
os.read makes signal-driven InterruptedError unreliable under iTerm
split panes, so a dedicated pipe wakes the loop deterministically.

Streaming tracks a single _streaming_block whose source grows monotonically
through safe-position commits; mid-stream resize re-renders both the
committed source and the volatile live_tail.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
macOS Terminal.app quantises truecolor to its nearest 256 slot (so
#5e6ad2 lavender lands on slot 62 #5f5fd7, visibly more blue) and
renders \x1b[2m as a heavy multiply instead of a gentle blend, producing
the "blue border + heavy shadow" against iTerm's identical code.

Branch on TERM_PROGRAM: iTerm keeps its truecolor accent and \x1b[2m
dim; Apple_Terminal switches to pinned 256-slot accent (105) and an
explicit mid-gray (244) for dim. Box border (146) was already 256-slot
so it carries unchanged.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
feat(tui): scrollback-first TUI rewrite (tui_sb)
* 加入高危命令黑名单 Add  high-risk command blacklist

* GenericAgent Agent System Design Overview

* Subagent 集群监控,Subagents Monitor

* remove desing overview html

* skill_learn_from_cases — 案例驱动技能学习 CLI 工具

* skill_learn_from_cases — 案例驱动技能学习 CLI 工具 v001

* skill_learn_from_cases — 案例驱动技能学习 CLI 工具 v002

* skill_learn_from_cases — 案例驱动技能学习 CLI 工具 v003

* skill_learn_from_cases — 案例驱动技能学习 CLI 工具 v005

* skill_learn_from_cases 案例驱动技能学习 CLI 工具 v006

* skill_learn_from_cases 案例驱动技能学习 CLI 工具 v007

* skill\_learn\_from\_cases   案例驱动技能学习 CLI 工具 v008

* skill_learn_from_cases   案例驱动技能学习 CLI 工具 v009

* skill_learn_from_cases v010

* skill_learn_from_cases v 011

* earn_skill_from_cases v000

* fix:[开始空闲自主行动]按钮点击无反应

* 修复点击[开始空闲自主行动]按钮,无反应问题

* clean fork 0515

* clean051502

* 自主行动相关按钮修复

* learn_skill_from_cases v000

* cleaned again

---------

Co-authored-by: benechen <13817895035@126.com>
原链接 installation.zh.md 路径不正确,已更正为 docs/installation.md
docs: 修复安装文档中的链接路径
lsdefine and others added 23 commits June 26, 2026 12:03
…tree HTML monitor

- assets/ga_ultraplan.py: 3 primitives (phase/parallel/pipeline), file-first
  message passing, runtime f-string prompt fill, subagent via subprocess,
  nested phase tree + random-port auto-popup HTML panel (1h idle self-destruct)
- agentmain.py: --nolog flag
- llmcore.py: honor log suppression
…le ChunkedEncodingError

When the LLM stream encounters a transient network error (ConnectionError,
Timeout, SSLError, ProxyError), the retry path yielded the error string
back to the caller on every attempt. This caused the UI to display a wall
of '!!!Error: ProxyError!!!Error: ConnectionError!!!Error: SSLError'
strings during a network blip, even when the retry ultimately succeeded.

Match the existing HTTP retry path (which sleeps without yielding) by
removing the intermediate yield so the caller only sees the final error
if retries are exhausted. Retry attempts remain visible via the existing
'[LLM Retry] ...' stdout log.

Also extend the exception tuple to include ChunkedEncodingError. Without
it, a truncated chunked response falls through to the bare 'except
Exception' branch and aborts the stream with no retry, even though the
transport-level error is transient and recoverable.

Closes lsdefine#571
context_chars_used doesn't exist in cost_tracker → AttributeError
silently caught → always falls back to 'n/a'. Also cap*3 double-
multiplies since context_window_chars already returns ctx_win*3.

Co-Authored-By: GenericAgent <bot@gaagent.ai>
…play

context_window_chars() was returning context_win * 3 (the internal trim
trigger) instead of the actual model context window. This made /cost and
/status show 3M instead of 1M for deepseek/mimo.

Also:
- Update _cost_str docstring in tui_v3.py
- Set deepseek default_context_win to 1M in llmcore.py
- Add context_win/max_tokens for deepseek (384k) and mimo (128k)
  in configure_mykey.py template

The trim trigger *3 in llmcore.py:99 (trim_messages_history) remains
unchanged — it's the internal buffer before trimming kicks in.

Co-Authored-By: GenericAgent <bot@gaagent.ai>
These packages are required by tui_v3.py at startup but were listed
only under optional [ui] extras.  Users running  (the
recommended install path) would hit ImportError for rich, then for
prompt_toolkit, before the TUI could start.

Co-Authored-By: GenericAgent <bot@gaagent.ai>
tui_v3.py redirects sys.stdout to a log file before creating the
prompt_toolkit Application.  The stdout redirect disrupts prompt_toolkit's
CPR (cursor position report) probe — the DSR escape sequence is written
to the terminal fd, but the response either fails to arrive or is not
dispatched correctly, causing a 2-second timeout and a loud warning.

Disable CPR probing unconditionally.  GA's scrollback-first layout uses
dont_extend_height=True, no floats, and no mouse support — none of the
prompt_toolkit features that consume CPR data are active.

Co-Authored-By: GenericAgent <bot@gaagent.ai>
- Promote rich>=15.0.0 and prompt-toolkit>=3.0.52 as direct deps
- Expand CPR disable comment with terminal compatibility details
- Add uv.lock

Co-Authored-By: GenericAgent <bot@gaagent.ai>
The previous fix (807ab60) disabled CPR probing entirely to
silence a startup warning, but this also breaks
_min_available_height tracking in prompt_toolkit's
renderer — which is needed for dynamic-height regions
like the ask_user picker card.

Instead, keep CPR probing enabled (so height tracking
works) and just override cpr_not_supported_callback
to silence the warning on terminals that don't support
cursor position requests.

Co-Authored-By: GenericAgent <bot@gaagent.ai>
With CPR probing restored (4e0f985), the free-text ask_card
no longer freezes the terminal.  Remove the guard that
skipped the ask_card when no candidates were supplied,
so that free-text ask_user prompts now get a proper
interactive input card instead of falling through to
the normal input box.

Co-Authored-By: GenericAgent <bot@gaagent.ai>
Commit 59370f5 removed the no-candidates guard in _extract_ask_user,
allowing free-text ask_user events to reach _ask_card.  But _ask_card
defined 'multi' inside the 'if ae.candidates:' block while referencing
it outside — causing NameError when candidates were empty.

Move the multi assignment before the candidates block so it is always
in scope.  Free-text ask_user (no candidates) now correctly renders the
inline input box inside the ask card.

Co-Authored-By: GenericAgent <bot@gaagent.ai>
uv.lock was accidentally included; upstream does not track it.
Add to .gitignore to prevent future inclusion.

Co-Authored-By: GenericAgent <bot@gaagent.ai>
The Application's cpr_not_supported_callback override was not
enough — prompt-toolkit's Renderer captures its own copy during
__init__ and calls that one when CPR times out.  The warning wrote
directly to the real terminal fd (via self.output.write()), causing
a brief flash before the TUI overwrote it on the next frame.

Silence both callbacks (app + renderer) to fully suppress the flash.

Co-Authored-By: GenericAgent <bot@gaagent.ai>
…ersion

The rotating banner tip told the user to put [multi-select] in an
ask_user prompt — something only the agent writes.  Replaced both en/zh
with user-facing instructions on how to use the multi-choice picker
(Space to toggle, Enter to confirm).

Co-Authored-By: GenericAgent <bot@gaagent.ai>
nianyucatfish and others added 4 commits July 7, 2026 08:36
…d + /worldline) (lsdefine#667)

* feat(worldline): integrate checkpoint-tree rewind into tui_v2

- tuiapp_v2.py: /rewind durable inline picker + /worldline three-pane
  checkpoint tree (tree UI inlined; colors follow the v2 theme) with a
  per-node diff viewport; conv-only /rewind restores via the persistent store.
- worldline.py (new): UI-agnostic backend — RewindStore (persistent
  checkpoint tree + content-addressed blobs), reconcile, restore_plan,
  node_diff, native-log projection.
- continue_cmd.py: gated worldline support — list_sessions(rewind_root=),
  continue_inplace/copy(allow_empty=); default-off, other UIs byte-identical.
- tui_v3.py: status line shows concrete model id (unrelated minor tweak).

* feat(worldline): /rewind reuses the restore-mode picker (conv/code/both)

After picking a turn, /rewind now opens the same RestoreModeScreen as
/worldline to choose conversation / code / both, then restores via the
persistent store — making /rewind a true lite view of /worldline (linear
list, no tree/diff) over the identical restore backend.

* feat(worldline): sync working memory (history_info/key_info) on rewind

树节点除 conv 外,再存 history_info 增量(与 conv 同构)+ key_info 快照。
restore_plan 重建二者,_rw_sync_working_memory 在 rewind 时就地写回
handler.history_info / working['key_info'],使注入的 WORKING MEMORY 与
回退后的对话一致,消除旧 rewind「历史回退、纪要串味」缺陷。

向后兼容:老树无 WM 记录 → path_has_wm=False → 恢复时跳过 WM 同步,
不动现场纪要。单测 21/21 + 真机(363580)端到端验证(call_013→014
v2 痕迹 ×3→×0)通过。

* fix(worldline): reconcile against the log, only on /continue; rebuild working memory on absorb

- reconcile now takes the log-parsed full history, never the compressed
  backend.history. Compressed memory is a lossy derivative; reconciling the
  full tree against it made the tree adopt a front-trimmed suffix as a new
  trunk (false divergence). Contract pinned in the docstring.
- drop in-session reconcile from /rewind and /worldline: the tree is kept
  current every turn by _rw_commit, and live history there is compressed.
  reconcile now runs only on /continue, where backend.history is the freshly
  loaded full log.
- reconcile rebuilds history_info/key_info from the absorbed log so resumed
  and external-UI sessions also carry working memory on rewind.

* fix(worldline): feed commit the full log; derive deltas from the tree's real length

_rw_commit fed the compressed backend.history, so once a long session front-trims,
commit's absolute hist_len delta drops turns. Now:
- _rw_commit feeds the log-parsed full history (fallback to backend.history).
- commit derives the delta start from len(rebuild_history(parent)) /
  len(rebuild_hist_info(parent)) instead of the stored hist_len, so a poisoned
  count can't drop turns and a previously-lost turn self-heals on the next commit
  (the log is append-only, always a superset of the tree).

* chore(worldline): mark in-memory _do_rewind as the non-persistent degraded fallback

It chops the compressed live history (can't reach front-trimmed turns, keeps
truncated remnants, restores neither files nor working memory). Only used when
there is no checkpoint tree; the durable tree-rebuild path is always preferred.

* fix(worldline): derive tree working memory from the log, not live handler state

/continue resets agent.history (handler.history_info) to empty. _rw_commit
previously fed that live list as hist_info, so after a continue the per-turn WM
delta misaligned with the tree's log-derived WM and new turns' summaries were
dropped. commit now derives hist_info/key_info from the history it stores (the
full log) when not given, matching reconcile; _rw_commit stops passing the live
list. Tree WM is uniformly log-derived and survives the continue reset.

* feat(worldline): restore working memory on /continue (TUI-only, derived from the log)

continue_cmd clears handler.history_info on restore; the TUI's continue path now
derives history_info from the loaded log (same _derive_hist_info as the tree) and
writes it back to agent.history, so a continued session keeps its working memory,
consistent with the tree and live sessions. Scoped to tuiapp_v2 only — shared
continue_cmd is untouched, so telegram/discord/streamlit frontends are unaffected.

* refactor(continue): restore working memory on /continue via opt-in restore_wm

Move the WM restore from the TUI into continue_cmd as an opt-in. _load_history_into
derives history_info from the loaded log and writes it back to agent.history when
restore_wm=True; continue_inplace/continue_copy thread the flag and the worldline
TUI passes True. Default False keeps every other frontend (telegram/discord/
streamlit/shared chat) byte-for-byte unchanged. _derive_hist_info is self-contained
(no worldline dependency), same extraction口径 as the tree.

* perf(worldline): only re-parse the log in _rw_commit when front-trim is detected

_rw_commit re-parsed the whole log every segment to feed commit a full history;
for very long sessions (multi-MB logs) that adds a multi-second lag per turn. Now
it uses the fast in-memory backend.history by default and only falls back to
parsing the log when front-trim is detected — RewindStore.first_question() vs the
live history's first question (front-trim pops the first question, so they diverge).
Common case (no trim) pays nothing; correctness under trim is preserved.

* feat(worldline): conv/code bridge nodes + align _rw_commit to native log

- worldline: commit_bridge() + restore_plan() three-way (both/conv/code) with a new rw_tag field. conv bridges fork at parent(target); code bridges mount at parent(old_head) as a sibling of the current conversation node (same conversation, older code).

- tuiapp_v2: _rw_commit now reads the native log (parse_native_log) instead of the compressible live backend.history; stat-cached _rw_log_history; title from the real log delta; skip commit when there is no new complete pair. Cursor points at the bridge for conv/code; rw_tag rendered after the title.

- continue_cmd: add parse_native_log() public wrapper (complete Prompt->Response pairs only; [] for native-but-empty, None for non-native).

* fix(worldline): realpath 去重 key + 节点行数增删 + 剥离 project-mode

- key()/cwd 改用 realpath 解析 junction/symlink,同一物理文件只产生一个 tracked
  key(修 workspace 下 @提及绝对路径与相对写入产生双 key、回退误覆写当前内容)
- 新增 node_line_delta:按 node_diff 计每节点相对父节点的 +新增/-删除 行数,
  供 /rewind、worldline 显示代码变动量
- 重建历史时剥离 project-mode 注入块与自动注入标记(worldline._msg_user_text +
  continue_cmd._derive_hist_info),标题/提问只留用户原话

* feat(worldline/rewind): 面板与 rewind UI 精简重设计

worldline 面板:
- 右栏改用原生 OptionList(整项高亮/滑窗跟踪/点击映射,取代手写 ClickableList)
- 左右栏比例 3:2;详情面板精简(删掉与状态条/diff 重复的操作提示、时间、回退说明)
- 状态条重写为完整动宾短语,Enter 随聚焦切文案;右栏聚焦高亮改用更亮的 alt_bg
  (对齐左树,修此前聚焦态反而更淡的问题)

/rewind:
- 三列:用户 prompt · 上次时间 · 代码 +新增/-删除(绿/红,无改动显示「(无改动)」);
  倒序、聚焦底部、超长 prompt 省略号截断
- 延迟折叠(defer_collapse):选中先弹模式窗,确认才回退并清卡片;Esc 取消保留列表、
  焦点回到列表,不再留误导性「✓ 已选中」

其他:
- Ctrl+D 关会话时释放日志锁(abort + release_current),之后可原地 /continue

* fix(worldline): 回填输入框的 prefill 剥离 project-mode 注入

restore_plan 的 prefill/_prompt_of 走未清洗的 user_msg_text(树里的 user 消息取自
含注入的 native 日志),把 --- [PROJECT MODE:...] --- 一起塞进输入框。统一过
_strip_project_mode(幂等)。回归:temp/test_rewind_prefill_no_projectmode.py 9/9。

* refactor(tui): 删除被取代的旧 rewind 面板与死代码

- 删旧 /rewind 整簇:RewindScreen 类 + _on_rewind_pick + _rw_* 9 个 helper
  (已被内联 choice 卡 + RestoreModeScreen / RewindTreeScreen 取代,无 live 入口)
- 删未用 import next_same_depth/CheckpointTree、只写不读的 _diff_for/assistant_len
- 净 -276 行;import tuiapp_v2 通过、worldline 回归全绿

* fix(worldline): apply_code 回退失败不再静默,回传错误由 UI 提示

apply_code 直接写用户工作区文件,中途失败(权限/磁盘)会留下半回退状态。原先
restore_plan 吞成 changed=[],UI 显示「无变更」,用户毫不知情——全 PR 里失败半径
最大却唯一被静默的点。改为 _apply_code_safe 回传 (changed, code_error),
_rw_restore_node 据此追加「⚠️ 代码回退未完成…请检查」告警。
Replace the existing GenericAgent experience group QR image with the latest version while keeping the same filename and README reference intact.

Co-authored-by: Shen Hao <shenhao-stu@users.noreply.github.com>
Co-authored-by: GenericAgent <bot@gaagent.ai>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.