use serde::{Deserialize, Deserializer, Serialize, Serializer};
use std::fmt;
use std::hash::Hash;
use std::marker::PhantomData;
use std::str::FromStr;
use uuid::Uuid;
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum IdParseError {
InvalidPrefix { expected: &'static str, got: String },
InvalidHex(String),
InvalidLength { expected: usize, got: usize },
}
impl std::fmt::Display for IdParseError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
IdParseError::InvalidPrefix { expected, got } => {
write!(f, "invalid prefix: expected '{}', got '{}'", expected, got)
}
IdParseError::InvalidHex(s) => write!(f, "invalid hex in ID: {}", s),
IdParseError::InvalidLength { expected, got } => {
write!(f, "invalid length: expected {}, got {}", expected, got)
}
}
}
}
impl std::error::Error for IdParseError {}
pub trait IdMarker: Clone + Copy + Send + Sync + 'static {
const PREFIX: &'static str;
fn generate_uuid() -> Uuid {
Uuid::now_v7()
}
}
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
pub struct TypedId<T: IdMarker> {
uuid: Uuid,
_marker: PhantomData<T>,
}
impl<T: IdMarker> TypedId<T> {
pub fn new() -> Self {
Self {
uuid: T::generate_uuid(),
_marker: PhantomData,
}
}
pub fn new_random() -> Self {
Self {
uuid: Uuid::new_v4(),
_marker: PhantomData,
}
}
pub fn from_uuid(uuid: Uuid) -> Self {
Self {
uuid,
_marker: PhantomData,
}
}
pub fn uuid(&self) -> Uuid {
self.uuid
}
pub fn prefix() -> &'static str {
T::PREFIX
}
pub fn parse(s: &str) -> Result<Self, IdParseError> {
let expected_prefix = format!("{}_", T::PREFIX);
if !s.starts_with(&expected_prefix) {
let got_prefix = s.split('_').next().unwrap_or("").to_string();
return Err(IdParseError::InvalidPrefix {
expected: T::PREFIX,
got: got_prefix,
});
}
let suffix = &s[expected_prefix.len()..];
if suffix.len() != 32 {
return Err(IdParseError::InvalidLength {
expected: 32,
got: suffix.len(),
});
}
if !suffix
.chars()
.all(|c| c.is_ascii_hexdigit() && !c.is_ascii_uppercase())
{
return Err(IdParseError::InvalidHex(suffix.to_string()));
}
let uuid =
Uuid::parse_str(suffix).map_err(|_| IdParseError::InvalidHex(suffix.to_string()))?;
Ok(Self {
uuid,
_marker: PhantomData,
})
}
pub fn from_seed(value: u128) -> Self {
let uuid = Uuid::from_u128(value);
Self {
uuid,
_marker: PhantomData,
}
}
}
impl<T: IdMarker> Default for TypedId<T> {
fn default() -> Self {
Self::new()
}
}
impl<T: IdMarker> From<TypedId<T>> for Uuid {
fn from(id: TypedId<T>) -> Self {
id.uuid
}
}
impl<T: IdMarker> From<Uuid> for TypedId<T> {
fn from(uuid: Uuid) -> Self {
Self::from_uuid(uuid)
}
}
impl<T: IdMarker> std::borrow::Borrow<Uuid> for TypedId<T> {
fn borrow(&self) -> &Uuid {
&self.uuid
}
}
impl<T: IdMarker> AsRef<Uuid> for TypedId<T> {
fn as_ref(&self) -> &Uuid {
&self.uuid
}
}
impl<T: IdMarker> PartialEq<Uuid> for TypedId<T> {
fn eq(&self, other: &Uuid) -> bool {
self.uuid == *other
}
}
impl<T: IdMarker> PartialEq<TypedId<T>> for Uuid {
fn eq(&self, other: &TypedId<T>) -> bool {
*self == other.uuid
}
}
impl<T: IdMarker> fmt::Display for TypedId<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}_{}", T::PREFIX, self.uuid.simple())
}
}
impl<T: IdMarker> fmt::Debug for TypedId<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"{}({})",
std::any::type_name::<T>()
.split("::")
.last()
.unwrap_or("Id"),
self
)
}
}
impl<T: IdMarker> FromStr for TypedId<T> {
type Err = IdParseError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Self::parse(s)
}
}
impl<T: IdMarker> Serialize for TypedId<T> {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
serializer.serialize_str(&self.to_string())
}
}
impl<'de, T: IdMarker> Deserialize<'de> for TypedId<T> {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
let s = String::deserialize(deserializer)?;
Self::parse(&s).map_err(serde::de::Error::custom)
}
}
#[cfg(feature = "openapi")]
impl<T: IdMarker> utoipa::ToSchema for TypedId<T> {
fn name() -> std::borrow::Cow<'static, str> {
std::borrow::Cow::Owned(format!("{}Id", T::PREFIX))
}
}
#[cfg(feature = "openapi")]
impl<T: IdMarker> utoipa::PartialSchema for TypedId<T> {
#[allow(deprecated)]
fn schema() -> utoipa::openapi::RefOr<utoipa::openapi::schema::Schema> {
let example_value = format!("{}_{}", T::PREFIX, "01933b5a000070008000000000000001");
utoipa::openapi::ObjectBuilder::new()
.schema_type(utoipa::openapi::schema::Type::String)
.description(Some(format!(
"Prefixed identifier with '{}' prefix",
T::PREFIX
)))
.example(Some(serde_json::json!(example_value)))
.pattern(Some(format!("^{}_[0-9a-f]{{32}}$", T::PREFIX)))
.into()
}
}
#[cfg(feature = "sqlx")]
impl<T: IdMarker> sqlx::Type<sqlx::Postgres> for TypedId<T> {
fn type_info() -> sqlx::postgres::PgTypeInfo {
<Uuid as sqlx::Type<sqlx::Postgres>>::type_info()
}
fn compatible(ty: &sqlx::postgres::PgTypeInfo) -> bool {
<Uuid as sqlx::Type<sqlx::Postgres>>::compatible(ty)
}
}
#[cfg(feature = "sqlx")]
impl<T: IdMarker> sqlx::Encode<'_, sqlx::Postgres> for TypedId<T> {
fn encode_by_ref(
&self,
buf: &mut sqlx::postgres::PgArgumentBuffer,
) -> Result<sqlx::encode::IsNull, Box<dyn std::error::Error + Send + Sync>> {
<Uuid as sqlx::Encode<sqlx::Postgres>>::encode_by_ref(&self.uuid, buf)
}
}
#[cfg(feature = "sqlx")]
impl<T: IdMarker> sqlx::Decode<'_, sqlx::Postgres> for TypedId<T> {
fn decode(
value: sqlx::postgres::PgValueRef<'_>,
) -> Result<Self, Box<dyn std::error::Error + Send + Sync>> {
let uuid = <Uuid as sqlx::Decode<sqlx::Postgres>>::decode(value)?;
Ok(Self::from_uuid(uuid))
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct OrgIdMarker;
impl IdMarker for OrgIdMarker {
const PREFIX: &'static str = "org";
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct AgentIdMarker;
impl IdMarker for AgentIdMarker {
const PREFIX: &'static str = "agent";
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct AgentVersionIdMarker;
impl IdMarker for AgentVersionIdMarker {
const PREFIX: &'static str = "agentver";
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct HarnessIdMarker;
impl IdMarker for HarnessIdMarker {
const PREFIX: &'static str = "harness";
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct AgentIdentityIdMarker;
impl IdMarker for AgentIdentityIdMarker {
const PREFIX: &'static str = "identity";
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct TriggerIdMarker;
impl IdMarker for TriggerIdMarker {
const PREFIX: &'static str = "trg";
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct PrincipalIdMarker;
impl IdMarker for PrincipalIdMarker {
const PREFIX: &'static str = "principal";
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct SessionIdMarker;
impl IdMarker for SessionIdMarker {
const PREFIX: &'static str = "session";
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct SessionParticipantIdMarker;
impl IdMarker for SessionParticipantIdMarker {
const PREFIX: &'static str = "part";
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct MessageIdMarker;
impl IdMarker for MessageIdMarker {
const PREFIX: &'static str = "message";
fn generate_uuid() -> Uuid {
Uuid::new_v4()
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct EventIdMarker;
impl IdMarker for EventIdMarker {
const PREFIX: &'static str = "event";
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct ProviderIdMarker;
impl IdMarker for ProviderIdMarker {
const PREFIX: &'static str = "provider";
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct ModelIdMarker;
impl IdMarker for ModelIdMarker {
const PREFIX: &'static str = "model";
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct ImageIdMarker;
impl IdMarker for ImageIdMarker {
const PREFIX: &'static str = "img";
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct FileIdMarker;
impl IdMarker for FileIdMarker {
const PREFIX: &'static str = "file";
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct McpServerIdMarker;
impl IdMarker for McpServerIdMarker {
const PREFIX: &'static str = "mcp";
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct SkillIdMarker;
impl IdMarker for SkillIdMarker {
const PREFIX: &'static str = "skill";
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct DeclarativeCapabilityIdMarker;
impl IdMarker for DeclarativeCapabilityIdMarker {
const PREFIX: &'static str = "cap";
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct TurnIdMarker;
impl IdMarker for TurnIdMarker {
const PREFIX: &'static str = "turn";
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct ExecIdMarker;
impl IdMarker for ExecIdMarker {
const PREFIX: &'static str = "exec";
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct ScheduleIdMarker;
impl IdMarker for ScheduleIdMarker {
const PREFIX: &'static str = "sched";
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct LeasedResourceIdMarker;
impl IdMarker for LeasedResourceIdMarker {
const PREFIX: &'static str = "resource";
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct AppIdMarker;
impl IdMarker for AppIdMarker {
const PREFIX: &'static str = "app";
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct AppChannelIdMarker;
impl IdMarker for AppChannelIdMarker {
const PREFIX: &'static str = "appchan";
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct NotificationIdMarker;
impl IdMarker for NotificationIdMarker {
const PREFIX: &'static str = "notification";
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct MemoryIdMarker;
impl IdMarker for MemoryIdMarker {
const PREFIX: &'static str = "mem";
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct WorkspaceIdMarker;
impl IdMarker for WorkspaceIdMarker {
const PREFIX: &'static str = "wsp";
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct EvalIdMarker;
impl IdMarker for EvalIdMarker {
const PREFIX: &'static str = "eval";
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct EvalCaseIdMarker;
impl IdMarker for EvalCaseIdMarker {
const PREFIX: &'static str = "evalcase";
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct EvalRunIdMarker;
impl IdMarker for EvalRunIdMarker {
const PREFIX: &'static str = "evalrun";
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct EvalDatasetIdMarker;
impl IdMarker for EvalDatasetIdMarker {
const PREFIX: &'static str = "evaldataset";
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct HealthCheckRunIdMarker;
impl IdMarker for HealthCheckRunIdMarker {
const PREFIX: &'static str = "healthcheck";
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct EvalResultIdMarker;
impl IdMarker for EvalResultIdMarker {
const PREFIX: &'static str = "evalresult";
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct ObserverIdMarker;
impl IdMarker for ObserverIdMarker {
const PREFIX: &'static str = "observer";
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct TraceScoreIdMarker;
impl IdMarker for TraceScoreIdMarker {
const PREFIX: &'static str = "score";
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct BudgetIdMarker;
impl IdMarker for BudgetIdMarker {
const PREFIX: &'static str = "bdgt";
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct PaymentAccountIdMarker;
impl IdMarker for PaymentAccountIdMarker {
const PREFIX: &'static str = "payacct";
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct PaymentPolicyIdMarker;
impl IdMarker for PaymentPolicyIdMarker {
const PREFIX: &'static str = "paypol";
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct PaymentAttemptIdMarker;
impl IdMarker for PaymentAttemptIdMarker {
const PREFIX: &'static str = "payatt";
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct LedgerEntryIdMarker;
impl IdMarker for LedgerEntryIdMarker {
const PREFIX: &'static str = "ledger";
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct KnowledgeBaseIdMarker;
impl IdMarker for KnowledgeBaseIdMarker {
const PREFIX: &'static str = "kb";
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct KnowledgeEntryIdMarker;
impl IdMarker for KnowledgeEntryIdMarker {
const PREFIX: &'static str = "kbe";
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct KnowledgeIndexIdMarker;
impl IdMarker for KnowledgeIndexIdMarker {
const PREFIX: &'static str = "kidx";
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct KnowledgeIndexDocumentIdMarker;
impl IdMarker for KnowledgeIndexDocumentIdMarker {
const PREFIX: &'static str = "kidoc";
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct KnowledgeIndexChunkIdMarker;
impl IdMarker for KnowledgeIndexChunkIdMarker {
const PREFIX: &'static str = "kchk";
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct ModelRouterIdMarker;
impl IdMarker for ModelRouterIdMarker {
const PREFIX: &'static str = "mrtr";
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct PluginMarketplaceIdMarker;
impl IdMarker for PluginMarketplaceIdMarker {
const PREFIX: &'static str = "plgmkt";
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct PluginInstallIdMarker;
impl IdMarker for PluginInstallIdMarker {
const PREFIX: &'static str = "plugin";
}
pub type OrgId = TypedId<OrgIdMarker>;
pub type AgentId = TypedId<AgentIdMarker>;
pub type AgentVersionId = TypedId<AgentVersionIdMarker>;
pub type HarnessId = TypedId<HarnessIdMarker>;
pub type AgentIdentityId = TypedId<AgentIdentityIdMarker>;
pub type TriggerId = TypedId<TriggerIdMarker>;
pub type PrincipalId = TypedId<PrincipalIdMarker>;
pub type SessionId = TypedId<SessionIdMarker>;
pub type SessionParticipantId = TypedId<SessionParticipantIdMarker>;
pub type MessageId = TypedId<MessageIdMarker>;
pub type EventId = TypedId<EventIdMarker>;
pub type ProviderId = TypedId<ProviderIdMarker>;
pub type ModelId = TypedId<ModelIdMarker>;
pub type ImageId = TypedId<ImageIdMarker>;
pub type FileId = TypedId<FileIdMarker>;
pub type McpServerId = TypedId<McpServerIdMarker>;
pub type SkillId = TypedId<SkillIdMarker>;
pub type DeclarativeCapabilityId = TypedId<DeclarativeCapabilityIdMarker>;
pub type TurnId = TypedId<TurnIdMarker>;
pub type ExecId = TypedId<ExecIdMarker>;
pub type ScheduleId = TypedId<ScheduleIdMarker>;
pub type LeasedResourceId = TypedId<LeasedResourceIdMarker>;
pub type AppId = TypedId<AppIdMarker>;
pub type AppChannelId = TypedId<AppChannelIdMarker>;
pub type NotificationId = TypedId<NotificationIdMarker>;
pub type MemoryId = TypedId<MemoryIdMarker>;
pub type WorkspaceId = TypedId<WorkspaceIdMarker>;
pub type EvalId = TypedId<EvalIdMarker>;
pub type EvalCaseId = TypedId<EvalCaseIdMarker>;
pub type EvalRunId = TypedId<EvalRunIdMarker>;
pub type EvalDatasetId = TypedId<EvalDatasetIdMarker>;
pub type HealthCheckRunId = TypedId<HealthCheckRunIdMarker>;
pub type EvalResultId = TypedId<EvalResultIdMarker>;
pub type ObserverId = TypedId<ObserverIdMarker>;
pub type TraceScoreId = TypedId<TraceScoreIdMarker>;
pub type BudgetId = TypedId<BudgetIdMarker>;
pub type PaymentAccountId = TypedId<PaymentAccountIdMarker>;
pub type PaymentPolicyId = TypedId<PaymentPolicyIdMarker>;
pub type PaymentAttemptId = TypedId<PaymentAttemptIdMarker>;
pub type LedgerEntryId = TypedId<LedgerEntryIdMarker>;
pub type KnowledgeBaseId = TypedId<KnowledgeBaseIdMarker>;
pub type KnowledgeEntryId = TypedId<KnowledgeEntryIdMarker>;
pub type KnowledgeIndexId = TypedId<KnowledgeIndexIdMarker>;
pub type KnowledgeIndexDocumentId = TypedId<KnowledgeIndexDocumentIdMarker>;
pub type KnowledgeIndexChunkId = TypedId<KnowledgeIndexChunkIdMarker>;
pub type ModelRouterId = TypedId<ModelRouterIdMarker>;
pub type PluginMarketplaceId = TypedId<PluginMarketplaceIdMarker>;
pub type PluginInstallId = TypedId<PluginInstallIdMarker>;
pub const DEFAULT_ORG_ID: OrgId = TypedId {
uuid: Uuid::from_u128(1),
_marker: PhantomData,
};
pub mod well_known {
use super::*;
pub const OPENAI_PROVIDER_ID: ProviderId = TypedId {
uuid: Uuid::from_u128(0x01933b5a_0000_7000_8000_000000000001),
_marker: PhantomData,
};
pub const ANTHROPIC_PROVIDER_ID: ProviderId = TypedId {
uuid: Uuid::from_u128(0x01933b5a_0000_7000_8000_000000000002),
_marker: PhantomData,
};
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn literal_ids_preserve_seed_uuid_and_wire_identity() {
for (seed, wire) in [
(0, "agent_00000000000000000000000000000000"),
(1, "agent_00000000000000000000000000000001"),
(u128::MAX, "agent_ffffffffffffffffffffffffffffffff"),
] {
let id = AgentId::from_seed(seed);
assert_eq!(id.to_string(), wire);
assert_eq!(AgentId::parse(wire).unwrap(), id);
assert_eq!(wire.parse::<AgentId>().unwrap(), id);
assert_eq!(id.uuid().as_u128(), seed);
assert_eq!(serde_json::to_value(id).unwrap(), serde_json::json!(wire));
assert_eq!(
serde_json::from_value::<AgentId>(serde_json::json!(wire)).unwrap(),
id
);
}
}
#[test]
fn parsing_rejects_wrong_namespace_length_and_noncanonical_hex() {
for (input, prefix) in [
("session_00000000000000000000000000000001", "session"),
("agentx_00000000000000000000000000000001", "agentx"),
("", ""),
] {
assert_eq!(
AgentId::parse(input),
Err(IdParseError::InvalidPrefix {
expected: "agent",
got: prefix.into()
})
);
}
for size in [0, 31, 33] {
assert_eq!(
AgentId::parse(&format!("agent_{}", "0".repeat(size))),
Err(IdParseError::InvalidLength {
expected: 32,
got: size
})
);
}
for suffix in [
"A".repeat(32),
"g".repeat(32),
"é".repeat(16),
"0".repeat(31) + " ",
] {
assert_eq!(
AgentId::parse(&format!("agent_{suffix}")),
Err(IdParseError::InvalidHex(suffix))
);
}
for value in [
serde_json::json!(42),
serde_json::json!(null),
serde_json::json!("session_00000000000000000000000000000001"),
serde_json::json!("agent_123"),
] {
assert!(serde_json::from_value::<AgentId>(value).is_err());
}
assert_eq!(
AgentId::parse("agent_123").unwrap_err().to_string(),
"invalid length: expected 32, got 3"
);
assert_eq!(
AgentId::parse("session_123").unwrap_err().to_string(),
"invalid prefix: expected 'agent', got 'session'"
);
}
#[test]
fn uuid_conversions_support_borrowed_map_lookup() {
let first = Uuid::from_u128(17);
let second = Uuid::from_u128(23);
let first_id = AgentId::from_uuid(first);
let second_id: AgentId = second.into();
let map = std::collections::HashMap::from([(first_id, "first"), (second_id, "second")]);
assert_eq!(map.get(&first), Some(&"first"));
assert_eq!(map.get(&second), Some(&"second"));
assert_eq!(map.get(&Uuid::from_u128(99)), None);
assert_eq!(first_id.as_ref(), &first);
assert_eq!(Uuid::from(second_id), second);
assert_eq!(first_id, first);
assert_eq!(first, first_id);
assert_ne!(first_id, second);
assert_ne!(second, first_id);
}
#[test]
fn generation_respects_storage_policy_and_explicit_random_override() {
for uuid in [
AgentId::new().uuid(),
AgentId::default().uuid(),
SessionId::new().uuid(),
EventId::new().uuid(),
TurnId::new().uuid(),
] {
assert_eq!(uuid.get_version_num(), 7);
}
for uuid in [
MessageId::new().uuid(),
MessageId::default().uuid(),
MessageId::new_random().uuid(),
AgentId::new_random().uuid(),
] {
assert_eq!(uuid.get_version_num(), 4);
}
}
#[test]
fn message_ids_accept_literal_legacy_and_random_versions() {
for (wire, version) in [
("message_01933b5a000070008000000000000001", 7),
("message_01933b5a000040008000000000000001", 4),
] {
let id = MessageId::parse(wire).unwrap();
assert_eq!(id.uuid().get_version_num(), version);
assert_eq!(id.to_string(), wire);
}
}
#[test]
fn seeded_database_identities_remain_stable() {
assert_eq!(
DEFAULT_ORG_ID.to_string(),
"org_00000000000000000000000000000001"
);
assert_eq!(
well_known::OPENAI_PROVIDER_ID.to_string(),
"provider_01933b5a000070008000000000000001"
);
assert_eq!(
well_known::ANTHROPIC_PROVIDER_ID.to_string(),
"provider_01933b5a000070008000000000000002"
);
}
#[cfg(feature = "openapi")]
#[test]
fn openapi_examples_match_the_identifier_contract() {
let schema = serde_json::to_value(<AgentId as utoipa::PartialSchema>::schema()).unwrap();
assert_eq!(schema["type"], "string");
assert_eq!(schema["pattern"], "^agent_[0-9a-f]{32}$");
let example = schema["example"].as_str().unwrap();
assert!(
AgentId::parse(example).is_ok(),
"invalid schema example: {example}"
);
}
}