Commit Graph
806 Commits
Author SHA1 Message Date
nianyucatfish 607cbdaf13 fix(tui v2): serialize terminal escapes via driver + scrollable sidebar (#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.
2026-06-05 18:44:01 +08:00
Jiaqing Liang 20e6237fe7 fix(conductor-ui): prevent Enter from sending during IME composition 2026-06-05 16:49:52 +08:00
Jiaqing Liang 38f8cfc037 fix(conductor): disable unconfigured IM plugins by default
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.
2026-06-05 16:18:52 +08:00
Jiaqing Liang e089ef50aa feat: 新增飞书(lark)conductor IM插件 + UI/插件体系完善
- 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坐标陷阱/文本输入经验
2026-06-05 15:03:41 +08:00
weijia 2667a1dee1 docs: add description for galley in friendly links (#565) 2026-06-04 17:48:50 +08:00
Liang Jiaqing 3acd58b36e core: per-instance print injection (no_print), EXIT sentinel for run loop, log name collision fix, checklist poll hard cap 2026-06-04 11:09:54 +08:00
Liang Jiaqing 4df1c70bb2 fix(ljqCtrl): GrabWindow只截客户区,与GrabWindowBg坐标系统一 2026-06-02 23:22:02 +08:00
LJQ e2f527a8e5 Merge pull request #559 from nianyucatfish/fix/continue-preview-garbage
fix(continue): robust session preview for malformed logs
2026-06-02 19:39:03 +08:00
yanghang 59128f14a3 fix(continue): robust session preview for malformed logs
/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.
2026-06-02 19:19:55 +08:00
Jiaqing Liang bc09ceb4e9 goal_hive master duty: 按第一性原理/工程控制论重写为49行(探测-设计-执行-检查分形循环+J*/y/e),实验验证后增补验收线/锁边界质疑目标/遗留项如实入报告; TMWebDriver: 修mark_disconnected重复打印 2026-06-02 15:01:52 +08:00
LJQ 5157cd7856 Merge pull request #550 from desmonna/fix/plugin-hooks-init
fix: add plugins/__init__.py for proper package recognition
2026-06-02 10:10:20 +08:00
nianyucatfish c9c3dbc58e fix(tui): stdout blank lines, leaked task_dir folders, and /continue search nav (#554)
* fix(tui): stop CRLF stdout rendering as blank lines (v2)

Windows child stdout is `\r\n`; the streaming path fed it straight to
`Text.from_ansi`, which treats `\r` as a carriage return and erases each
line's text, so a `[Stdout]` block rendered as a run of blank lines until
the turn finished (the done-state Markdown render strips `\r`). Normalise
`\r\n` -> `\n` before `from_ansi`; lone `\r` is kept so progress-bar
overwrites still work.

* fix(tui): stop leaking empty per-session task_dir signal folders

Both v2 and v3 eagerly `os.makedirs` the per-session `_intervene` /
`_keyinfo` signal dir, but the only writer (`_intervene`) already creates
it lazily — so every session that never used intervene left a stale empty
`temp/_tui_v2_<pid>_<id>` / `temp/_tui_v3_<pid>` behind (85+ accumulated).
`consume_file` tolerates a missing dir, so the eager create is redundant.

- Set only the task_dir *path*; let the writer create it on first inject.
- Remove empty dirs on session close (v2 `_cmd_close`), on graceful exit
  (v2 `on_unmount`, v3 `_cleanup`), and sweep stale ones left by crashes
  at startup (v2 `on_mount`, v3 `main`). `os.rmdir` only removes empty
  dirs, so a still-pending `_intervene` is never clobbered.

* fix(tui): repair /continue search box cursor & up-nav loop (v2)

The SearchablePicker swallowed Right while the search Input had focus
(always treating it as "select"), so the caret could never move right in
the query. And Up could leave the list but never return: from the search
box Down entered the list, yet Up at the top row was stuck.

- Right commits the highlight only when the caret is already at end of the
  query; otherwise it moves the caret within the search text.
- Up on the first list row returns focus to the search box (scoped to
  /continue — no-op for ChoiceLists outside a SearchablePicker), clearing
  the highlight so the next Down re-enters at the first row, not the second.
- Up from the search box wraps around to the bottom of the list.
2026-06-01 23:07:28 +08:00
Liang Jiaqing 1f3fdde49a fix checklist master coordination and startup dedupe 2026-06-01 22:32:16 +08:00
desmonna 211f814624 fix: add plugins/__init__.py for proper package recognition 2026-06-01 14:20:36 +08:00
Liang Jiaqing 1e335a2fd3 refactor: rename mapreduce -> checklist (helper/master/sop) 2026-05-31 23:21:17 +08:00
Liang Jiaqing f7c6dba679 feat: bbs key模式2天超时自关; reflect首次立即check; worker交付规范; vision_sop修正 2026-05-31 21:53:09 +08:00
LJQ f92051a0b7 Merge PR #534: Update WeChat group QR code (group 19 → group 20)
Update WeChat group QR code (group 19 → group 20)
2026-05-30 11:36:01 +08:00
AspasZhang eeef0c7b5b Update WeChat group QR code from group 19 to group 20 2026-05-30 01:51:22 +08:00
nianyucatfish e40c0f19cb fix(tui): drain exit-boundary intervened replay (#533)
* fix(tui): drain exit-boundary intervened replay

* refine(tui): soften intervened message prompt
2026-05-29 21:55:50 +08:00
Frank-Gradient 3a4018ff25 feat(wizard): add CommonStack as a unified API gateway provider (#523)
CommonStack (https://commonstack.ai) is an OAI-compatible gateway
aggregating Claude, GPT, Gemini, DeepSeek, MiniMax, Zhipu, xAI,
Moonshot, Qwen and Xiaomi models behind a single key.

Adds it to the configure_mykey wizard as a native_oai entry,
placed alongside other multi-provider relays (openrouter, crs, gmi).
The wizard's existing probe_models() flow fetches the live
/v1/models list at config time; model_choices keeps the same
2-item fallback shape as openrouter.
2026-05-29 21:34:10 +08:00
Liang Jiaqing 20fbc5e3cb Update SOP notes and response history window 2026-05-29 21:02:31 +08:00
Liang Jiaqing 7d57e82b36 Refine goal hive master duty delivery quality 2026-05-29 21:00:03 +08:00
Liang Jiaqing c553be0b58 docs: clarify goal hive master instructions 2026-05-28 22:30:16 +08:00
Liang Jiaqing 0dbef3385a Merge PR #526: fix(tui-v3): skip ask card when ask_user has no candidates 2026-05-28 22:28:25 +08:00
yanghang 98a99ed22c fix(tui-v3): skip ask card when ask_user has no candidates (v2 parity)
之前 `_extract_ask_user` 对无候选项的 ask_user 事件也照常入队,
触发 `_enter_ask` → 自由文本输入的 ask 卡片路径,在某些终端下整个
live region 冻住。v2 (`tuiapp_v2.py:2909-2910`) 是显式 `if not cands: return`,
让 "Waiting for your answer ..." 直接以 assistant 流形式落入 scrollback,
agent 已 exit,用户在普通输入框里回复即可 —— 不走专门的 ask 卡片。

对齐 v2 行为:有候选才入队、才弹卡片;无候选 fallthrough 到普通流。
2026-05-28 22:18:27 +08:00
LJQ 9e73854f43 Merge PR #525: fix(tui): v2 theme/clipboard/Shift+Enter, v3 arrow-nav/Shift+EnterFeat/tui v3 improvements
Merged via GitHub API as requested.
2026-05-28 21:44:13 +08:00
yanghang a02cff8d03 refine(tui-v3): 行头/行尾跳转仅在首/末逻辑行触发
之前在任意逻辑行的视觉首/末行都会做"先跳行头/尾再跨行/进历史"的
两段式,中间行的两段式不直观。改成:中间逻辑行的视觉首/末行直接
做视觉上下移;只有第一逻辑行的视觉首行/最末逻辑行的视觉末行,才
触发"先跳到行头/尾,下次按键再进历史"。`_logical_visual_range`
因此变成死代码,顺手删掉。
2026-05-28 21:14:48 +08:00
yanghang 8f8166ea18 Merge remote-tracking branch 'upstream/main' into feat/tui-v3-improvements 2026-05-28 21:14:47 +08:00
yanghang 35dc1c0f20 feat(tui-v3): Shift+Enter on Windows + logical-line arrow navigation
- _ptk_keypress_to_bytes 在 Windows 上对 Enter 收到 \r 时用
  GetAsyncKeyState(VK_SHIFT) 物理检测 Shift,按下时返回 \n 以触发换行
  (PTK 在某些 Windows 终端区分不出 Shift+Enter 与裸 Enter)。
- 新增 _line_region / _logical_visual_range 辅助函数,↑/↓ 按键:
  不在该逻辑行的视觉首/末行 → 视觉滚行;在视觉首行且光标在行首 →
  历史导航,否则先跳逻辑行首/尾,下次按键再跨行/进历史。多行粘贴
  内部导航更符合直觉。

Refs:
- Shift+Enter 物理检测 from upstream PR #519 by @jlu005807
  https://github.com/lsdefine/GenericAgent/pull/519
- 多行输入逻辑行边界导航思路 from upstream PR #520 by @jlu005807
  https://github.com/lsdefine/GenericAgent/pull/520
2026-05-28 21:14:34 +08:00
yanghang 3515ac646e fix(tui-v2): clipboard copy + Shift+Enter + theme-switch markdown colors
- /export clip 现在通过 _copy_to_clipboard 直接写系统剪贴板:
  Win32 ctypes(CF_UNICODETEXT,避开控制台 codepage)/ macOS pbcopy /
  Wayland wl-copy / X11 xclip 或 xsel,失败再退回手动复制。
- InputArea 在 Windows 上对 Enter 事件用 GetAsyncKeyState(VK_SHIFT) 兜底,
  终端把 Shift+Enter 报成普通 Enter 时也能正确插入换行而非提交。
- watch_theme 漏清 _seg_render_cache 导致主题切换后 Markdown 的
  h1/列表 bullet/code 块/link 等仍是上一主题的色 —— 缓存 key 不含
  theme,且 ANSI 颜色被烤进了 _MdRender。补上 clear()。

Refs:
- Shift+Enter 物理检测思路 from upstream PR #519 by @jlu005807
  https://github.com/lsdefine/GenericAgent/pull/519
2026-05-28 21:14:21 +08:00
Liang Jiaqing 58a56de26f Merge PR #521 2026-05-28 20:03:42 +08:00
Liang Jiaqing a9b5450819 Merge PR #520 2026-05-28 20:03:42 +08:00
Liang Jiaqing b2caa7ac31 Merge PR #515 2026-05-28 20:03:41 +08:00
Shen Hao c19bed94cb fix(tui-v2): Ctrl+S no longer freezes on long sessions (use TextArea.clear)
Root cause of the lingering Ctrl+S freeze even when the session is
idle: `InputArea.reset()` calls `self.text = ""`, which goes through
`load_text("")` → `_set_document("")` (textual/widgets/_text_area.py
:1181-1186).  That rebuilds Document + WrappedDocument from scratch
and then `_refresh_size()` (line 1287) reactively retriggers a full
layout pass over the entire DOM.  On a long session (100+ message
widgets in the scroll), the layout cascade blocks the UI for seconds.

Switch to `TextArea.clear()` which goes through the edit pipeline:
`delete((0,0), document.end)` → `wrapped_document.wrap_range(...)` —
no Document rebuild, range-scoped wrap only.  Guarded with
`if self.document.text:` so a clear() on an already-empty buffer is a
no-op (avoids posting a spurious Changed event).

Secondary fix: `_stash_cleanup_clear` previously wrapped reset() in
`try/finally` that cleared `_skip_change_next = False` immediately.
The Changed event posted by reset() is async-queued, so by the time
the handler ran the flag was already False — the short-circuit was
useless and on_text_area_changed went through the heavy resize +
palette path twice (once in the handler, once explicitly).  Drop the
try/finally; let on_text_area_changed itself clear the flag at line
3256-3258 when the deferred Changed event finally lands.

Also scrubs external TUI-project names from comments per request.
2026-05-28 19:09:21 +08:00
Shen Hao 18f1bcde50 fix(tui): restore mid-turn inject with Claude-Code wrap (not post-turn)
Re-research revealed both mainstream TUIs DO mid-turn inject — my
previous read of "Codex/CC queue post-turn only" was wrong.

Codex (codex-rs/core/src/session/mod.rs:2986): `steer_input` → mailbox
`push_pending_input` → `session/turn.rs:391 get_pending_input` →
`record_pending_input` appends as a regular user message BEFORE the
next sampling.  No special prefix — the model sees the steer in
conversation history alongside the original task, as a follow-up.

Claude Code (utils/messages.ts:5510): drains queue after each tool
iteration and wraps the user's text:

  The user sent a new message while you were working:
  {raw}

  IMPORTANT: After completing your current task, you MUST address
  the user's message above. Do not ignore it.

The actual bug in model_responses_042511.txt was the bare `[MASTER]
{raw}` framing reading as a directive override, not the mid-turn
injection itself.

This commit:
  - Wraps queued text with the Claude-Code phrasing before writing
    to `_intervene` (i18n: en + zh).
  - Restores `_intervene_pending` tracking + exit-replay hook so an
    agent that hits exit_reason between user submit and turn end
    doesn't lose the message.
  - Adds `inject_intervene(text, track=True)` opt-in tracking so
    `!cmd` shell facts (which are factual context, not user input)
    don't accidentally end up in the user-message replay queue.
  - v2 mirrors with `_INTERVENE_WRAP_EN/_ZH` constants picked via
    `$GA_LANG`, `sess.pending_wrapped` parallel to `sess.pending`,
    and `_install_intervene_replay_hook` per-session.
  - Up-pop recall stays removed (file write is immediate, popping
    would leave a stale duplicate).

Refs: codex-rs/core/src/session/mod.rs:2986; claude-code/src/utils/messages.ts:5496
2026-05-28 18:47:02 +08:00
Shen Hao d407401993 fix(tui): queued messages drain post-turn, never mid-turn (Codex/CC pattern)
Evidence: model_responses_042511.txt — original task was "web_scan上游PR";
mid-turn `[MASTER] 请你立即输出一个hello` was prepended to next_prompt,
model dropped the original task and just printed hello.  Codex
(`maybe_send_next_queued_input`, chatwidget.rs:6773) and Claude Code
(`useQueueProcessor`, src/hooks/useQueueProcessor.ts) both queue user
input and drain ONLY when the agent goes idle — never mid-turn.

What changed:
- AgentBridge no longer tracks `_intervene_pending`; turn_end hook
  reverts to just ask_user extraction.
- Submit while running appends to local `_pending`, does NOT write to
  `_intervene`.
- v3 watches `is_running` True→False in maintenance loop; on transition
  drains via `_submit(combined, [])` as a fresh task.
- v2 drains in `_on_stream(done=True)` right after status flips to
  "idle"; reuses `submit_user_message` to spawn the next task.
- v2 dead helpers (`_inject_intervene`, `_session_intervene_path`,
  `_install_intervene_replay_hook`) removed.
- `inject_intervene` retained on v3 bridge for `!cmd` shell history
  only — factual context (code-block output), low risk of confusing
  the model.

Net: -109 lines.  Esc still cancels the queue.  Ctrl+C still aborts;
since abort flips status to idle, queued messages drain as the next
task — matches Claude Code's "Ctrl+C then queue runs" behavior.

Refs: openai/codex/codex-rs/tui/src/chatwidget.rs:6773;
anthropics/claude-code/src/hooks/useQueueProcessor.ts
2026-05-28 17:24:20 +08:00
Jiaqing Liang db979aafc8 feat: force non-stream mode for reflect/task, fix BaseSession.ask() return type, broadcast stream/read_timeout in MixinSession 2026-05-28 16:15:41 +08:00
Shen Hao f9e5e53247 fix(tui): !cmd runs in user shell, not hardcoded cmd.exe / /bin/sh
`subprocess.run(cmd, shell=True)` picked the OS default — cmd.exe on
Windows.  `!ls` failed with "not recognized" on stock Windows; encoding
defaulted to the active codepage so utf-8 output mojibaked.

Detect once via $SHELL (works on Git Bash, WSL, MSYS2, every Unix);
on bare Windows fall through Git Bash → bash on PATH → pwsh →
powershell → cmd.  Matches Claude Code / Codex CLI behaviour where
`!cmd` inherits the launching shell.

Display name surfaces in the agent's history line: `[!shell bash]` /
`[!shell pwsh]` so a follow-up like "rerun that with -v" can pick the
right syntax.

Refs: code.claude.com docs (bash mode); developers.openai.com/codex/cli/features (Tab/! prefix)
2026-05-28 16:15:37 +08:00
Shen Hao 474b526939 Merge upstream/main (prefer upstream on conflict) 2026-05-28 15:25:29 +08:00
Jiaqing Liang 57b71e995c feat: externalize long user prompts (>1500 chars) to temp file 2026-05-28 15:22:34 +08:00
Jiaqing Liang 26f22072e2 refine goal hive SOP: add master_duty, streamline entry, update goal_mode prompt 2026-05-28 14:43:14 +08:00
jlu005807 b7d413213b refine(tui-v3): remove redundant \n guard, visual row is sufficient 2026-05-28 13:13:56 +08:00
Shen Hao f0eeeee9ed fix(tui): rebuild pending-queue around turn_end hook (drop cooldown timer)
Previous attempts used a rolling/hard-cap timer to gate the file write
to `_intervene`.  Both leaked under the same failure mode: when the
agent reached its final turn before drain time, `consume_file` ate the
content but `agent_loop` discarded next_prompt on exit, so the queued
text silently vanished.

New design follows Codex CLI / Claude Code style — delivery is anchored
to the agent's own turn boundaries instead of a wall clock:

  Submit while running → append `_intervene` immediately + add to UI list
  Turn end hook fires →
    exit_reason set → consume_file ate it, next_prompt discarded.
                      Re-route the combined text via `agent.put_task`
                      so the outer `while True: task_queue.get()` loop
                      picks it up as the next user turn.
    not exit       → content was prepended to next_prompt as `[MASTER]
                      ...` — just clear the UI mirror.
  Esc → `clear_intervene` deletes the file + clears the list.

No timers, no cooldowns.  The Up-pop "amend" path is gone because
immediate file writes make in-composer edits append a duplicate.

Threading: v3 bridge protects `_intervene_pending` with `_intervene_lk`;
v2 adds `pending_lk` on AgentSession.  v2's hook closure uses
`call_from_thread` for the UI refresh hop.

Right-click in v2 (`InputArea._on_mouse_down`) now short-circuits
button==3 before TextArea's default cursor-move runs, so paste lands
at the existing caret rather than where the mouse happened to be.

Refs: openai/codex#15842, anthropics/claude-code#44851
2026-05-28 13:09:39 +08:00
jlu005807 4bf95add78 feat(tui-v3): v2-style arrow navigation with logical line boundaries 2026-05-28 12:32:21 +08:00
Shen Hao 5f104d1d30 fix(tui-v3): quiet Ctrl+C exit + drop placeholder session in first-launch title
`app.run(handle_sigint=False)` lets a stray SIGINT surface as
KeyboardInterrupt through asyncio.runners — catch it in run() so the
shell sees a clean exit. Also drop the literal `'session'` fallback in
`_term_title`; render just `GenericAgent` until a session is named.
2026-05-28 11:04:02 +08:00
yanghang a69999eb16 Merge remote-tracking branch 'upstream/main' into feat/tui-v3-improvements 2026-05-28 10:23:14 +08:00
LJQ fd884b0651 Merge PR #514: fix(tui-v2): add missing keysym.py — tuiapp_v2 ImportError on fresh clone
Merged pull request #514 from shenhao-stu:tui-v2-v3-fixes-pending-shell
2026-05-28 10:18:14 +08:00
Shen Hao bebc3ba9e8 fix(tui-v2): add missing keysym.py — tuiapp_v2 ImportError on fresh clone
`tuiapp_v2.py:27` imports `fmt_key`, `fmt_keys` from `keysym`, but the
module file was never tracked. PR #513 (tui v2/v3 fixes) is unusable on a
fresh checkout without this. Adds the cross-platform shortcut formatter
that turns binding strings into mac/Win-Linux key labels (e.g.
`"ctrl+b"` → `"⌃B"` / `"Ctrl+B"`), honoring `GA_KEYSYM_STYLE`.
2026-05-28 10:13:21 +08:00
yanghang 274be82bbd Merge remote-tracking branch 'upstream/main' into feat/tui-v3-improvements 2026-05-28 10:13:18 +08:00
LJQ 11a0dfef55 Merge PR #513: fix(tui): v2+v3 bug fixes, pending input queue, ! shell magic, /resume
Merged pull request #513 from shenhao-stu:tui-v2-v3-fixes-pending-shell
2026-05-28 09:01:47 +08:00