让Direct Codex请求携带项目聊天历史

读取project.jsonl完整user assistant tool记录
新请求覆盖当前消息并保留ephemeral线程
This commit is contained in:
2026-09-02 18:00:18 +08:00
parent 609efe805c
commit 9e8b7d880e
2 changed files with 64 additions and 1 deletions
@@ -3149,6 +3149,23 @@ pub(crate) async fn direct_game_creator_codex_chat_at_with_optional_observer(
.map_err(|error| error.to_string())
}
pub(crate) fn build_direct_codex_history_prompt(
root: &std::path::Path,
client_turn_id: &str,
current_prompt: &str,
) -> Result<String, String> {
let conversation = read_local_conversation_for_session_at(root, None, None)?;
let current_message_id = format!("direct-codex:{client_turn_id}:user");
let mut lines = conversation
.messages
.iter()
.filter(|message| message.message_id.as_deref() != Some(current_message_id.as_str()))
.map(|message| format!("{}: {}", message.role, message.content))
.collect::<Vec<_>>();
lines.push(format!("user: {}", current_prompt.trim()));
Ok(lines.join("\n"))
}
/// Direct home-page chat never binds Codex to a user project. It gets a
/// fresh isolated read-only workspace and a stable in-process thread so a
/// normal conversation can continue without creating a project, assets, a
@@ -3234,6 +3251,41 @@ pub(in crate::agent) fn shutdown_game_creator_codex_app_servers_impl() -> Result
mod tests {
use super::*;
#[test]
fn direct_history_prompt_replays_all_project_messages_in_order() {
let root = tempfile::tempdir().expect("temp dir");
init_local_game_project_at(root.path(), "history-project", "history").expect("init");
for (role, content, message_id) in [
("user", "hello", "direct-codex:old:user"),
("assistant", "partial\nunexpected interrupt happened here", "partial-id"),
("tool", "file-read result", "tool-id"),
("user", "stored raw request", "direct-codex:new-turn:user"),
] {
append_local_conversation_message_for_session_idempotent_at(
root.path(),
None,
None,
LocalConversationMessage {
role: role.to_string(),
content: content.to_string(),
agent_id: None,
},
message_id,
)
.expect("append history");
}
let prompt = build_direct_codex_history_prompt(
root.path(),
"new-turn",
"new request",
)
.expect("build prompt");
assert_eq!(
prompt,
"user: hello\nassistant: partial\nunexpected interrupt happened here\ntool: file-read result\nuser: new request"
);
}
#[test]
fn direct_item_activities_are_closed_safe_categories() {
let allowed = [
@@ -4220,9 +4220,20 @@ pub(crate) async fn chat_with_game_creator_direct_codex(
return Err(error);
}
};
let replay_prompt = match crate::agent::codex_app_server::build_direct_codex_history_prompt(
root,
&turn_id,
&user_prompt,
) {
Ok(prompt) => prompt,
Err(error) => {
audit.finish(false);
return Err(error);
}
};
let reply = match run_direct_game_creator_turn_at_with_creation_type_and_emitter(
root,
&user_prompt,
&replay_prompt,
creation_type.as_deref(),
Some(&turn_emitter),
Some(&mut audit),