Files
codex/codex-rs/ext/history-notes/src/backend.rs
pmccrary-oai daa48072f4 Add history and notes tools for token-budget sessions (#39827)
## Why

Token-budget sessions need a way to recover prior conversation context and preserve working state across context-window transitions.

## What changed

- Add direct-model `history` tools for listing windows and items, reading items, and searching conversation contents.
- Add direct-model `notes` tools for listing, reading, searching, appending, and writing persistent notes.
- Route tool calls through the configured Codex backend with trusted session and agent context, bounded request arguments, and truncation-aware output handling.
- Expose the extension when `features.token_budget.use_history_notes_history` is enabled with an OpenAI provider and Codex backend authentication.

## Testing

- Cover tool registration, configuration changes, provider and authentication requirements, backend request context, encrypted output preservation, and request and response limits.

GitOrigin-RevId: 43b259f01014ba3f30803dd7cd5634942407a9bb
2026-08-21 01:24:52 +00:00

72 lines
2.2 KiB
Rust

use std::time::Duration;
use codex_api::ReqwestTransport;
use codex_client::HttpTransport;
use codex_client::RequestBody;
use codex_login::default_client::create_client;
use codex_model_provider::SharedModelProvider;
use http::Method;
use serde_json::Value;
use serde_json::json;
const HISTORY_NOTES_BACKEND_TIMEOUT: Duration = Duration::from_secs(35);
#[derive(Clone)]
pub(crate) struct HistoryNotesBackend {
provider: SharedModelProvider,
}
impl HistoryNotesBackend {
pub(crate) fn new(provider: SharedModelProvider) -> Self {
Self { provider }
}
pub(crate) async fn call(
&self,
path: &str,
session_id: &str,
current_agent_name: &str,
mut arguments: Value,
) -> Result<Value, String> {
let Some(arguments_object) = arguments.as_object_mut() else {
return Err("History tool arguments must be a JSON object".to_string());
};
arguments_object.insert(
"context".to_string(),
json!({
"session_id": session_id,
"current_agent_name": current_agent_name,
}),
);
let provider =
self.provider.api_provider().await.map_err(|error| {
format!("History backend provider could not be resolved: {error}")
})?;
let auth = self
.provider
.api_auth()
.await
.map_err(|error| format!("History backend auth could not be resolved: {error}"))?;
let mut request = provider.build_request(Method::POST, path);
request.body = Some(RequestBody::Json(arguments));
request.timeout = Some(HISTORY_NOTES_BACKEND_TIMEOUT);
let request = auth
.apply_auth(request)
.await
.map_err(|error| format!("History backend auth failed: {error}"))?;
let response = ReqwestTransport::from_http_client(create_client())
.execute(request)
.await
.map_err(|error| format!("History backend request failed: {error}"))?;
serde_json::from_slice(&response.body)
.map_err(|error| format!("History backend returned invalid JSON: {error}"))
}
}
#[cfg(test)]
#[path = "backend_tests.rs"]
mod tests;