| fix(webui): /webui 用 TUI 当前会话播种 LiveSession,续聊后直接落到该会话
atomcode -c 续聊后 ctx.current_session 已是续聊会话,但 /webui 调用的是
ensure_live_session——新建空 LiveSession(空消息 + 随机 LIVE_SESSION_ID),从不把
当前会话搬进去。于是 webui 连 /live 拿到空快照、落到空白新页面而非续聊会话。
- live_api.rs:新增 ensure_live_session_seeded(initial, session_id),新建时用给定消息
播种并复用 session_id(LIVE_SESSION_ID + 执行器 id 一致,后续每轮覆盖同一会话文件、
不产生重复);ensure_live_session 改为空播种封装。lib.rs 导出之。
- commands.rs:/webui 在开浏览器之前先用 ctx.current_session 的消息+id 播种(非空才复用
id;先播种再开浏览器,避免浏览器抢先连 /live 建出空会话)。attach_live_session 加
render_snapshot 参数:/webui 传 false(画面已有该对话,跳过快照回放避免重复刷),
/sync 传 true(重新附着补 webui 期间对话)。
live 测试 core(4)/daemon(3) 全过;LiveSession initial 播种本就被 core 测试覆盖。
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
| 22 小时前 |
| chore(highlight): delete dormant syntect machinery and dependency
Follow-up to 5e498358 (drop per-token colour from code blocks). Now
that highlight_block returns plain-indented source unconditionally,
the syntect SyntaxSet/Theme/CodePalette plumbing and the syntect
dependency itself are dead weight — delete them. Theme module trims
to just the runtime palette accessors actually used by non-code-block
markdown (headings, inline code, etc.).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
| 8 天前 |
| refactor(i18n): move source files to core + polish wave
The i18n module previously lived in atomcode-tuix/src/i18n/ so all
core renderers wanting localised output had to either reach into the
tuix crate (a dependency inversion) or hardcode strings. Now that
coding_plan::setup::SetupReport::render and similar core paths
need the same t(Msg::*) lookup, the natural home for the i18n
module is atomcode-core. Tuix keeps a one-line re-export shim so
every existing crate::i18n::* call site compiles unchanged.
Bundled in the same wave because they depend on the move or land
naturally alongside it:
* coding_plan/setup.rs — JediTerm SGR-9 fallback. When the
TERMINAL_EMULATOR env var marks the host as JetBrains JediTerm
(Android Studio / IntelliJ / etc.), strikethrough renders
inconsistently, so locked-model rows fall back to ASCII ✗ +
"(Locked: require plan upgrade)" text marker. Threaded through a
testable render_with_terminal_caps(is_jediterm: bool) so both
shapes are unit-coverable without env mutation.
* event_loop/commands.rs — /language slash command now emits the
unified Msg::LanguageSwitched { label, locale } line (matches the
LanguagePicker's confirmation shape, not the older bare-locale-code
one). /status instruction-files block migrated to
Msg::StatusInstructionFilesHeader/Present/Missing.
* modals/language_picker.rs — already on LanguageSwitched, kept
in sync.
* render/alt_screen.rs — adds regression test
command_output_survives_subsequent_input_prompt_redraw: pins the
/language flow where the picker emits a CommandOutput then the
event loop immediately redraws the input prompt (no menu). The
confirmation must stay in body_lines AND in painted output past
that second redraw, otherwise the user sees no feedback that the
locale switch took effect.
All existing i18n tests (13 in core, multiple in tuix), coding_plan
suite (51), and the new alt_screen regression pass.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
| 24 天前 |
| refactor(tuix): collapse dead selection logic + harden Drop panic path
Task 7.1 follow-up to 62036bdf (which disabled SGR mouse capture
in favour of terminal-native selection/wheel/copy). With ?1002h /
?1006h never emitted, the terminal stops sending MouseDown/Drag/Up
to atomcode, so the internal SelectionState machinery is now
unreachable from any interactive event:
* begin_selection / update_selection / end_selection / copy_selection
trait methods never fire
* apply_selection_overlay paint hook never has selection bounds
* Ctrl+C copy_selection fallback always returns false → degrades
to plain Cancel (== pre-62036bdf behaviour)
* Windows OS-level Ctrl+C signal arm copy_selection branch + the
keyboard-echo suppression debounce both lose their reason
to exist
Deletes: selection.rs entirely (789 lines), the 4 trait default
methods, the SelectionState field + apply_selection_overlay +
screen_to_body in retained, the 4 RenderCmd variants and worker
forwards, the 3 InputEvent mouse variants and reader arms, the
Ctrl+C copy_selection intercept, win_ctrl_c copy_selection branch,
last_ctrl_c_copy debounce, and 7 selection-only retained tests.
Keeps: ?1002l / ?1006l defensive disable in shutdown/suspend/Drop
(clears any stale capture inherited from a prior process), and
the InputEvent::MouseScroll → scroll_body no-op (some terminals
forward wheel events outside the SGR mouse protocol).
Also hardens RetainedRenderer::Drop with cursor / autowrap /
DECSTBM restore, mirroring shutdown(): a panic while the spinner
hid the cursor (DECTCEM off) used to leak that state into the
parent shell since shutdown() never ran on the panic path.
Net: 9 files, -1429 / +44.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
| 8 天前 |
| feat(session): 恢复对话时还原轮次分隔线与每轮 token/工具统计
会话只持久化 messages,每轮的 token/时长/轮数统计来自实时 TurnComplete 事件、从不
落盘;replay_session 又只在开头/结尾画分隔线,轮与轮之间一条没有。于是恢复后上一轮
模型输出和下一条用户输入贴死,统计分隔线也丢了。
- session.rs:新增 TurnStat(after_message 锚点 + 轮数/工具数/时长/tokens/errored)
和 Session.turn_stats(#[serde(default)],老会话加载为空)。
- event_loop/mod.rs:TurnComplete 时把本轮统计按"轮结束时的消息数"锚定推入
current_session.turn_stats 再落盘(对取消/缺失的轮也稳)。
- session_picker.rs:replay_session 在每个轮边界(每条非首条用户消息前 + 末轮)画
分隔线——有统计就画与实时一致的 ✓ Done · N 轮 · M 工具 · Xs · K tokens(错误轮 ✗),
没有就画纯横线。新会话 resume 完整还原统计;老会话也能拿回轮间间隔(纯线)。
新增测试:turn_stats 落盘往返 + 老会话缺字段降级空;turn_divider_label 三态。
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
| 1 天前 |
| chore(tui): on_resize 增加 RSZ 阶段诊断日志(定位 conhost 闪退)
ED2 修复后仍闪退。在 on_resize 各阶段「写控制台之前」打 RSZ trace
(enter/wipe/body/paint/done),并打印 legacy_conhost 标志与新旧尺寸。
因为是在把字节发给 conhost 之前记录,即便 conhost 当场 fastfail、
trace 文件里仍会留下「最后一个阶段」=触发崩溃的那步,并能确认 ED2
安全路径到底有没有生效。仅 ATOMCODE_TUIX_LOG 开启时有开销。
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
| 1 天前 |
| feat(undo): i18n strings + command description for /undo
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
| 23 小时前 |
| Merge branch 'feat/webui-design' into release/v4.24.1
feat/webui-design 带来 WebUI:登录入口、会话重命名/删除、设置对话框重构、
GET /skills、daemon 抽出 run_server 薄壳 + WebUI token 鉴权 + LiveSession 同步。
冲突解决:
- tuix/src/lib.rs:Context 结构体两侧各加字段,全部保留
(dangerously_skip_permissions/pending_guide_topic + sync_session/sync_forwarder)。
- tuix/src/event_loop/mod.rs:保留 webui 的「sync_session 投递 LiveSession / 否则
原有 SendMessage」分支,并在其前置上 release 的「每轮清理 hook 警告」逻辑。
- daemon/src/main.rs:取 feat/webui-design 的薄壳版本(逻辑已迁入 lib.rs::run_server);
release 对旧 main.rs 仅有的改动是 OpenAtomcode 遥测字段,见下。
语义合并修复(非冲突但合并后不可编译):
- daemon/src/lib.rs:OpenAtomcode -> OpenAtomcode { dangerously_skip_permissions: false }
(release 给该遥测事件加了字段)。
- daemon TurnRunner 构造:release 把 hook 系统从 HookExecutor/hook_executor 重构为
hook_engine(HookEngine) + current_turn_number。daemon/lib.rs 与 live_api.rs 改用
HookEngine::new()+load_all(&working_dir),与 TUI 主 agent 一致,保留 webui
「daemon 加载并执行配置 hook」的行为;并移除 TurnParts 中不再使用的 hook_executor。
验证:cargo check --tests -p atomcode-core/atomcode-daemon/atomcode-tuix/atomcode 全部通过。
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
| 2 天前 |
| fix(md): strip_md_for_width must mirror render_inline's parser, not blanket replace
User report (Mac + Windows both): markdown tools-listing table where one
row containing ` src/**/*.ts ` has its right border painted ~2 cells
past the column position every other row uses. Visible as a tiny orphan
│ segment hanging off the right side of just that row.
Root cause: strip_md_for_width was naive —
s.replace("**", "").replace('`', "")
The (**, "") replacement is unconditional, so it also strips ** that
appears as *literal content inside an inline-code span*. The reported
input drives the divergence cleanly:
* Markdown source: 文件名通配符匹配(如 \src/**/*.ts\)
* render_inline parses ` … ` as one code span; the inner text
src/**/*.ts is preserved verbatim (the ** is glob-pattern literal,
not a bold marker — bold markers can't open inside a code span
anyway). Visible width 34 cells.
* strip_md_for_width blindly does .replace("**", "") → drops the
two stars → returns the cell as if its content were src//*.ts →
display_width reports 32 cells.
plain_w (= 32) is then fed to the table-layout padding step
let pad = w.saturating_sub(plain_w);
so pad is 2 cells short. Render paints 34 cells of content + 0 padding
into a 32-cell column slot, the │ separator lands 2 cells past where
the column was supposed to end, and that row's right border no longer
lines up with neighbouring rows.
Symmetric latent bug: *italic* was the opposite — render_inline strips
the single * and emits "italic", but strip_md_for_width had no rule
for single * so it kept them. plain_w over-counted by 2 cells per
italic span, padding came out 2 cells too long, that row's right border
landed 2 cells short. Same misalignment class, reverse direction.
The bug is architectural: any string-replace-based stripper can't honour
markdown nesting, because nesting is decided by *parser state*. The fix
has to share the parser. Rewrite strip_md_for_width as a parallel walk
of the same character pump render_inline uses, emitting only the inner
text and falling back to "keep marker verbatim" for unclosed runs (which
is also what render does — so unclosed markers stay as literal content
in both functions and column widths match).
The duplication between the two functions is intentional; refactoring
into a shared parser with an "emit SGR / strip SGR" flag is a separate
cleanup that doesn't need to gate the bug fix. Both functions are small
(~40 lines each) and the rules are now lock-step.
Tests:
* strip_md_for_width_keeps_double_star_inside_inline_code — the
reported bug, minimal repro.
* strip_md_for_width_strips_single_star_italic_markers — the
symmetric latent bug.
* strip_md_for_width_strips_double_star_bold_markers,
strip_md_for_width_strips_backtick_code_markers — happy path.
* strip_md_for_width_keeps_unclosed_markers_verbatim — fallback
behaviour matches render_inline's literal-marker-on-no-closer rule
for all three marker kinds (**, *, ` ``).
* strip_md_for_width_matches_render_visible_width_on_glob_cell —
end-to-end: feed the literal reported cell through both functions,
strip SGR escapes from the render output, assert equal. Locks in
the "stripper visible width == renderer visible width" invariant
that table layout depends on.
50 markdown tests pass (44 → 50, 6 new), no regressions in the existing
suite.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
| 7 天前 |
| fix(tuix): fix Windows home path collapse bug and add path-aware truncation for status row
1. Fix collapse_home_with Windows slice-offset bug: Old code used path[s.len()..] where s was the lowercase remainder after strip_prefix, causing wrong offset (took last N chars instead of everything after home prefix). e.g. C:\Users\hao\Documents\WPSDrive\NotLoginPage showed as ~NotLoginPage instead of ~/Documents/WPSDrive/NotLoginPage. Fix: use home_str.len() to slice original path directly.
2. Add truncate_path() in width.rs for smart path truncation: Preserves the last path segment (project/folder name) when the path exceeds available width, replacing leading segments with .../. CJK-aware, handles both / and \ separators.
3. Pre-truncate cwd in both retained and alt_screen renderers: Compute cwd budget based on terminal width minus model name, separators, and mode badge widths. Prevents long paths from overflowing the status row on narrow terminals.
Closes: gitcode.com/atomgit_atomcode/atomcode/issues/356
| 21 天前 |
| feat(codingplan): per-model base_url/type/context_window + red locked rows
ModelEntry now accepts the new wire shape (id, is_infinity,
is_atomcode_exclusive, display_model_name, base_url, type,
context_window, plan_available). Missing / empty / zero fields fall
back to the existing constants (LLM_BASE_URL, PROVIDER_TYPE,
CONTEXT_WINDOW) so older models-v2 builds keep working.
build_codingplan_provider takes the full entry instead of just the
model name and threads each server field into ProviderConfig — bigger
models (e.g. GLM-4.6 128k) no longer get silently capped at 64k.
Locked-model rows render in the terminal-theme red across all three
renderers: a new scrub_controls_keep_sgr sanitizer keeps CSI ...m
sequences (SGR — cosmetic only) while still stripping cursor / OSC /
other dangerous escapes. alt_screen + plain reach the terminal with
the SGR intact; retained gets a new push_str_cells_sgr that parses
SGR into CellStyle so the cell-grid pipeline shows the same color
rather than literal ^[[31m characters. Three layered signals — red
color, ✗ prefix, "(requires Pro plan or higher)" suffix — any one
failing still leaves the meaning intact.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
| 23 天前 |
| fix(bash,tuix): 修复 bash 长卡死 + 审批阶段 spinner 文案误导
bash.rs: snapshot_workspace_changes 对 git status --porcelain -uall
无超时,遇到 .git/index.lock 残留 / 卡死的 fsmonitor 等情况会拖垮整个
bash 调用 —— 曾观察到 273s 卡死,远超 bash_execute 自身的 60s 超时
(那 60s 超时只覆盖子进程,覆盖不到这个 pre/post 快照)。现强制 2s
超时,超时即视为"无快照"。同时给 echo/ls/grep/cat/... 等纯只读命令
直接跳过快照,hot path 省两次 git fork。新增 6 个单元测试覆盖
is_pure_readonly_command 的边界:纯 readonly / 2>&1 / 重定向 / 命令
替换 / 链式 / 首词不在列表。
tuix/state.rs: 审批阶段 spinner 之前不切文案、不重置计时 —— 用户看到
"Running Bash… · 273s" 实际上是在等审批,骗人显示和真正的卡死无法
区分。on_approval_needed 现在暂存原 "Running X" label、切到
"Waiting approval"、重置 phase 时钟;on_approval_resolved 还原。
最近 grep / list_symbols / diagnostics 都改成"外部非敏感路径 →
RequireApproval",扩大了这个 UI bug 的触发面。新增 4 个测试覆盖
label swap / restore + phase clock 重置。
tuix/event_loop: 调用 on_approval_needed 时传 PascalCase 的 display
名,落到 prior_spinner_label 里形如 "Running Bash"。
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
| 12 天前 |
| fix(tui): 经典 conhost resize 闪退——改用 ED2 清屏避开崩溃
Windows 经典控制台 conhost(10.0.19041) 在窗口拖动 resize 时会 fastfail
(0xc0000409),整个终端窗口直接消失。根因:on_resize 用逐行 CUP+EL 擦除整屏
(2×rows 条序列),在 conhost 缓冲区 resize 中途踩中其 VT 处理 bug。
新增 TerminalCaps.legacy_conhost(is_windows 且无 WT_SESSION/TERM_PROGRAM),
在该宿主下 resize 改发单条 ED2(\x1b[2J\x1b[H) 清屏,conhost 能稳定处理;
其它终端(mac/Linux/iTerm2/Windows Terminal)维持原逐行路径不变。附检测单测。
注:conhost 自身 bug 改不了,只能让 atomcode 不触发它;本修复移除最可能的
触发点(整屏擦除 burst),需在 Windows 经典控制台实测确认。
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
| 1 天前 |
| fix(build): silence Windows-only unused_imports + dead_code warnings
Two warnings surfaced when building on Windows:
1. process_utils.rs:13 — use std::os::windows::process::CommandExt
inside suppress_console_window (for tokio::process::Command)
was unused. tokio exposes creation_flags as an inherent method
on Windows so the trait import is redundant; std's variant in
suppress_console_window_sync still needs it. Drop the import and
document why the two bodies differ.
2. terminal_bg.rs — parse_osc11_response and parse_hex_component
are only reachable from detect_light_unix (cfg unix) and from
the in-file tests. On Windows non-test builds they're dead. Gate
both with #[cfg(any(unix, test))] so they're excluded from the
Windows prod build entirely instead of #[allow(dead_code)]-ing
the warning away.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
| 8 天前 |
| fix(tuix): dark-theme color hierarchy for tool-call header & result line
In dark theme Role::Muted resolves to near-white (SGR 37), so the ●
tool-call bullet and its └ result line were visually indistinct from
the bold default-fg command name — only bold told them apart. Render
the ● bullet, the └ prefix, and the success summary line FAINT on
dark so they dim to gray and read as subordinate to the bold command
name, matching the two-tier hierarchy light theme already gets from
MUTED_LIGHT (DarkGrey). Light theme is unchanged.
Extends the test VirtualTerminal to track SGR 2 (faint) / SGR 22 so the
behavior is asserted.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
| 6 天前 |
| Merge feat/telemetry-v2 into release/v4.20.1
Integrates the atomcode-telemetry crate (new) with the MCP support,
Ollama function-calling, and background-commands features from release.
Key resolutions:
- CLI Commands enum: kept both Mcp(McpCli) and Telemetry { action } variants
- atomcode_tuix::run(): added mcp_registry + mcp_connect_rx + telemetry params
- LoopCtx: added both mcp_registry/mcp_connect_rx/mcp_reload and telemetry fields
- runner.rs: kept telemetry scope + tel_return! macro; added unwrap_doubly_nested_args
from HEAD into v2's ToolCallDone arm; preserved HEAD's async ToolRegistry API
- bash.rs: merged HEAD's enhanced netcat/mknod security detection with v2's
code structure improvements; added visit_f64 to lenient deserializer
- agent/mod.rs: took HEAD's emit_rich_context_stats().await form
- daemon/main.rs: kept register_sync form + telemetry Disabled("daemon") init
- commands.rs: took HEAD (mcp, background, init; no /fixissue)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
| 1 个月前 |
| Merge feat/telemetry-v2 into release/v4.20.1
Integrates the atomcode-telemetry crate (new) with the MCP support,
Ollama function-calling, and background-commands features from release.
Key resolutions:
- CLI Commands enum: kept both Mcp(McpCli) and Telemetry { action } variants
- atomcode_tuix::run(): added mcp_registry + mcp_connect_rx + telemetry params
- LoopCtx: added both mcp_registry/mcp_connect_rx/mcp_reload and telemetry fields
- runner.rs: kept telemetry scope + tel_return! macro; added unwrap_doubly_nested_args
from HEAD into v2's ToolCallDone arm; preserved HEAD's async ToolRegistry API
- bash.rs: merged HEAD's enhanced netcat/mknod security detection with v2's
code structure improvements; added visit_f64 to lenient deserializer
- agent/mod.rs: took HEAD's emit_rich_context_stats().await form
- daemon/main.rs: kept register_sync form + telemetry Disabled("daemon") init
- commands.rs: took HEAD (mcp, background, init; no /fixissue)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
| 1 个月前 |
| fix(tui): 修正含 Tab 缩进的粘贴文本输入光标错位
输入框对 Tab 用了两套不一致的列宽模型:渲染端 push_str_cells 把每个 \t
画成 SOFT_TAB_WIDTH(4) 个空格单元,而光标定位端 wrap_with_cursor 用
cluster_width('\t')==0 计列。于是每个 Tab 缩进的行,光标会比真实插入点
左偏 4×Tab数 列——粘贴 IDE 的 Tab 缩进代码(方法体两级=8 列)时光标左偏
8 列,与实际输入位置不符。仅粘贴代码会触发,键入不会插入字面 \t(Tab 绑定
补全)。
修复:wrap_with_cursor 把 \t 按 SOFT_TAB_WIDTH 计列,与渲染端一致;
SOFT_TAB_WIDTH 提为 pub(crate) 作单一来源。新增回归测试。
来源 Issue: https://atomgit.com/atomgit_atomcode/atomcode/issues/528
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
| 3 天前 |