Skip to main content

crabtalk_core/agent/
id.rs

1//! Stable agent identity.
2//!
3//! An [`AgentId`] is a ULID — Crockford base32, 26 characters, sortable
4//! by creation time. Agents get a fresh ULID when they're created and
5//! keep it across renames. Phase 5 introduces the field; later phases
6//! (agent CRUD via Storage, sessions) start keying on it.
7
8use serde::{Deserialize, Serialize};
9use std::{
10    fmt::{self, Display},
11    str::FromStr,
12};
13use ulid::Ulid;
14
15/// Stable identifier for an agent. Newtype over [`Ulid`] so callers can
16/// extend the type later without touching call sites.
17#[derive(
18    Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize, Default,
19)]
20#[serde(transparent)]
21pub struct AgentId(pub Ulid);
22
23impl AgentId {
24    /// Generate a fresh ULID for a new agent.
25    pub fn new() -> Self {
26        Self(Ulid::new())
27    }
28
29    /// The nil/zero ID — used as a sentinel for "not yet backfilled".
30    /// Callers that see this on a registered agent should treat it as a
31    /// bug: the daemon's startup backfill is expected to replace any
32    /// missing `id` field before agents reach the runtime.
33    pub const fn nil() -> Self {
34        Self(Ulid::nil())
35    }
36
37    /// Is this the nil/zero sentinel?
38    pub fn is_nil(&self) -> bool {
39        self.0.is_nil()
40    }
41}
42
43impl Display for AgentId {
44    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
45        Display::fmt(&self.0, f)
46    }
47}
48
49impl FromStr for AgentId {
50    type Err = ulid::DecodeError;
51
52    fn from_str(s: &str) -> Result<Self, Self::Err> {
53        Ulid::from_str(s).map(Self)
54    }
55}