mirror of
https://github.com/pchuan98/codex.git
synced 2026-07-01 00:31:56 +08:00
## Why Revert #27910 to remove the newly introduced thread `recencyAt` persistence and API behavior from `main`. ## What changed This reverts commit `fac3158c2a783095768076489815f361fa9b0db4`, including the state migration, thread-store propagation, app-server API surface, generated schemas, and related tests. ## Validation Not run before opening; relying on CI for the initial fast signal.
204 lines
7.2 KiB
Rust
204 lines
7.2 KiB
Rust
use codex_app_server_protocol::McpToolCallResult;
|
|
use codex_app_server_protocol::ThreadItem;
|
|
use codex_app_server_protocol::Turn;
|
|
use serde_json::Value as JsonValue;
|
|
|
|
// Temporary bandaid for remote clients: thread/resume can include large MCP and
|
|
// image-generation payloads. Keep this response-only so persisted rollout
|
|
// history, model resume history, and other APIs stay unchanged.
|
|
const REDACTED_PAYLOAD: &str = "[redacted]";
|
|
const CHATGPT_REMOTE_CLIENT_NAMES: &[&str] =
|
|
&["codex_chatgpt_android_remote", "codex_chatgpt_ios_remote"];
|
|
|
|
pub(super) fn should_redact_thread_resume_payloads(client_name: Option<&str>) -> bool {
|
|
client_name.is_some_and(|client_name| CHATGPT_REMOTE_CLIENT_NAMES.contains(&client_name))
|
|
}
|
|
|
|
pub(super) fn redact_thread_resume_payloads(turns: &mut [Turn]) {
|
|
for turn in turns {
|
|
turn.items.retain_mut(|item| match item {
|
|
ThreadItem::McpToolCall {
|
|
arguments,
|
|
result,
|
|
error,
|
|
..
|
|
} => {
|
|
*arguments = JsonValue::String(REDACTED_PAYLOAD.to_string());
|
|
if result.is_some() {
|
|
*result = Some(Box::new(redacted_mcp_tool_call_result()));
|
|
}
|
|
if let Some(error) = error {
|
|
error.message = REDACTED_PAYLOAD.to_string();
|
|
}
|
|
true
|
|
}
|
|
ThreadItem::ImageGeneration { .. } => false,
|
|
_ => true,
|
|
});
|
|
}
|
|
}
|
|
|
|
fn redacted_mcp_tool_call_result() -> McpToolCallResult {
|
|
McpToolCallResult {
|
|
content: vec![serde_json::json!({
|
|
"type": "text",
|
|
"text": REDACTED_PAYLOAD,
|
|
})],
|
|
structured_content: None,
|
|
meta: None,
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
use codex_app_server_protocol::McpToolCallError;
|
|
use codex_app_server_protocol::McpToolCallStatus;
|
|
use codex_app_server_protocol::SessionSource;
|
|
use codex_app_server_protocol::Thread;
|
|
use codex_app_server_protocol::ThreadStatus;
|
|
use codex_app_server_protocol::TurnItemsView;
|
|
use codex_app_server_protocol::TurnStatus;
|
|
use codex_utils_absolute_path::test_support::PathBufExt;
|
|
use codex_utils_absolute_path::test_support::test_path_buf;
|
|
use pretty_assertions::assert_eq;
|
|
|
|
#[test]
|
|
fn redacts_mcp_success_result_and_removes_image_generation() {
|
|
let mut thread = test_thread(vec![
|
|
ThreadItem::AgentMessage {
|
|
id: "agent-1".to_string(),
|
|
text: "kept".to_string(),
|
|
phase: None,
|
|
memory_citation: None,
|
|
},
|
|
ThreadItem::McpToolCall {
|
|
id: "mcp-1".to_string(),
|
|
server: "docs".to_string(),
|
|
tool: "lookup".to_string(),
|
|
status: McpToolCallStatus::Completed,
|
|
arguments: serde_json::json!({"secret":"argument"}),
|
|
mcp_app_resource_uri: Some("ui://widget/lookup.html".to_string()),
|
|
plugin_id: Some("sample@test".to_string()),
|
|
result: Some(Box::new(McpToolCallResult {
|
|
content: vec![serde_json::json!({
|
|
"type": "text",
|
|
"text": "secret result"
|
|
})],
|
|
structured_content: Some(serde_json::json!({"secret":"structured"})),
|
|
meta: Some(serde_json::json!({"secret":"meta"})),
|
|
})),
|
|
error: None,
|
|
duration_ms: Some(8),
|
|
},
|
|
ThreadItem::ImageGeneration {
|
|
id: "ig-1".to_string(),
|
|
status: "completed".to_string(),
|
|
revised_prompt: Some("revised".to_string()),
|
|
result: "base64-result".to_string(),
|
|
saved_path: Some(test_path_buf("/tmp/ig-1.png").abs()),
|
|
},
|
|
]);
|
|
|
|
redact_thread_resume_payloads(&mut thread.turns);
|
|
|
|
assert_eq!(thread.turns[0].items.len(), 2);
|
|
assert_eq!(
|
|
thread.turns[0].items[0],
|
|
ThreadItem::AgentMessage {
|
|
id: "agent-1".to_string(),
|
|
text: "kept".to_string(),
|
|
phase: None,
|
|
memory_citation: None,
|
|
}
|
|
);
|
|
assert_eq!(
|
|
thread.turns[0].items[1],
|
|
ThreadItem::McpToolCall {
|
|
id: "mcp-1".to_string(),
|
|
server: "docs".to_string(),
|
|
tool: "lookup".to_string(),
|
|
status: McpToolCallStatus::Completed,
|
|
arguments: JsonValue::String(REDACTED_PAYLOAD.to_string()),
|
|
mcp_app_resource_uri: Some("ui://widget/lookup.html".to_string()),
|
|
plugin_id: Some("sample@test".to_string()),
|
|
result: Some(Box::new(redacted_mcp_tool_call_result())),
|
|
error: None,
|
|
duration_ms: Some(8),
|
|
}
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn redacts_mcp_error_message() {
|
|
let mut thread = test_thread(vec![ThreadItem::McpToolCall {
|
|
id: "mcp-1".to_string(),
|
|
server: "docs".to_string(),
|
|
tool: "lookup".to_string(),
|
|
status: McpToolCallStatus::Failed,
|
|
arguments: serde_json::json!({"secret":"argument"}),
|
|
mcp_app_resource_uri: None,
|
|
plugin_id: None,
|
|
result: None,
|
|
error: Some(McpToolCallError {
|
|
message: "secret error".to_string(),
|
|
}),
|
|
duration_ms: Some(8),
|
|
}]);
|
|
|
|
redact_thread_resume_payloads(&mut thread.turns);
|
|
|
|
assert_eq!(
|
|
thread.turns[0].items[0],
|
|
ThreadItem::McpToolCall {
|
|
id: "mcp-1".to_string(),
|
|
server: "docs".to_string(),
|
|
tool: "lookup".to_string(),
|
|
status: McpToolCallStatus::Failed,
|
|
arguments: JsonValue::String(REDACTED_PAYLOAD.to_string()),
|
|
mcp_app_resource_uri: None,
|
|
plugin_id: None,
|
|
result: None,
|
|
error: Some(McpToolCallError {
|
|
message: REDACTED_PAYLOAD.to_string(),
|
|
}),
|
|
duration_ms: Some(8),
|
|
}
|
|
);
|
|
}
|
|
|
|
fn test_thread(items: Vec<ThreadItem>) -> Thread {
|
|
Thread {
|
|
id: "thread-1".to_string(),
|
|
session_id: "session-1".to_string(),
|
|
forked_from_id: None,
|
|
parent_thread_id: None,
|
|
preview: "preview".to_string(),
|
|
ephemeral: false,
|
|
model_provider: "mock_provider".to_string(),
|
|
created_at: 0,
|
|
updated_at: 0,
|
|
status: ThreadStatus::Idle,
|
|
path: None,
|
|
cwd: test_path_buf("/tmp").abs(),
|
|
cli_version: "0.0.0".to_string(),
|
|
source: SessionSource::Cli,
|
|
thread_source: None,
|
|
agent_nickname: None,
|
|
agent_role: None,
|
|
git_info: None,
|
|
name: None,
|
|
turns: vec![Turn {
|
|
id: "turn-1".to_string(),
|
|
items,
|
|
items_view: TurnItemsView::Full,
|
|
status: TurnStatus::Completed,
|
|
error: None,
|
|
started_at: None,
|
|
completed_at: None,
|
|
duration_ms: None,
|
|
}],
|
|
}
|
|
}
|
|
}
|