Skip to main content

codetether_agent/provenance/
identity.rs

1const GIT_EMAIL_DOMAIN: &str = "codetether.run";
2const GIT_NAME: &str = "CodeTether Agent";
3
4pub fn git_author_name(agent_identity_id: Option<&str>) -> String {
5    match agent_identity_id.and_then(sanitize_identity) {
6        Some(identity) => format!("{GIT_NAME} [{identity}]"),
7        None => GIT_NAME.to_string(),
8    }
9}
10
11pub fn git_author_email(
12    agent_identity_id: Option<&str>,
13    worker_id: Option<&str>,
14    provenance_id: Option<&str>,
15) -> String {
16    let identity = [agent_identity_id, worker_id, provenance_id]
17        .into_iter()
18        .flatten()
19        .find_map(sanitize_identity)
20        .unwrap_or_else(|| "unknown".to_string());
21    format!("agent+{identity}@{GIT_EMAIL_DOMAIN}")
22}
23
24fn sanitize_identity(value: &str) -> Option<String> {
25    let mut output = String::new();
26    let mut last_was_dash = false;
27    for ch in value.trim().chars() {
28        let lower = ch.to_ascii_lowercase();
29        if lower.is_ascii_alphanumeric() {
30            output.push(lower);
31            last_was_dash = false;
32        } else if !last_was_dash && !output.is_empty() {
33            output.push('-');
34            last_was_dash = true;
35        }
36        if output.len() >= 48 {
37            break;
38        }
39    }
40    while output.ends_with('-') {
41        output.pop();
42    }
43    (!output.is_empty()).then_some(output)
44}
45
46#[cfg(test)]
47mod tests {
48    use super::{git_author_email, git_author_name};
49
50    #[test]
51    fn derives_unique_email_from_agent_identity() {
52        let email = git_author_email(Some("user:abc-123"), None, None);
53        assert_eq!(email, "agent+user-abc-123@codetether.run");
54    }
55
56    #[test]
57    fn includes_identity_in_display_name() {
58        let name = git_author_name(Some("user:abc-123"));
59        assert_eq!(name, "CodeTether Agent [user-abc-123]");
60    }
61}