| feat(undo): UndoToPrompt command + ConversationTruncated/UndoFailed events
Agent truncates the authoritative conversation and replies; TUI persists the
truncated session, replays scrollback, and refills the input box.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
| 1 天前 |
| !136 merge cocreate-issue-url-gitcode into release/v4.23.1
[共创大赛] 支持 GitCode 镜像域名 Issue URL
Created-by: Graychao
Commit-by: Codex
Merged-by: saulcy
Description: ## Summary
- allow IssueRef::parse to accept gitcode.com issue URLs in addition to atomgit.com
- strip an optional host port before mirror-host validation
- add regression coverage for GitCode issue links and host-port issue URLs
## Why
parse_repo_url already treats gitcode.com as an AtomGit mirror, so a checkout whose origin is gitcode.com can be detected correctly. IssueRef::parse still rejected issue URLs copied from GitCode, which blocks /fixissue and related workflows for users who copy the issue link from the current GitCode UI.
## Verification
- docker run --rm -v /tmp/atomcode-cocreate-pr:/work -v /tmp/atomcode-cargo-registry:/usr/local/cargo/registry -w /work rust:1.88 cargo test -p atomcode-core --lib atomgit::url
- git diff --check
Note: a broader cargo test -p atomcode-core on current main is blocked by the existing hooks_integration test initializer missing plugin_root; this is covered separately by MR !134.
See merge request: atomgit_atomcode/atomcode!136 | 13 天前 |
| feat(referral): add invite referral tracking system
- Add --invite= / ATOMCODE_INVITE support to install.sh and install.ps1,
writing pending_invite file with invite code, install UUID, and timestamp
- Create pending_invite.rs module to parse and validate pending_invite file
with 30-day expiry and format checks
- Add maybe_emit_install_completed() to Telemetry, emitting InstallCompleted
event on first launch after referral install
- Attach invite_code and install_uuid to LoginSuccess events across CLI
login, daemon API login, and codingplan setup paths
- Add referral.html landing page with invite code generation, metrics
dashboard (devices, active users, conversion rate, settlement), leaderboard
and rules
| 2 天前 |
| feat(mcp): skip non-protocol stdout lines from noisy stdio servers
Some third-party MCP servers incorrectly print status logs to stdout
after initialization, causing an immediate transport error. Change the
stdio transport to skip plain-text lines (up to 100) between protocol
messages instead of bailing, making these servers usable while still
failing on truly malformed or infinite non-protocol output.
- recv_jsonrpc_response now loops past non-JSON/Content-Length lines
- Add MAX_SKIP_LINES=100 guard to prevent infinite loops
- Add env-gated noise (MCP_TEST_STDOUT_NOISE_AFTER_INITIALIZED) to
mock test server for testing this scenario
- Add integration tests for both noisy and clean stdio servers
| 29 天前 |
| fix(codingplan): 漂移监控按用户实际档位查询模型列表,消除 Lite/Pro 永久误报
models-v2 的 plan_available 是相对所请求的 plan_type 计算的(ModelEntry 文档):
setup 用用户实际档位(如 Lite)请求并注册 provider,但漂移监控 spawn_check 硬编码用
PlanType::Max 请求——Max 档下高档模型(如 GLM-5.1)也被标成 plan_available=true,
服务端列表比本地(按实际档位配置)多出来,decide_warning 永久返回 StaleList,状态栏
一直显示"CodingPlan 模型列表更新 — 可执行 /login"。
- types.rs:新增纯函数 PlanType::from_plan_name,把 /status 的 plan_name 映回档位;
无法识别(Free/空)返回 None。
- monitor.rs:spawn_check 先 status_v2() 取真实档位,再以该档位请求 list_models_v2;
无法确定档位时静默跳过,不再用 Max 兜底。升级套餐后自纠正。
新增测试 plan_type_from_plan_name;monitor/coding_plan 既有测试全过。
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
| 1 天前 |
| 统一 ATOMCODE_HOME 路径语义:设置 ATOMCODE_HOME 后不再多加 .atomcode 这一级
| 13 天前 |
| feat(plugin): support install/uninstall by plugin name, per-startup marketplace sync, and CC skill auto-detection
| 3 天前 |
| docs(undo): note real-prompt vs divider numbering trade-off in prompt_count
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
| 1 天前 |
| fix(prompt,ctx): Plan Mode 与压缩摘要移出 system,保住前缀缓存
承接会话级 system 冻结(3cda63dc)。litellm 线上 175 对 system 断裂抽样
归类 + 逐字节 diff 证据(systemA/B/D):冻结 build_system_prompt 还不够,
有两处动态内容仍进了 system 第0条。
== systemA:Plan Mode 指令进 system(占 system 断裂 12%) ==
prompt.rs 在 plan_mode 时往 system 注入整段 "=== PLAN MODE (ACTIVE) ===";
中途切换 plan mode 就重写 messages[0]、整会话缓存归零。
修复:从 system 删除该段;改为在 AgentCommand::SetPlanMode 状态切换时,
通过 add_synthetic_user_message 往历史注入一次说明(进入/退出各一条),
system 保持会话级常量;read-only 工具门控(use_read_only)继续每轮强制约束。
不再在 SetPlanMode 里失效 cached_system_prompt。
== systemB:压缩摘要被合并器折进 messages[0] ==
cold_summaries / 溢出 digest 原以 Role::System push(render.rs),被
clean_message_pipeline 的"合并连续 system 消息"折进 messages[0] → 每次压缩
连 ~16K system 前缀一起失效。改成 Role::User:合并器不再污染 messages[0],
冻结的 system 跨压缩字节稳定。影响:摘要现以 user 角色呈现(标注
"[Earlier conversation history…]"/"[Context overflow…]"),会并入相邻 user
消息;压缩时仍从 messages[1] 断一次(历史确实变了,不可避免),但 system
前缀保住。truncate(cold_msgs)/microcompact 的位置逻辑与角色无关,已验证。
未处理(下游已确认、单独任务):systemC/E —— 主/子 agent、工具型短请求共用
session_id(约占 system 断裂 22%),非改写 bug,需独立缓存归属,另议。
回归测试:
- agent/mod.rs::plan_mode_is_not_in_system_prompt(plan_mode 不进 system 且
不改变 system 一个字节)。
- ctx/render.rs::test_no_consecutive_system_messages_after_compression(更新:
断言摘要不在 system、改骑在 user 消息上)。
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
| 1 天前 |
| feat: suppress Windows console windows & pass --client mode to daemon
- Add process_utils module with CREATE_NO_WINDOW helpers for Windows
- Apply suppress_console_window to all child process spawns (git, curl,
rg, find, LSP servers, MCP servers, hooks, bash tool, etc.)
- Add --client CLI arg to daemon, propagate SessionMode to API handlers
- Enhance build_api_system_prompt to align with TUI (instructions,
memory, git snapshot, platform rules, model identity)
- Bump vscode extension to 0.0.3
| 24 天前 |
| fix: ~ 路径在 sudo 场景下错误解析为 /root
问题: 当程序通过 sudo 运行时,~ 被错误解析为 /root 而不是实际用户的 home 目录
原因: dirs::home_dir() 优先读取 $HOME 环境变量,在 sudo 下 $HOME=/root
修复方案:
- 新增 real_home_dir() 函数,检查 SUDO_USER 环境变量
- 通过 getpwnam_r 获取实际用户的 home 目录
- 替换所有 dirs::home_dir() 调用
修改的文件:
- atomcode-core/tool/mod.rs: 新增 real_home_dir() 和 get_user_home()
- atomcode-core/tool/bash.rs, cd.rs, write.rs: 使用 real_home_dir()
- atomcode-core/agent/services.rs, mcp/config.rs, turn/datalog.rs: 同上
- atomcode-core/skill.rs, graph/indexer.rs, config/mod.rs: 同上
- atomcode-daemon/main.rs: 使用 real_home_dir()
- atomcode-cli/main.rs: panic hook 中使用 real_home_dir()
- atomcode-telemetry/identity.rs: 新增简化版 real_home_dir()
- atomcode-tuix/platform.rs: 使用 atomcode_core::tool::real_home_dir()
Closes: #214
| 1 个月前 |
| chore(core): 清理既有编译告警
合并后 cargo check 暴露的存量告警(均在 atomcode-core,与本次合并无关):
- 移除未使用 import(glob/bash/hook_test/webhook_test/hook_integration_test)
- 去掉多余 mut、未使用变量改 _result(turn/tests、plugin/loader)
- 死代码测试辅助函数加 #[allow(dead_code)](engine、config_loader)
- RAII Guard 的 TempDir 字段加 #[allow(dead_code)](plugin_integration)
- script_runner trait-bound 断言 require_hook::<ScriptHook>(); 改为真正调用,
消除 path_statement 告警
- hook_integration_test::create_test_runner 改 async 并 .await registry.register
(register 是 async,原先 future 被丢弃,MockEchoTool 实际从未注册——顺手修掉)
验证:cargo check --workspace --exclude atomcode-codingplan-crypto --tests 零告警零错误。
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
| 2 天前 |
| feat(undo): i18n strings + command description for /undo
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
| 1 天前 |
| fix(live): 审批通道整回合保持注册,修复同回合第二个工具被秒拒
webui 发起、tuix 审批的共享会话里,DaemonTurnExecutor::run_turn 整回合
只注册一次 approver sender,而 LiveSession.approve() 用 .take() 投递后
即 drop 掉这唯一的 sender,关闭了执行器持有的审批通道。结果同一回合内
第二个及之后的工具在 InteractivePermissionDecider::decide() 里 recv()
立刻得到 None → unwrap_or(Deny),审批卡刚弹出就被秒拒。
approve() 改为 .as_ref() 引用发送、不取走 sender,通道在整个回合内保持
armed(回合结束仍由 coordinator 清空,不跨回合误用)。固化了该 bug 的
旧测试 approval_first_come_first_served 重写为回归用例
approval_channel_survives_multiple_tools_in_one_turn。
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
| 1 天前 |
| feat(codingplan): render rate_limit_windows, surface monthly exhaustion, fix ✗→× glyph
- Add RateLimitWindow type + rate_limit_windows field to StatusResponse
- /codingplan status iterates rate_limit_windows where show_enable==1:
- window_size_seconds > 5h → 本月用量已耗尽 + reset_at
- else → usage_status_desc + reset_at (single time source, no more divergence)
- Fall back to legacy current_usage path when new field empty (old server compat)
- Replace ✗ (U+2717) with × (U+00D7) in all i18n strings — macOS Terminal.app
default font lacks ✗, rendering as ☐ tofu; × is Latin-1, universal.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
| 9 天前 |
| Auto-commit at turn #57 (9 files changed)
| 8 天前 |
| Merge remote-tracking branch 'origin/release/v4.24.1' into release/v4.24.1
| 2 天前 |
| fix(prompt): 冻结会话级 system 提示 + 锁定 assistant 序列化确定性,稳住前缀缓存
Part 2,承接 read_file 历史冻结(86e73592)。线上 920 个"好轮→坏轮"逐字节
比对:system(第0条)占缓存塌缩 19.8%,assistant 历史占被改写消息 23.3%。
== A. system 提示逐轮改写(已修) ==
build_system_prompt 每轮用 live 输入重建:working_dir 在模型每次 cd
(tool/cd.rs、tool/bash.rs 直接写 working_dir)后变化、被插进 4 行
(SCOPE/CONFIG/Working directory);plan_mode 切换增删 PLAN MODE 段;
memory / layered-instructions 每轮重读磁盘。system 是 messages[0],变 1 字节
整条会话缓存归零。
修复:会话级冻结——build_system_prompt 首轮 assemble 后缓存,后续原样复用;
仅在显式契约边界失效重建(SetPlanMode / ClearConversation / ReloadConfig /
change_dir 即 /cd)。模型自己的 cd 工具不失效——最新 cwd 仍通过 cd/bash
工具结果到达模型,冻结 system 不致盲。hook 扩展仍每轮收集(行为不变),但冻结后
只在冷缓存(首建/失效后)被消费;刻意保持无条件收集,避免某构建路径(如
RefreshContextStats)在 SystemPromptHook 收集前抢先冻结。
== B. assistant 历史重新序列化(排查结论:openai 路径本就确定,无需改) ==
provider/openai.rs format_messages 复用存储的 tool_call.arguments 原始字符串
(不重新 stringify)、serde_json 键序确定、无任何按 recency 裁剪历史
reasoning_content 的逻辑。故 deepseek-v4(Include)下历史 assistant 序列化
本就逐字节稳定;23.3% 主要是 system/tool 断点后的下游位移。加测试锁死该不变量
防回归(有人重编码 args / 重排键 / 裁剪历史 reasoning)。
回归测试:
- agent/mod.rs::system_prompt_is_frozen_across_model_cwd_change
改 working_dir 后 system 逐字节不变;显式失效后重建并反映新 cwd。
- provider/openai.rs::format_messages_is_deterministic_and_prefix_stable
同输入两轮字节一致;会话增长后旧前缀消息序列化不变。
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
| 1 天前 |
| fix(semantic): php.scm uses function_definition (not function_declaration)
tree-sitter-php 0.24.2 grammar exposes:
- function_definition (top-level functions)
- method_declaration (class/interface methods)
- class_declaration
- interface_declaration
Previous query referenced function_declaration — not a valid node
name in this grammar. Result chain:
Query::new(grammar, php.scm) → Err(NodeType "function_declaration")
→ list_symbols_treesitter() → None
→ list_symbols() → None
→ test_list_symbols_php panicked on .unwrap()
Verified by cargo test -p atomcode-core --lib test_list_symbols_php:
test semantic::tests::test_list_symbols_php ... ok
Bonus: also added method_declaration capture so class methods (e.g.
add in the test's Calculator::add($a, $b)) appear in the symbol
list — without it the assertion names.contains(&"add") would fail
even after the function_definition fix.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
| 27 天前 |
| 统一 ATOMCODE_HOME 路径语义:设置 ATOMCODE_HOME 后不再多加 .atomcode 这一级
| 13 天前 |
| fix(claude): preserve extended-thinking blocks across turns
claude.rs::format_messages destructured AssistantWithToolCalls with ..
and never emitted thinking content blocks, so any model going through the
Anthropic-style provider with thinking enabled (Opus/Sonnet/Haiku, plus
Anthropic-compatible proxies serving deepseek-v4-pro etc.) hit
"The content[].thinking in the thinking mode must be passed back to the
API" 400 from the second turn onward. The SSE parser also wasn't
capturing the cryptographic signature attached to each thinking block.
Now ContentBlock parses signature_delta (and accepts both thinking
and text field names for thinking_delta to stay compatible with
non-spec-conforming proxies), the runner accumulates StreamEvent::ThinkingBlock
events into AssistantWithToolCalls.thinking_blocks, and format_messages
emits each block (with signature) as the first elements of parts,
matching Anthropic's required block ordering.
OpenAI/Ollama provider paths ignore the new field via .. / _:
destructuring, and old session JSON files load fine via `#[serde(default,
skip_serializing_if = "Vec::is_empty")]`, so non-thinking models and
existing histories see no behaviour change.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
| 24 天前 |
| !214 merge fix-web-fetch-tests into release/v4.24.2
[共创大赛] fix(web_fetch): 修正 approval() 端对端测试断言
Created-by: yeli12
Commit-by: Contributor
Merged-by: saulcy
Description: ## 问题背景
三个 approval() 测试的断言与函数实际行为不符:
| 测试 | 旧断言 | 实际行为 |
|-----|--------|---------|
| localhost_literal | AutoApprove | RequireApproval(127.0.0.1 不在 allowlist
) |
| file_scheme | AutoApprove | RequireApproval(file:// 无 host) |
| unknown_domain | AutoApprove | RequireApproval(example.com 不在 allowlist …
|
## 修复方案
更新测试名称和断言以正确反映 approval() 行为:对非白名单 URL 要求审批,`exec
ute()` 再做二次校验。
## 变更
- crates/atomcode-core/src/tool/web_fetch.rs
See merge request: atomgit_atomcode/atomcode!214 | 1 天前 |
| fix(tool-args): Windows 路径预转义仅对单反斜杠路径生效,修复多行 content 换行被毁
pre_escape_windows_paths_in_json 会对参数里每个字符串都跑。当 write_file 的
content 是多行脚本、里面又引用了已正确转义的 Windows 路径(如
r'C:\\Users\\Administrator\\Desktop\\文章.xlsx')时,looks_like_windows_path 在
整段 content 上命中,rewrite_windows_path_body 把整段里所有真实 \n 翻倍成字面
\n,文件落盘成一行坏掉的 Python(报告里的 813 bytes, 1 lines),python exit 1,
模型误判成换行编码问题后用 echo/Add-Content/powershell 反复折腾直到网关 403。
收紧触发条件:仅当盘符冒号后是单个反斜杠(X:\,会被 serde 误解码)才重写;
正斜杠 X:/ 和已转义的 X:\\(合法 JSON、解码正确)一律跳过。这样多行 content
里正确转义的路径不再触发整段重写,旁边的换行得以保留。
与已回退的 7771f66c 正交:那次用"body 含 \n/\r 即非路径"判据,会误杀
D:\readme.txt / D:\new 等目录名带 r/n 的纯路径而被回退;本次只看冒号后反斜杠
单/双,防回退 pin 测试 repair_tool_args_loose_windows_path_with_n_dir_name_still_rewrites
仍通过。新增回归测试覆盖截图场景。
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
| 1 天前 |
| 统一 ATOMCODE_HOME 路径语义:设置 ATOMCODE_HOME 后不再多加 .atomcode 这一级
| 13 天前 |
| feat: add /init, /compact, /background commands
- Background agent with minimal tool registry (read/write only)
- AtomicBool guard prevents duplicate background tasks
- Acquire/Release ordering for cross-thread flag
- /init scaffolds CLAUDE.md, /compact triggers conversation compression
- Full Config passed to background (provider needs it), tools restricted
Squashed from: 46fd50c, 4e5270d, 0f6ad02, cab5e0f
| 1 个月前 |
| 统一 ATOMCODE_HOME 路径语义:设置 ATOMCODE_HOME 后不再多加 .atomcode 这一级
| 13 天前 |
| feat(core): LiveSession 总线骨架与公共类型(Phase 1.5 阶段①)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
| 3 天前 |
| fix(i18n): address code review findings (Critical + Important + Minor)
Critical:
- C1: main.rs hardcoded Chinese → use i18n t(Msg::CmdNoActiveProvider)
- C2: provider_wizard API key format string → new Msg::ProviderStepApiKeyWithHint
- C3: "provider default" → new Msg::ProviderDefaultHint
- C4: "Language set to" → new Msg::LanguageSetTo in commands.rs & language_picker.rs
Important:
- I1: zh_cn ProviderStepType half-width → full-width parentheses
- I2: IssueRequiredField add space before {field}
- I3: FromStr adds zh_tw/zh_hk/繁體中文 aliases with tests
- I4: RwLock graceful degradation (unwrap_or instead of expect)
- I5: test_lock() extracted as pub #[cfg(test)] fn, used in welcome_wizard tests
Minor:
- M1: classify_env_locale comment on zh_TW → ZhCn fallback
- M2: commands.rs use import simplification
- M3: LanguagePicker unit tests added
- M5: Locale Default impl
- M6: eprintln save errors get TODO comments
- M7: ProviderMenu labels translated to Chinese
- M8: doc comments on t(), set_locale(), resolve_initial_locale()
Also: fix daemon api_config.rs missing language field.
| 24 天前 |
| add notification for ghostty
1. TerminalApp 枚举 — 新增 Ghostty
2. 检测逻辑 (detect_terminal_app) — 当 TERM_PROGRAM=ghostty 时识别为 Ghostty
3. 通知发送 (write_terminal_notification) — Ghostty 与 WezTerm 共用同一条分支,调用 write_osc777_notification()
发送 OSC 777
4. 标题栏范围 (build_turn_terminal_notification_text) — Ghostty 与 Kitty | WezTerm 一样,在 body 前附加工作目录名
5. macOS bundle ID — 添加 com.mitchellh.ghostty,用于 terminal-notifier 点击激活
6. 测试 — 补充了 Ghostty bundle ID 的断言
Signed-off-by: mixail <1fp54hola@mozmail.com> | 15 天前 |
| feat: add thinking cost tracking and pricing module
| 1 个月前 |
| fix(tool/bash): decode subprocess output via OEM CP fallback on Windows
User report from Windows + Chinese (CP936) ACP: running dir C:\... via
the Bash tool produces ������ mojibake for every Chinese label in the
command's output. ASCII parts (paths, <DIR>, volume serial) come
through cleanly; CJK bytes get replaced with U+FFFD REPLACEMENT CHARACTER.
Root cause: cmd.exe builtins (dir, type, chcp, ...) load their
localized output strings from cmd.exe's resource segment in the system
OEM code page (CP936 on Simplified Chinese, CP950 / CP932 / CP949 on
Traditional / Japanese / Korean), regardless of chcp or
SetConsoleOutputCP state — resource lookup happens before any console
CP applies, and when stdout is piped (not a console) there is no console
CP to apply anyway. Our four read paths in bash.rs (lines 673 / 695
for streaming chunks, lines 738-739 for final stdout / stderr buffers)
and the git status --porcelain snapshot reader at line 431 all called
String::from_utf8_lossy(), which silently rewrites the CP936 multi-byte
sequences as U+FFFD.
SetConsoleOutputCP(CP_UTF8) in atomcode-cli/src/main.rs sets the parent
process's console CP; it does not switch cmd.exe's resource-string
encoding, and the piped child has no console anyway. So the fix has to
be on the decode side.
Add process_utils::decode_subprocess_output(bytes) -> String:
1. Try strict UTF-8 first — modern cross-platform tools (git, cargo,
npm, the user's own shell scripts) emit UTF-8 even on Windows, and
we want them to round-trip unchanged.
2. If UTF-8 fails purely because the byte buffer ended mid-codepoint
(Utf8Error::error_len() == None), the valid prefix is real UTF-8 —
return from_utf8_lossy so only the truncated tail becomes one
U+FFFD. Critical for streaming chunks read in 64 KiB slices: the
next chunk replays the tail. Without this branch the chunk-boundary
case would mis-classify a valid-UTF-8 prefix as OEM CP and garble it.
3. On Windows with a hard UTF-8 failure, decode through the system
OEM code page resolved by GetOEMCP():
* 936 → encoding_rs::GB18030 (Simplified Chinese, GBK superset)
* 950 → encoding_rs::BIG5 (Traditional Chinese)
* 932 → encoding_rs::SHIFT_JIS (Japanese)
* 949 → encoding_rs::EUC_KR (Korean / UHC)
* other → from_utf8_lossy (best effort; ASCII parts still readable)
4. On non-Windows, fall back to from_utf8_lossy. POSIX subprocess
stdout is UTF-8-by-convention; guessing another encoding from LANG
would be the same kind of vote we already retired for cell widths
(see width::is_cjk_locale, commits 6a1d42e8 + 92f4bd42).
encoding_rs is already in atomcode-core's deps (used by tool/read.rs
for non-UTF-8 text files), so no new crate. The GetOEMCP() extern is
inline — windows-sys doesn't have the Win32_Globalization feature in
atomcode-core and adding it just for one symbol is overkill.
Wired into all four bash.rs decode sites plus snapshot_workspace_changes
(git status output — modern git usually emits UTF-8, but the helper is
free at runtime when the input is already valid UTF-8 so the cost is
zero and we get correctness on pathological repo names too).
Verified locally: 4 new process_utils unit tests pin the ASCII / valid
UTF-8 / truncated-tail / empty-input behaviour; the 98 existing bash tool
tests continue to pass.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
| 7 天前 |
| fix(upgrade): gate prepare_deferred_upgrade in distro-pm builds
Address review: the background deferred-upgrade path still downloaded and
staged a binary on startup in package-managed builds. Guard it like the
other entry points, add a feature-gated test, and note distro-pm behavior
in the upgrade-fn docs.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
| 2 天前 |
| 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 天前 |
| feat(bash): 拦截 ORM 迁移 schema 重置类破坏性命令
check_destructive_command 新增对迁移/数据库重置子命令的识别:
- 空格分隔:迁移触发词(-- / migrate / migration / db / database)后跟
fresh / refresh / reset,如 cargo run -- fresh、sea-orm-cli migrate fresh
- 冒号连接:migrate:fresh、db:reset(Laravel / Rails 风格)
破坏发生在应用二进制运行时,命令行不含 rm/drop,纯模式列表无法捕获;
token 级匹配避免误伤安全子命令(up / status / refresh-cache)。
附正例与反例测试。
skill.rs: load_skills_dir 改为 pub(crate) 以便 crate 内复用。
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
| 2 天前 |
| 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 个月前 |
| diag: ctrace points across the Ctrl+C / Esc cancellation flow
Cross-crate diagnostic trace targeting the "spinner stays spinning after
Ctrl+C / Esc" class of bugs. The chain has several hand-offs where any
single broken link kills the cancel signal:
TUI key handler → AgentCommand::Cancel via mpsc
AgentLoop outer recv → cancel_token.cancel()
AgentLoop inner recv → inner cancel_token.cancel()
TurnRunner.execute_single_tool → select! { tool / cancel } branch
Adds atomcode_core::trace (mirror of the existing atomcode_tuix::trace,
sharing the ATOMCODE_TUIX_LOG=/path env switch + append-mode so both
crates write to the same file without truncation races). ctrace!
macro = atomcode-core analogue of tuix_trace!, single atomic load
+ branch when the env var is unset so leaving the points in hot paths
is free in release.
Categories:
KEY — TUI key handler emits Cancel send_res + active spinner label
AGT — agent loop outer/inner cmd_rx pops + cancel_token.cancel calls
RNR — execute_single_tool tool-returned vs cancel-fired branch
Capture: $env:ATOMCODE_TUIX_LOG = "C:\tmp\atomcode.log" then
reproduce the hang, grep KEY/AGT/RNR to see exactly where the chain
broke. (web_search's TOOL category is already wired in tool/web_search.rs
via the kill_on_drop fix.)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
| 7 天前 |
| 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(session): 压缩后保住原始 prompt,/resume 不再开局就是 tool_call
bug: /resume 后滑到顶部看不见自己最初问的 prompt,直接是 list_directory /
bash / read_file 一串 tool 调用;session JSON 文件里也没有原始 prompt
的痕迹。
根因:hard_truncate_to_target (agent/mod.rs:3178) 找"last user
message"作为 sacred 锚点,但 agent 在 turn 过程中会以 Role::User 注入
3 种合成消息: [Additional context from user]: ...、 `Output limit
hit. ...、[Context was compressed. ...]`。这些合成消息让 last_user_idx
指向了某条注入而非真实原始 prompt,触发压缩时原始 prompt 在
drain(0..keep_from) 里被一并砍掉,落盘 JSON 也丢失。
修复(opencode 子集):
1. Message 加 synthetic: bool 字段。#[serde(default)] + skip-if-
false,旧 session.json 反序列化默认为 false,序列化时常见 false 不
写盘,无 bloat。新增 Message::synthetic_user() 构造器。
2. 3 个合成注入点改用新的 Conversation::add_synthetic_user_message,
该方法 merge 逻辑保留既有 synthetic 标(real + synthetic 文字 append
后不会被错误升级为 synthetic)。
3. hard_truncate_to_target sacred 集合从 {last_user} 扩展为
{first_real_user, last_real_user},两个 anchor 都 filter
!m.synthetic。first 保会话锚点供 /resume,last 保当前任务上下文。
单 prompt 场景下两者重合,compaction 宁可超 budget 也不丢 prompt
(tier 1/2 仍可降 token,tier 3 在这种场景退化为 no-op 是设计取舍)。
4. session.rs::auto_name_from_messages、event_loop::apply_session_messages
主信号改用 synthetic 字段,次信号保留 bracket-prefix 启发式作为旧
session 兜底,避免老 JSON /resume 标题退化。
参考:opencode 的 message-v2.ts synthetic part 字段 + replay 机制
是公认的"原始 prompt 保护"工程化做法;DeepSeek-TUI 只在 metadata.title
存截断版,不能恢复完整 prompt。我们抄了 opencode 的 synthetic 字段 +
双 anchor sacred,没抄 replay(那是单独的"压缩后给模型重新喂上下文"
机制,不在本 bug 范围)。
测试(12 个新):
- message.rs: 5 个 — 构造器 / serde 默认 false / 不序列化 false /
序列化 true / 反序列化兼容
- conversation/mod.rs: 3 个 — syn 注入标记 / syn 合并到 real 保 real /
syn 合并到 syn 保 syn
- agent/mod.rs: 3 个 — 复现 bug 场景验证原始 prompt 保留 / 多轮场景
验证 last real 跳过尾部 syn / 空 conv 不 panic
- event_loop session_naming tests 全过(legacy bracket fallback 还在)
跨 provider/render/test fixture 共 19 处 Message {} 字面量补全
synthetic: false(脚本批量,brace-aware,跳过 -> Message { 函数签名)。
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
| 10 天前 |