fix(fsapp): avoid premature task finalization#686
Open
Tomodad wants to merge 795 commits into
Open
Conversation
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>
…rface-v2 feat: 更新飞书接口
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: 修复安装文档中的链接路径
feat: add agent lifecycle hook trigger mechanism
This reverts commit ce0ad05.
- Add sys.path.append for llmcore's own directory as fallback - Remove dead variable _imp_err - Distinguish missing mykey vs import failure with clear error messages
…y discover_and_load)
…; add export_history; fix GenericAgent typo
…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
…ge GLM/Kimi/MiniMax into vendor tables
…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>
…ompt The /update prompt reconciles the working tree "upstream-first" file by file. That silently drops fork-local additions to files BOTH sides edit — most painfully .gitignore, whose local ignore rules vanish, so a fork's private dirs resurface as untracked noise (and can get committed by accident). Treat files carrying a `Fork-only local overlay` marker (e.g. .gitignore) as a MERGE: take upstream as the base, then re-append everything under the marker verbatim, and verify the marker survived. Along the way, tighten both the EN and ZH prompts (75 -> 58 lines) without dropping any operative step.
…erlay-merge fix(slash_cmds): merge additive-overlay files (e.g. .gitignore) on /update
…OP); ga.py: file_patch/file_write 保持原文件换行符, 修复LF文件被污染成CRLF; _arg 类型强转修复字符串false等
Add maxlen_multiplier derived from context_win (capped 3x); drives per-tool truncation caps and history compression interval. WebScan uses half-increment growth to avoid saturating its 35000 base. Docs updated for the new coupling.
Hard-delete path can preserve the first K history messages, insert a "..." gap, and strip dangling tool_use at the cut. Default 0 keeps previous behavior.
Ignore assets/glm52_instruction.txt and assets/gpt55_instruction.txt so local customizations stay out of the repo.
…#681) The agent's default cwd is the temp/ subdirectory, where git resolves '-- <file>' pathspecs relative to cwd. Fed the root-relative paths that 'git diff --name-only' prints, 'git checkout upstream/main -- <file>' fails with "pathspec did not match", breaking the working-tree reconcile steps of /update. The prompt now tells the agent to run every git command from the repo root (_ROOT), in both EN and ZH. Co-authored-by: Shen Hao <shenhao-stu@users.noreply.github.com>
Keep task hooks summary-only so turn-end metadata cannot finalize queued work before display queue done. Co-Authored-By: GenericAgent <bot@gaagent.ai>
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.
Summary
exit_reasonmetadata from finalizing queued work earlydisplay_queue'sdoneitem as the normal task-finalization pathFixes #685.
Root cause
_make_task_hookreceived_finishason_finaland invoked it whenever a hook context containedexit_reason. That metadata can describe an intermediate agent turn rather than completion of the queued Feishu task, so_finishcould setresult["sent"]before the display queue published its realdoneitem.Change
Remove the final callback from
_make_task_hookand let the hook handle onlysummary -> card.step. The existing display-queuedone -> _finishpath and timeout/stop/error failure paths are unchanged.Verification
exit_reasonfinalization while preserving summary updatesexit_reason, repeated calls, inactive task IDs, and formatting inputspython -m py_compile frontends/fsapp.pygit diff --check