mirror of
https://github.com/pchuan98/codex.git
synced 2026-07-01 00:31:56 +08:00
Import external agent sessions in background (#20284)
Summary: - Return from external agent import before session history import finishes - Run session import work in the background and emit the existing completion notification when it is done - Serialize session imports so duplicate requests do not create duplicate imported threads Verification: - cargo test -p codex-app-server external_agent_config_ - cargo test -p codex-external-agent-sessions - just fix -p codex-app-server - just fix -p codex-external-agent-sessions - git diff --check
This commit is contained in:
committed by
GitHub
Unverified
parent
7bcd4626c4
commit
c8abcbf925
@@ -94,6 +94,37 @@ pub fn prepare_pending_session_imports(
|
||||
Ok(pending_session_imports)
|
||||
}
|
||||
|
||||
pub fn prepare_validated_session_imports(
|
||||
codex_home: &Path,
|
||||
requested_sessions: Vec<ExternalAgentSessionMigration>,
|
||||
) -> Vec<PendingSessionImport> {
|
||||
requested_sessions
|
||||
.into_iter()
|
||||
.filter_map(|session| pending_session_import(codex_home, session))
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn pending_session_import(
|
||||
codex_home: &Path,
|
||||
session: ExternalAgentSessionMigration,
|
||||
) -> Option<PendingSessionImport> {
|
||||
let has_been_imported = match has_current_session_been_imported(codex_home, &session.path) {
|
||||
Ok(has_been_imported) => has_been_imported,
|
||||
Err(_) => return None,
|
||||
};
|
||||
if has_been_imported {
|
||||
return None;
|
||||
}
|
||||
let imported_session = match load_importable_session(&session.path) {
|
||||
Ok(Some(imported_session)) => imported_session,
|
||||
Ok(None) | Err(_) => return None,
|
||||
};
|
||||
Some(PendingSessionImport {
|
||||
source_path: session.path,
|
||||
session: imported_session,
|
||||
})
|
||||
}
|
||||
|
||||
fn load_importable_session(path: &Path) -> io::Result<Option<ImportedExternalAgentSession>> {
|
||||
let Some(imported_session) = load_session_for_import(path)? else {
|
||||
return Ok(None);
|
||||
|
||||
@@ -13,6 +13,8 @@ use std::path::PathBuf;
|
||||
|
||||
const NOTE_MAX_LEN: usize = 2_000;
|
||||
const TOOL_RESULT_MAX_LEN: usize = 4_000;
|
||||
const EXTERNAL_AGENT_TOOL_CALL_TAG: &str = "external_agent_tool_call";
|
||||
const EXTERNAL_AGENT_TOOL_RESULT_TAG: &str = "external_agent_tool_result";
|
||||
|
||||
pub struct SessionSummary {
|
||||
pub latest_timestamp: i64,
|
||||
@@ -252,7 +254,7 @@ fn tool_call_note(block: &JsonValue) -> String {
|
||||
.get("name")
|
||||
.and_then(JsonValue::as_str)
|
||||
.unwrap_or("unknown");
|
||||
let mut lines = vec![format!("[external tool call: {name}]")];
|
||||
let mut lines = vec![format!("[{EXTERNAL_AGENT_TOOL_CALL_TAG}: {name}]")];
|
||||
if let Some(input) = block.get("input").and_then(JsonValue::as_object) {
|
||||
if let Some(description) = input.get("description").and_then(JsonValue::as_str) {
|
||||
lines.push(format!("description: {description}"));
|
||||
@@ -279,20 +281,24 @@ fn tool_call_note(block: &JsonValue) -> String {
|
||||
truncate(&input.to_string(), NOTE_MAX_LEN)
|
||||
));
|
||||
}
|
||||
lines.push(format!("[/{EXTERNAL_AGENT_TOOL_CALL_TAG}]"));
|
||||
lines.join("\n")
|
||||
}
|
||||
|
||||
fn tool_result_note(block: &JsonValue) -> String {
|
||||
let label = if block.get("is_error").and_then(JsonValue::as_bool) == Some(true) {
|
||||
"[external tool result: error]"
|
||||
format!("[{EXTERNAL_AGENT_TOOL_RESULT_TAG}: error]")
|
||||
} else {
|
||||
"[external tool result]"
|
||||
format!("[{EXTERNAL_AGENT_TOOL_RESULT_TAG}]")
|
||||
};
|
||||
let text = tool_result_text(block.get("content"));
|
||||
if text.is_empty() {
|
||||
label.to_string()
|
||||
format!("{label}\n[/{EXTERNAL_AGENT_TOOL_RESULT_TAG}]")
|
||||
} else {
|
||||
format!("{label}\n{}", truncate(&text, TOOL_RESULT_MAX_LEN))
|
||||
format!(
|
||||
"{label}\n{}\n[/{EXTERNAL_AGENT_TOOL_RESULT_TAG}]",
|
||||
truncate(&text, TOOL_RESULT_MAX_LEN)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -314,3 +320,59 @@ fn parse_timestamp(timestamp: &str) -> Option<i64> {
|
||||
.ok()
|
||||
.map(|value| value.timestamp())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn converts_tool_use_blocks_to_bounded_external_agent_tags() {
|
||||
let block = serde_json::json!({
|
||||
"type": "tool_use",
|
||||
"name": "Bash",
|
||||
"input": {
|
||||
"description": "Check repo status",
|
||||
"command": "git status --short"
|
||||
}
|
||||
});
|
||||
|
||||
assert_eq!(
|
||||
tool_call_note(&block),
|
||||
"[external_agent_tool_call: Bash]\n\
|
||||
description: Check repo status\n\
|
||||
command: git status --short\n\
|
||||
[/external_agent_tool_call]"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn converts_tool_result_blocks_to_bounded_external_agent_tags() {
|
||||
let block = serde_json::json!({
|
||||
"type": "tool_result",
|
||||
"content": "codex-rs/external-agent-sessions/src/records.rs"
|
||||
});
|
||||
|
||||
assert_eq!(
|
||||
tool_result_note(&block),
|
||||
"[external_agent_tool_result]\n\
|
||||
codex-rs/external-agent-sessions/src/records.rs\n\
|
||||
[/external_agent_tool_result]"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn converts_error_tool_result_blocks_to_bounded_external_agent_tags() {
|
||||
let block = serde_json::json!({
|
||||
"type": "tool_result",
|
||||
"is_error": true,
|
||||
"content": "command failed"
|
||||
});
|
||||
|
||||
assert_eq!(
|
||||
tool_result_note(&block),
|
||||
"[external_agent_tool_result: error]\n\
|
||||
command failed\n\
|
||||
[/external_agent_tool_result]"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user