文件最后提交记录最后更新时间
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. Messagesynthetic: 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 天前
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(provider): unwrap JSON envelope when surfacing upstream HTTP errors Gateways wrap the real error message in JSON shapes (AtomCode {"detail":{"message":...}}, OpenAI {"error":{"message":...}}, FastAPI {"detail":"..."}). Showing the raw body buried the message in JSON noise — extract the known shapes and render just the message. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> 16 天前
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(provider): regenerate signature per retry — eliminates SIG_REPLAY/SIG_STALE 403 on atomgit gateway When the inner reqwest-level retry (send_with_retry) replayed the same request bytes on transient 5xx / network errors, the server's signing middleware rejected the second attempt: either SIG_REPLAY (server's nonce cache held the first attempt's nonce) or SIG_STALE (cumulative backoff pushed ts past the 300s freshness window). Server returned 403 with body "请升级到最新版 AtomCode 后再试" for both cases, which atomcode surfaced verbatim — users saw it, ran /upgrade, found themselves already on latest, then ran /login and the issue "resolved" (the new login triggered a fresh chat_stream which re-signed naturally on first attempt). Fix: add send_with_retry_resign that rebuilds the request via a factory closure on each attempt. The atomgit-gateway caller in openai.rs uses it; the factory regenerates fresh nonce+ts+signature per attempt via build_codingplan_headers. Non-atomgit providers keep using send_with_retry unchanged. Trade-off accepted: on 5xx where backend already started processing the first attempt, a re-signed retry will be processed again (potentially double-billing). The previous accidental SIG_REPLAY rejection acted as primitive idempotency. Long-term fix is an Idempotency-Key header; for now the user-visible benefit (no more false-positive 'upgrade' message, transparent recovery from network blips) outweighs the rare double-process risk. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> 9 天前