Skip to main content

tea_context/
provenance.rs

1use serde::{Deserialize, Serialize};
2use thiserror::Error;
3
4use crate::ContextProviderId;
5
6/// Fixed high-to-low prompt authority.
7#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
8#[serde(rename_all = "snake_case")]
9pub enum PromptAuthority {
10    /// Runtime safety and protocol invariants.
11    Kernel,
12    /// Organization-wide behavior and policy guidance.
13    Organization,
14    /// Product/profile identity and behavior.
15    Product,
16    /// Caller-supplied workspace instructions.
17    Workspace,
18    /// Active tool-specific guidance.
19    Tool,
20    /// Explicitly active skill metadata or instructions.
21    Skill,
22    /// Session summary or retrieved session context.
23    Session,
24    /// Explicit user-supplied system addition.
25    UserAddition,
26}
27
28/// Declared origin trust for inspection and downstream policy.
29#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
30#[serde(rename_all = "snake_case")]
31pub enum TrustLevel {
32    /// Content is owned by the runtime/product trust boundary.
33    Trusted,
34    /// Content is supplied by a configured delegated source.
35    Delegated,
36    /// Content is caller-marked untrusted and receives no safety claim.
37    Untrusted,
38}
39
40/// Intended reuse scope for prompt caching adapters.
41#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
42#[serde(rename_all = "snake_case")]
43pub enum CacheScope {
44    /// Never reuse beyond this compiled prompt.
45    None,
46    /// Reuse within one active run.
47    Run,
48    /// Reuse within one session.
49    Session,
50    /// Reuse for one product profile.
51    Profile,
52    /// Reuse globally when the embedder can prove identity.
53    Global,
54}
55
56/// Bounded source attribution retained through compilation.
57#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
58#[serde(rename_all = "camelCase")]
59pub struct PromptProvenance {
60    provider_id: ContextProviderId,
61    source_kind: String,
62    #[serde(skip_serializing_if = "Option::is_none")]
63    locator: Option<String>,
64}
65
66impl PromptProvenance {
67    /// Creates bounded explicit provenance.
68    ///
69    /// # Errors
70    ///
71    /// Returns an error for invalid source kind or locator text.
72    pub fn new(
73        provider_id: ContextProviderId,
74        source_kind: impl Into<String>,
75        locator: Option<String>,
76    ) -> Result<Self, ProvenanceError> {
77        let source_kind = source_kind.into();
78        if !valid_source_kind(&source_kind)
79            || locator.as_ref().is_some_and(|value| {
80                value.is_empty() || value.len() > 2048 || value.chars().any(char::is_control)
81            })
82        {
83            return Err(ProvenanceError);
84        }
85        Ok(Self {
86            provider_id,
87            source_kind,
88            locator,
89        })
90    }
91
92    /// Returns the producing provider.
93    #[must_use]
94    pub const fn provider_id(&self) -> &ContextProviderId {
95        &self.provider_id
96    }
97    /// Returns canonical source category.
98    #[must_use]
99    pub fn source_kind(&self) -> &str {
100        &self.source_kind
101    }
102    /// Returns optional bounded source locator.
103    #[must_use]
104    pub fn locator(&self) -> Option<&str> {
105        self.locator.as_deref()
106    }
107}
108
109/// Invalid prompt provenance.
110#[derive(Debug, Clone, Copy, PartialEq, Eq, Error)]
111#[error("prompt provenance is invalid")]
112pub struct ProvenanceError;
113
114fn valid_source_kind(value: &str) -> bool {
115    let mut bytes = value.bytes();
116    value.len() <= 128
117        && bytes.next().is_some_and(|byte| byte.is_ascii_lowercase())
118        && bytes.all(|byte| byte.is_ascii_lowercase() || byte.is_ascii_digit() || byte == b'_')
119}