feat/AGC codex的工具调用 持久化处理 #282
Reference in New Issue
Block a user
Delete Branch "feat/agc-codex-native-tool-call-info"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
重构directproject的thread持久化, 保存response items用来恢复thread
修改了DirectProject的对话jsonl格式, 不兼容不迁移. (测试需要手动清理旧对话jsonl文件)
AGC显式写入user msg, 对codex 返回的user msg忽略
清理原来的上下文滑动窗口, 因为codex内部会自动compact
工具调用后重启,继续对话:

close #249
close #277
请求修改:
P1 用户消息未持久化。DirectProject 在 App.tsx 跳过浏览器写入,direct_runtime.rs 也删除了回合开始前的用户消息写入;当前仅在 rawResponseItem/completed 事件中保存 response item,该事件通常不包含本轮 user message。重启或新建 thread 后历史缺少用户提问,上下文会断裂。请在 turn/start 前显式追加 user item,并按 turn ID 做幂等。
P1 新 thread 恢复时通过 thread/inject_items 全量注入 project.jsonl,没有 token/字节上限或裁剪策略。长期对话可能超过 app-server/model 上下文限制,导致恢复或新回合失败。请增加按模型预算保留最新连续历史的策略,或先使用受支持的 compact 机制。
另外,PR 混入 UI 编辑器和文档改动,建议拆分以降低回归范围。
很遗憾没法直接获取压缩后的内容
审查 head sha:
86d74033c0已基于 base
524e2f0f4f逐文件检查本次变更,并运行 cargo check 及相关 Rust 单元测试(codex_app_server 46 passed,direct_runtime 持久化测试 passed)。结合本次提交的原始 response item 持久化、用户消息由 app-server 回传、以及 Codex app-server 自动 compact 设计,未发现达到明确成立标准的问题。结论:未发现明确问题。
审查 head sha:
86d74033c0结论:未发现明确问题。
请求修改:当前 head 仍有 3 个 P1 持久化问题,分别会导致内部上下文污染项目历史、同一 clientTurnId 重试重复执行,以及损坏尾行阻塞后续写入。请修复行内评论并补对应回归测试。
@@ -2115,3 +2122,3 @@request: LlmRunRequest,direct_history_root: Option<&std::path::Path>,direct_client_turn_id: Option<&str>,_direct_client_turn_id: Option<&str>,P1:
clientTurnId在生产路径已被改成未使用参数,且后续turn/start没有传clientUserMessageId;生产调用也没有在发起 turn 前幂等追加 user item。连接/响应在 Codex 已接受请求后丢失时,使用同一个 clientTurnId 重试会再次执行 turn、重复修改项目并可能重复计费。请恢复 turn 级幂等(发起前持久化并复用 user item,或使用等价的服务端去重),并补断线后重试测试。@@ -0,0 +124,4 @@.get("payload").cloned().ok_or_else(|| format!("DirectProject 历史记录缺少 payload:{}", path.display()))?;if is_direct_project_internal_context_item(&item) {P1:这里仍会把 Codex 0.147.0 产生的内部 contextual user item 当成项目用户消息。该版本的上下文构造使用
role: user,且internal_chat_message_metadata_passthrough为空;它不会命中前面的 role 或content_item_kinds过滤,随后会被追加、展示并在新 thread 中注入,污染对话历史。请按真实来源/元数据区分上下文 user item 与用户 turn,并补一个 0.147.0 形状的回归测试。@@ -0,0 +146,4 @@let _append_guard = lock.lock("DirectProject 历史追加写")?;if history_exists {if let Some(item_id) = item.get("id").and_then(Value::as_str) {if let Some(existing) = find_direct_project_history_item_by_id_at(&path, item_id)? {P1:这里在调用
append_jsonl_line_unlocked之前先扫描已有记录;如果文件末尾是无换行的半截 JSON 或损坏行,direct_project_history_item_from_line会直接返回解析错误,后续 append 根本不会执行到底层已有的尾行修复逻辑。因此一次中断写入会永久阻塞之后所有带 id 的 raw item。请先执行尾行修复,或让重复检查明确跳过最后一条不完整尾行,并补测试。审查结论:request changes。阻塞问题:apps/ai-game-creator-shell/src-tauri/src/agent/codex_app_server.rs:2138-2151 在 thread_for 创建并放入连接池后才读取/注入 DirectProject 历史;read_direct_project_history_items_at 或 thread/inject_items 失败时直接返回。CodexThreadLease 的 Drop 只递减 active_uses,不移除这个未完成初始化的 thread,下一次请求会以 thread_created=false 复用它并跳过历史注入,导致损坏/注入失败后的项目对话永久丢失(注入部分成功时还可能重复/错乱)。失败时应淘汰该 thread/连接并让重试重新创建并注入,补失败后重试回归测试。当前 PR 还与 master 存在冲突,请先 rebase/resolve。
更新: 改用AGC显式写入user msg, 对codex 返回的user msg忽略
请求修改(当前 head bfd6bb76cb0c2b4e4ed4f29f456fb829fd2fd920)。
DirectProject 新线程恢复时仍由 read_direct_project_history_items_at 读取完整 project.jsonl,再把全部 items 传给 thread/inject_items,没有按模型上下文预算裁剪或 compact 边界。对话增长后,恢复请求可能超过 app-server 或模型上下文限制,直接失败并再次触发上下文丢失;当前 CI 也仍在排队。
请增加按模型预算保留最新连续历史的策略,或使用受支持的 compact 机制,并补长历史恢复回归测试。失败清理和尾行修复已有覆盖,但不能替代注入总量上限。
@@ -2575,1 +2606,3 @@message.content = history_prompt;if self.inner.workspace_mode == CodexAppServerWorkspaceMode::DirectProject {if thread_created {let items = match read_direct_project_history_items_at(history_root) {这里直接读取完整历史并在下一段把全部 items 注入新 thread;随着 project.jsonl 增长,恢复会越过模型/app-server 上下文上限。请在 read 或 inject 前按 token/字节预算裁剪为最新连续历史,或先调用受支持的 compact,并测试超预算项目仍可恢复。
compact is triggered automatically by codex
发现 3 项需修复问题:
请修复并补充覆盖测试。
@@ -2575,1 +2606,3 @@message.content = history_prompt;if self.inner.workspace_mode == CodexAppServerWorkspaceMode::DirectProject {if thread_created {let items = match read_direct_project_history_items_at(history_root) {[P1] This history includes the current user item already appended above, while turn/start sends the same input again. Exclude the current item from injection.
@@ -0,0 +126,4 @@let mut line_end = combined.len();while let Some(newline) = combined[..line_end].iter().rposition(|byte| *byte == b'\n') {let line = &combined[newline + 1..line_end];if !line.is_empty() {[P1] Repairable unterminated final JSONL records must be ignored during reverse id scan; with a prior complete line this currently returns EOF before append repair.
@@ -5462,0 +5432,4 @@projectConversationWriteConfirmedRef.current !== directProjectPath) {try {const policyPaused = await queueProjectPolicyConfirmationIfNeeded([P2] Claim busy/active-turn ownership before this await; a second submit during policy I/O can clear the first turn state in its finally block.