Files
codex/codex-rs/ext/git-attribution/src/lib.rs
jay ab816f3ca0 Add the git attribution extension (#34769)
## What changed

- Add a `codex-git-attribution` context contributor that resolves the workspace's `commit_attribution_enabled` setting and supplies matching commit and pull request instructions through World State.
- Cache resolved policy by authentication generation, recover from unauthorized responses, and fail disabled on timeouts or settings errors with deferred retries.
- Handle enabled, disabled, and legacy attribution fragments so policy changes replace stale instructions without duplicating attribution.

## Testing

- Cover World State transitions, policy resolution after unauthorized responses, and authentication changes during an in-flight settings request.

GitOrigin-RevId: 6f733e708b5c960ba5e90751110910a03d1ba2df
2026-07-22 15:19:15 +00:00

114 lines
4.3 KiB
Rust

mod policy;
mod world_state;
use std::sync::Arc;
use std::time::Instant;
use codex_extension_api::ContextContributor;
use codex_extension_api::ExtensionFuture;
use codex_extension_api::ExtensionRegistryBuilder;
use codex_extension_api::WorldStateContributionInput;
use codex_extension_api::WorldStateSectionContribution;
use codex_http_client::HttpClientFactory;
use codex_login::AuthManager;
use crate::policy::GitAttributionPolicy;
use crate::policy::GitAttributionRetry;
use crate::policy::POLICY_RETRY_DELAY;
use crate::policy::auth_generation;
use crate::policy::cached_attribution_policy;
use crate::policy::resolve_attribution_policy;
use crate::policy::retry_deferred;
use crate::world_state::git_attribution_world_state_section;
/// Contributes model instructions for agent-created git commits and pull requests.
#[derive(Clone)]
struct GitAttributionExtension {
auth_manager: Arc<AuthManager>,
base_url: String,
http_client_factory: HttpClientFactory,
}
impl ContextContributor for GitAttributionExtension {
fn contribute_world_state<'a>(
&'a self,
input: WorldStateContributionInput<'a>,
) -> ExtensionFuture<'a, Vec<WorldStateSectionContribution>> {
Box::pin(async move {
let enabled = loop {
let current_auth_generation = auth_generation(self.auth_manager.as_ref());
let policy = match cached_attribution_policy(
input.thread_store,
input.turn_store,
current_auth_generation,
) {
Some(policy) => policy,
None if retry_deferred(input.thread_store, current_auth_generation) => {
GitAttributionPolicy {
auth_generation: current_auth_generation,
enabled: false,
}
}
None => {
match resolve_attribution_policy(
&self.auth_manager,
&self.base_url,
&self.http_client_factory,
)
.await
{
Ok(Some(policy)) => {
input.thread_store.insert(policy.clone());
policy
}
Ok(None) => {
let policy = GitAttributionPolicy {
auth_generation: current_auth_generation,
enabled: false,
};
input.turn_store.insert(policy.clone());
policy
}
Err(_) => {
let auth_generation = auth_generation(self.auth_manager.as_ref());
if auth_generation == current_auth_generation {
input.thread_store.insert(GitAttributionRetry {
auth_generation,
retry_at: Instant::now() + POLICY_RETRY_DELAY,
});
}
GitAttributionPolicy {
auth_generation: current_auth_generation,
enabled: false,
}
}
}
}
};
if policy.auth_generation == auth_generation(self.auth_manager.as_ref()) {
break policy.enabled;
}
};
vec![git_attribution_world_state_section(enabled)]
})
}
}
/// Installs the git-attribution contributor into the extension registry.
pub fn install<C: Sync>(
registry: &mut ExtensionRegistryBuilder<C>,
auth_manager: Arc<AuthManager>,
base_url: String,
http_client_factory: HttpClientFactory,
) {
registry.prompt_contributor(Arc::new(GitAttributionExtension {
auth_manager,
base_url,
http_client_factory,
}));
}
#[cfg(test)]
#[path = "git_attribution_tests.rs"]
mod tests;