mirror of
https://github.com/openai/codex.git
synced 2026-10-31 19:28:49 +00:00
## Why Loaded transcripts reduce several tool calls and file changes to basic status summaries, losing details available in live history cells. ## What changed - Reuse rich history cells for completed commands, MCP calls, patches, agent activity, image generation, and notices. - Add dynamic tool cells with bounded output previews while retaining arguments and full output in detailed and raw views. - Preserve last-known statuses for incomplete calls, unavailable results, and declined or failed operations. Keep legacy command text and terminal interaction output when rich reconstruction is unavailable. - Remove redundant move annotations from patch diffs and retain patch activity IDs. - Filter untrusted control characters before emitting terminal scrollback, while preserving semantic hyperlinks. ## Testing Add unit and snapshot coverage for compact and detailed tool rendering, status fallbacks, retained output, patch restoration, and control-character filtering with and without hyperlinks. GitOrigin-RevId: 73d754262768cf6d91f3ea4040116bf93cf87784
186 lines
7.1 KiB
Rust
186 lines
7.1 KiB
Rust
//! Narrow conversion helpers for approval-related app-server payloads.
|
|
//!
|
|
//! The TUI mostly keeps app-server approval types intact. These helpers cover
|
|
//! the remaining cases where the UI consumes a private file-change display
|
|
//! model or needs to translate a granted permission response for outbound
|
|
//! submission.
|
|
|
|
use crate::diff_model::FileChange;
|
|
use codex_app_server_protocol::AdditionalNetworkPermissions;
|
|
use codex_app_server_protocol::FileUpdateChange;
|
|
use codex_app_server_protocol::GrantedPermissionProfile;
|
|
use codex_app_server_protocol::PatchChangeKind;
|
|
use codex_protocol::request_permissions::RequestPermissionProfile as CoreRequestPermissionProfile;
|
|
use std::collections::HashMap;
|
|
use std::path::PathBuf;
|
|
|
|
pub(crate) fn granted_permission_profile_from_request(
|
|
value: CoreRequestPermissionProfile,
|
|
) -> GrantedPermissionProfile {
|
|
GrantedPermissionProfile {
|
|
network: value.network.map(|network| AdditionalNetworkPermissions {
|
|
enabled: network.enabled,
|
|
}),
|
|
file_system: value.file_system.map(Into::into),
|
|
}
|
|
}
|
|
|
|
pub(crate) fn file_update_changes_to_display(
|
|
changes: Vec<FileUpdateChange>,
|
|
) -> HashMap<PathBuf, FileChange> {
|
|
changes
|
|
.into_iter()
|
|
.map(|mut change| {
|
|
let path = PathBuf::from(change.path);
|
|
let file_change = match change.kind {
|
|
PatchChangeKind::Add => FileChange::Add {
|
|
content: change.diff,
|
|
},
|
|
PatchChangeKind::Delete => FileChange::Delete {
|
|
content: change.diff,
|
|
},
|
|
PatchChangeKind::Update { move_path } => {
|
|
if let Some(path) = &move_path
|
|
&& let Some(diff) = change
|
|
.diff
|
|
.strip_suffix(&format!("\n\nMoved to: {}", path.display()))
|
|
{
|
|
change.diff.truncate(diff.len());
|
|
}
|
|
FileChange::Update {
|
|
unified_diff: change.diff,
|
|
move_path,
|
|
}
|
|
}
|
|
};
|
|
(path, file_change)
|
|
})
|
|
.collect()
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::file_update_changes_to_display;
|
|
use super::granted_permission_profile_from_request;
|
|
use crate::diff_model::FileChange;
|
|
use codex_app_server_protocol::AdditionalFileSystemPermissions;
|
|
use codex_app_server_protocol::AdditionalNetworkPermissions;
|
|
use codex_app_server_protocol::FileSystemAccessMode;
|
|
use codex_app_server_protocol::FileSystemPath;
|
|
use codex_app_server_protocol::FileSystemSandboxEntry;
|
|
use codex_app_server_protocol::FileSystemSpecialPath;
|
|
use codex_app_server_protocol::FileUpdateChange;
|
|
use codex_app_server_protocol::GrantedPermissionProfile;
|
|
use codex_app_server_protocol::PatchChangeKind;
|
|
use codex_app_server_protocol::RequestPermissionProfile;
|
|
use codex_protocol::request_permissions::RequestPermissionProfile as CoreRequestPermissionProfile;
|
|
use codex_utils_absolute_path::AbsolutePathBuf;
|
|
use pretty_assertions::assert_eq;
|
|
use std::collections::HashMap;
|
|
use std::path::PathBuf;
|
|
|
|
fn absolute_path(path: &str) -> AbsolutePathBuf {
|
|
AbsolutePathBuf::try_from(PathBuf::from(path)).expect("path must be absolute")
|
|
}
|
|
|
|
#[test]
|
|
fn converts_file_update_changes_to_display() {
|
|
assert_eq!(
|
|
file_update_changes_to_display(vec![FileUpdateChange {
|
|
path: "foo.txt".to_string(),
|
|
kind: PatchChangeKind::Add,
|
|
diff: "hello\n".to_string(),
|
|
}]),
|
|
HashMap::from([(
|
|
PathBuf::from("foo.txt"),
|
|
FileChange::Add {
|
|
content: "hello\n".to_string(),
|
|
},
|
|
)])
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn converts_request_permissions_into_granted_permissions() {
|
|
let request = RequestPermissionProfile {
|
|
network: Some(AdditionalNetworkPermissions {
|
|
enabled: Some(true),
|
|
}),
|
|
file_system: Some(AdditionalFileSystemPermissions {
|
|
read: Some(vec![absolute_path("/tmp/read-only").into()]),
|
|
write: Some(vec![absolute_path("/tmp/write").into()]),
|
|
glob_scan_max_depth: None,
|
|
entries: None,
|
|
}),
|
|
};
|
|
let request = CoreRequestPermissionProfile::try_from(request)
|
|
.expect("API paths should convert to native paths");
|
|
|
|
assert_eq!(
|
|
granted_permission_profile_from_request(request),
|
|
GrantedPermissionProfile {
|
|
network: Some(AdditionalNetworkPermissions {
|
|
enabled: Some(true),
|
|
}),
|
|
file_system: Some(AdditionalFileSystemPermissions {
|
|
read: Some(vec![absolute_path("/tmp/read-only").into()]),
|
|
write: Some(vec![absolute_path("/tmp/write").into()]),
|
|
glob_scan_max_depth: None,
|
|
entries: Some(vec![
|
|
FileSystemSandboxEntry {
|
|
path: FileSystemPath::Path {
|
|
path: absolute_path("/tmp/read-only").into(),
|
|
},
|
|
access: FileSystemAccessMode::Read,
|
|
},
|
|
FileSystemSandboxEntry {
|
|
path: FileSystemPath::Path {
|
|
path: absolute_path("/tmp/write").into(),
|
|
},
|
|
access: FileSystemAccessMode::Write,
|
|
},
|
|
]),
|
|
}),
|
|
}
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn converts_request_permissions_into_canonical_granted_permissions() {
|
|
let request = RequestPermissionProfile {
|
|
network: None,
|
|
file_system: Some(AdditionalFileSystemPermissions {
|
|
read: None,
|
|
write: None,
|
|
glob_scan_max_depth: None,
|
|
entries: Some(vec![FileSystemSandboxEntry {
|
|
path: FileSystemPath::Special {
|
|
value: FileSystemSpecialPath::Root,
|
|
},
|
|
access: FileSystemAccessMode::Write,
|
|
}]),
|
|
}),
|
|
};
|
|
let request = CoreRequestPermissionProfile::try_from(request)
|
|
.expect("API paths should convert to native paths");
|
|
|
|
assert_eq!(
|
|
granted_permission_profile_from_request(request),
|
|
GrantedPermissionProfile {
|
|
network: None,
|
|
file_system: Some(AdditionalFileSystemPermissions {
|
|
read: None,
|
|
write: None,
|
|
glob_scan_max_depth: None,
|
|
entries: Some(vec![FileSystemSandboxEntry {
|
|
path: FileSystemPath::Special {
|
|
value: FileSystemSpecialPath::Root,
|
|
},
|
|
access: FileSystemAccessMode::Write,
|
|
}]),
|
|
}),
|
|
}
|
|
);
|
|
}
|
|
}
|