| feat(datalog): record session_id; unify session id generation
Tag datalog with the same session id that rides the request header and
telemetry, and collapse the two session-id generators into one.
- DatalogWriter gains a session_id (set via the agent's SetSessionId
handler). It's written into each turn's .md env header (session=…)
and the _requests.jsonl dump.
- log_llm_request (the llm/*.json writer) takes the session id from the
provider (new LlmProvider::session_id() getter) and records it, so the
per-call json — the one used for cache-hit analysis — is attributable to
a session.
- Agent::new no longer mints its own raw uuid; it reuses the single
generator SessionId::new() as a bootstrap value (the agent is built
before the Session exists, so the UI's SetSessionId still supplies the
real session-file id). Net: one generation site, and header / datalog /
telemetry / hooks all share the session-file id at runtime.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
| 2 天前 |
| feat(daemon): update daemon and vscode extension
Co-Authored-By: AtomCode (deepseek-v4-flash) <noreply@atomgit.com
| 16 天前 |
| 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 天前 |
| feat(datalog): record session_id; unify session id generation
Tag datalog with the same session id that rides the request header and
telemetry, and collapse the two session-id generators into one.
- DatalogWriter gains a session_id (set via the agent's SetSessionId
handler). It's written into each turn's .md env header (session=…)
and the _requests.jsonl dump.
- log_llm_request (the llm/*.json writer) takes the session id from the
provider (new LlmProvider::session_id() getter) and records it, so the
per-call json — the one used for cache-hit analysis — is attributable to
a session.
- Agent::new no longer mints its own raw uuid; it reuses the single
generator SessionId::new() as a bootstrap value (the agent is built
before the Session exists, so the UI's SetSessionId still supplies the
real session-file id). Net: one generation site, and header / datalog /
telemetry / hooks all share the session-file id at runtime.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
| 2 天前 |
| fix(core): cross-batch tool-call loop guard + JSON-canonical dedup
Weak models (deepseek-v4-flash in particular) get stuck on the same
(name, args) pair across many sequential batches with no progress
between them — the 22-identical-Bash(cargo check) screenshot symptom.
The existing in-batch is_dup gate only catches duplicates within one
assistant message, so cross-turn loops slipped through and burned
turn budget.
Two layers added:
1. turn/loop_guard.rs — new module. Per-turn rolling window keyed on
(name, normalised_args) with two trigger thresholds:
* THRESHOLD = 3: identical (name, args, output, success) repeats
block on the 3rd attempt. Catches deterministic loops where
output is byte-stable.
* HARD_THRESHOLD = 6: identical (name, args) regardless of
output drift. Catches the cargo/test/lint polling case where
warning order, paths, or cache state make output vary slightly
even though the model is clearly stuck. The previous deletion
commit 9339cf1 warned this could re-introduce false
positives — addressed via:
- args-must-match-exactly (no token-streak fuzziness, which
was the bit that wrongly caught ssh ls/cat/sshpass batches),
- intervening successful state-changing tool with a NEW key
clears the window (edit/write/create/search_replace count
as progress; spam-edit of the same key still triggers).
The agent clears the window at every user-message boundary.
2. runner.rs::normalize_tool_args — JSON-canonicalise both the
in-batch is_dup key and the cross-batch loop_guard key. Streaming
models routinely re-emit the same call with whitespace / key-order
variation; serde_json::Value (BTreeMap-backed without the
preserve_order feature) yields identical canonical strings for
{"a":1,"b":2} and {"b":2, "a":1}. Non-JSON args fall through
unchanged.
Plumbing:
* LoopGuardState is a field on TurnRunner (5 constructor sites
updated: agent/mod.rs, agent/background.rs, agent/sub_agent.rs,
daemon/main.rs, turn/tests.rs × 2).
* agent::DisciplineState::recent_calls (dead-code Vec, only
cleared never written) deleted; replaced by
turn_runner.loop_guard.clear() at the same per-user-message
reset point.
Tests:
* 11 unit tests in loop_guard.rs covering: 3rd-identical block,
rotating cargo-check filters (false-positive pin from 9339cf1),
ssh ls/cat/sshpass batch (other 9339cf1 false-positive),
output-drift hard cap, polling below cap, intervening state
change reset, repeated state-changing keys still trigger,
clear() resets, JSON-whitespace variants collapse cross-batch,
name-collision separator works.
* 5 normalize_tool_args tests in runner.rs (whitespace, key order,
nested objects, semantically distinct args, non-JSON pass-through).
* 1 integration test through the actual run_with_filter dispatch
loop — 3 sequential run() calls with the same MockProvider
produce 1st/2nd execute, 3rd blocked with a [Loop guard]
synthetic ToolResult.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
| 27 天前 |
| fix(core): cross-batch tool-call loop guard + JSON-canonical dedup
Weak models (deepseek-v4-flash in particular) get stuck on the same
(name, args) pair across many sequential batches with no progress
between them — the 22-identical-Bash(cargo check) screenshot symptom.
The existing in-batch is_dup gate only catches duplicates within one
assistant message, so cross-turn loops slipped through and burned
turn budget.
Two layers added:
1. turn/loop_guard.rs — new module. Per-turn rolling window keyed on
(name, normalised_args) with two trigger thresholds:
* THRESHOLD = 3: identical (name, args, output, success) repeats
block on the 3rd attempt. Catches deterministic loops where
output is byte-stable.
* HARD_THRESHOLD = 6: identical (name, args) regardless of
output drift. Catches the cargo/test/lint polling case where
warning order, paths, or cache state make output vary slightly
even though the model is clearly stuck. The previous deletion
commit 9339cf1 warned this could re-introduce false
positives — addressed via:
- args-must-match-exactly (no token-streak fuzziness, which
was the bit that wrongly caught ssh ls/cat/sshpass batches),
- intervening successful state-changing tool with a NEW key
clears the window (edit/write/create/search_replace count
as progress; spam-edit of the same key still triggers).
The agent clears the window at every user-message boundary.
2. runner.rs::normalize_tool_args — JSON-canonicalise both the
in-batch is_dup key and the cross-batch loop_guard key. Streaming
models routinely re-emit the same call with whitespace / key-order
variation; serde_json::Value (BTreeMap-backed without the
preserve_order feature) yields identical canonical strings for
{"a":1,"b":2} and {"b":2, "a":1}. Non-JSON args fall through
unchanged.
Plumbing:
* LoopGuardState is a field on TurnRunner (5 constructor sites
updated: agent/mod.rs, agent/background.rs, agent/sub_agent.rs,
daemon/main.rs, turn/tests.rs × 2).
* agent::DisciplineState::recent_calls (dead-code Vec, only
cleared never written) deleted; replaced by
turn_runner.loop_guard.clear() at the same per-user-message
reset point.
Tests:
* 11 unit tests in loop_guard.rs covering: 3rd-identical block,
rotating cargo-check filters (false-positive pin from 9339cf1),
ssh ls/cat/sshpass batch (other 9339cf1 false-positive),
output-drift hard cap, polling below cap, intervening state
change reset, repeated state-changing keys still trigger,
clear() resets, JSON-whitespace variants collapse cross-batch,
name-collision separator works.
* 5 normalize_tool_args tests in runner.rs (whitespace, key order,
nested objects, semantically distinct args, non-JSON pass-through).
* 1 integration test through the actual run_with_filter dispatch
loop — 3 sequential run() calls with the same MockProvider
produce 1st/2nd execute, 3rd blocked with a [Loop guard]
synthetic ToolResult.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
| 27 天前 |
| feat: add --dangerously-skip-permissions / -y flag
- Add CLI flag --dangerously-skip-permissions (alias -y) to auto-approve all tool calls without permission prompts
- InteractivePermissionDecider honors the flag in decide() and will_auto_approve()
- OpenAtomcode telemetry event includes dangerously_skip_permissions field
- TUI status bar shows SKIP badge on the right side (separate from PLAN)
- TUI scrollback warning banner on startup
- Headless mode stderr warning
- Add i18n messages (en/zh) for bypass warnings and badge
- Add comprehensive unit tests for permission logic and status bar rendering
| 5 天前 |
| feat(datalog): record session_id; unify session id generation
Tag datalog with the same session id that rides the request header and
telemetry, and collapse the two session-id generators into one.
- DatalogWriter gains a session_id (set via the agent's SetSessionId
handler). It's written into each turn's .md env header (session=…)
and the _requests.jsonl dump.
- log_llm_request (the llm/*.json writer) takes the session id from the
provider (new LlmProvider::session_id() getter) and records it, so the
per-call json — the one used for cache-hit analysis — is attributable to
a session.
- Agent::new no longer mints its own raw uuid; it reuses the single
generator SessionId::new() as a bootstrap value (the agent is built
before the Session exists, so the UI's SetSessionId still supplies the
real session-file id). Net: one generation site, and header / datalog /
telemetry / hooks all share the session-file id at runtime.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
| 2 天前 |
| 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 天前 |