Enable git attribution across Codex entry points (#34819)

## What changed

- Install the git attribution extension in the app server, MCP server, and `codex debug prompt-input` so authenticated workspace policy controls the commit and pull request attribution instructions sent to the model.
- Resolve attribution settings from the process-level ChatGPT base URL, independent of per-thread or per-tool configuration overrides.
- Treat git attribution as contextual developer content when mapping model events.

## Testing

- Cover policy fetch retries, workspace switches, rollbacks, cold thread resumes with legacy instructions, MCP tool calls, and prompt-debug output.

GitOrigin-RevId: 57d182c432c20d7c1c6c429057b6163c9f32088d
This commit is contained in:
jay
2026-07-22 20:23:35 +00:00
committed by copyberry
parent 4ebd976312
commit 88eb3a2b8a
21 changed files with 600 additions and 166 deletions
+57
View File
@@ -1,7 +1,13 @@
use std::path::Path;
use std::sync::Arc;
use std::sync::atomic::AtomicUsize;
use std::sync::atomic::Ordering;
use anyhow::Context;
use anyhow::Result;
use app_test_support::ChatGptAuthFixture;
use app_test_support::write_chatgpt_auth;
use codex_config::types::AuthCredentialsStoreMode;
use codex_login::CLIENT_ID;
use codex_login::REVOKE_TOKEN_URL_OVERRIDE_ENV_VAR;
use predicates::str::contains;
@@ -12,6 +18,7 @@ use tempfile::TempDir;
use wiremock::Mock;
use wiremock::MockServer;
use wiremock::ResponseTemplate;
use wiremock::matchers::header;
use wiremock::matchers::method;
use wiremock::matchers::path;
@@ -74,6 +81,56 @@ fn login_with_access_token_rejects_invalid_jwt() -> Result<()> {
Ok(())
}
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn debug_prompt_input_follows_authenticated_attribution_setting() -> Result<()> {
let server = MockServer::start().await;
let request_count = Arc::new(AtomicUsize::new(0));
Mock::given(method("GET"))
.and(path("/backend-api/wham/settings/user"))
.and(header("chatgpt-account-id", "workspace-123"))
.respond_with(move |_request: &wiremock::Request| {
ResponseTemplate::new(200).set_body_json(json!({
"commit_attribution_enabled": request_count.fetch_add(1, Ordering::SeqCst) == 0,
}))
})
.expect(2)
.mount(&server)
.await;
let codex_home = TempDir::new()?;
std::fs::write(
codex_home.path().join("config.toml"),
format!(
"cli_auth_credentials_store = \"file\"\nchatgpt_base_url = \"{}/backend-api\"\n",
server.uri()
),
)?;
write_chatgpt_auth(
codex_home.path(),
ChatGptAuthFixture::new("chatgpt-token")
.account_id("workspace-123")
.plan_type("enterprise"),
AuthCredentialsStoreMode::File,
)?;
for enabled in [true, false] {
let output = codex_command(codex_home.path())?
.env("NO_PROXY", "127.0.0.1,localhost")
.env("no_proxy", "127.0.0.1,localhost")
.env_remove("CODEX_ACCESS_TOKEN")
.env_remove("OPENAI_API_KEY")
.args(["debug", "prompt-input"])
.output()?;
assert!(output.status.success());
let prompt = String::from_utf8(output.stdout)?;
assert_eq!(
prompt.contains("Co-authored-by: Codex <noreply@openai.com>"),
enabled
);
assert!(!prompt.contains("attribution is disabled for the current workspace"));
}
server.verify().await;
Ok(())
}
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn device_login_revokes_existing_auth_before_requesting_new_tokens() -> Result<()> {
let server = MockServer::start().await;