清理 DirectProject 原始历史写入

取消 Rust 预写用户消息,改为只保存 Codex 原始回传

过滤技能、权限和环境上下文内部 item

增加内部上下文过滤回归测试
This commit is contained in:
2026-09-05 10:51:06 +08:00
parent b0a1ee0de2
commit ad3d04f51a
2 changed files with 59 additions and 13 deletions
@@ -2149,11 +2149,6 @@ impl CodexAppServerConnection {
.await
.map_err(platform_llm::LlmError::Transport)?;
}
append_direct_project_history_item_at(
history_root,
&direct_project_user_message_item(current_prompt),
)
.map_err(platform_llm::LlmError::InvalidRequest)?;
}
let prompt = if self.inner.workspace_mode.uses_direct_conversation() {
direct_codex_user_prompt(&request)
@@ -9,6 +9,28 @@ use std::io::{BufRead, BufReader, Read, Seek, SeekFrom};
use std::path::{Path, PathBuf};
const DIRECT_PROJECT_HISTORY_RECORD_TYPE: &str = "response_item";
const DIRECT_PROJECT_INTERNAL_CONTEXT_KINDS: &[&str] = &[
"host_skills.instructions",
"permissions.instructions",
"environments.environment_context",
];
pub(crate) fn is_direct_project_internal_context_item(item: &Value) -> bool {
if matches!(
item.get("role").and_then(Value::as_str),
Some("developer" | "system")
) {
return true;
}
item.pointer("/internal_chat_message_metadata_passthrough/content_item_kinds")
.and_then(Value::as_array)
.is_some_and(|kinds| {
kinds
.iter()
.filter_map(Value::as_str)
.any(|kind| DIRECT_PROJECT_INTERNAL_CONTEXT_KINDS.contains(&kind))
})
}
fn history_path(root: &Path) -> PathBuf {
root.join(".agent/conversations/project.jsonl")
@@ -113,6 +135,9 @@ pub(crate) fn append_direct_project_history_item_at(
item: &Value,
) -> Result<(), String> {
enforce_project_permission_policy(root, "conversation.write")?;
if is_direct_project_internal_context_item(item) {
return Ok(());
}
let _project_lock = crate::project::acquire_project_write_lock(root, "conversation.write")?;
let path = history_path(root);
prepare_game_creator_private_path_for_read(&path, false, "DirectProject 历史")?;
@@ -154,14 +179,6 @@ pub(crate) fn direct_project_local_message_item(
Ok(item)
}
pub(crate) fn direct_project_user_message_item(prompt: &str) -> Value {
serde_json::json!({
"type": "message",
"role": "user",
"content": [{"type": "input_text", "text": prompt}],
})
}
pub(crate) fn read_direct_project_history_items_at(root: &Path) -> Result<Vec<Value>, String> {
let path = history_path(root);
if !prepare_game_creator_private_path_for_read(&path, false, "DirectProject 历史")? {
@@ -204,6 +221,9 @@ pub(crate) fn read_direct_project_history_items_at(root: &Path) -> Result<Vec<Va
.get("payload")
.cloned()
.ok_or_else(|| format!("DirectProject 历史记录缺少 payload{}", path.display()))?;
if is_direct_project_internal_context_item(&item) {
continue;
}
items.push(item);
}
Ok(items)
@@ -249,3 +269,34 @@ pub(crate) fn read_direct_project_chat_history_at(
messages,
})
}
#[cfg(test)]
mod tests {
use super::is_direct_project_internal_context_item;
use serde_json::json;
#[test]
fn filters_host_context_but_keeps_real_user_items() {
assert!(is_direct_project_internal_context_item(&json!({
"type": "message",
"role": "developer",
"content": [{"type": "input_text", "text": "<skills_instructions>"}]
})));
assert!(is_direct_project_internal_context_item(&json!({
"type": "message",
"role": "user",
"content": [{"type": "input_text", "text": "<environment_context>"}],
"internal_chat_message_metadata_passthrough": {
"content_item_kinds": ["environments.environment_context"]
}
})));
assert!(!is_direct_project_internal_context_item(&json!({
"type": "message",
"role": "user",
"content": [{"type": "input_text", "text": "ls ui"}],
"internal_chat_message_metadata_passthrough": {
"content_item_kinds": ["user.text"]
}
})));
}
}