mirror of
https://github.com/pchuan98/codex.git
synced 2026-07-01 00:31:56 +08:00
[codex-analytics] add extensible feature thread sources (#27063)
## Why - `ThreadSource` currently defines a closed set of core-owned values - Product features also create threads for background or scheduled work - Adding every product-specific value to the core enum would require repeated `codex-rs` protocol changes - Feature-backed values let product callers provide precise attribution while preserving the existing core classifications ## What Changed - Adds `ThreadSource::Feature(String)` for app-owned thread source values - Represents all app-server v2 thread sources as scalar strings, so a feature source is supplied as `"automation"` - Persists and emits the feature's plain string label, so `"automation"` produces `thread_source="automation"` in analytics - Keeps `user`, `subagent`, and `memory_consolidation` as explicit core-owned values and regenerates the app-server schemas and TypeScript bindings ## Verification - `just write-app-server-schema` - `cargo check --workspace` - `just test -p codex-protocol feature_thread_source_serializes_as_its_app_owned_label` - `just test -p codex-app-server-protocol thread_sources_round_trip_as_scalar_labels` - `cargo test -p codex-analytics thread_initialized_event_serializes_expected_shape` - `just fmt`
This commit is contained in:
committed by
GitHub
Unverified
parent
99da697e4c
commit
a71e040df5
@@ -2480,12 +2480,12 @@ impl InitialHistory {
|
||||
|
||||
pub fn get_resumed_session_sources(&self) -> Option<(SessionSource, Option<ThreadSource>)> {
|
||||
let meta = self.get_resumed_session_meta()?;
|
||||
Some((meta.source.clone(), meta.thread_source))
|
||||
Some((meta.source.clone(), meta.thread_source.clone()))
|
||||
}
|
||||
|
||||
pub fn get_resumed_thread_source(&self) -> Option<ThreadSource> {
|
||||
self.get_resumed_session_meta()
|
||||
.and_then(|meta| meta.thread_source)
|
||||
.and_then(|meta| meta.thread_source.clone())
|
||||
}
|
||||
|
||||
pub fn get_resumed_parent_thread_id(&self) -> Option<ThreadId> {
|
||||
@@ -2529,20 +2529,23 @@ pub enum SessionSource {
|
||||
Unknown,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Copy, Debug, PartialEq, Eq, JsonSchema, TS)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
#[ts(rename_all = "snake_case")]
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, JsonSchema, TS)]
|
||||
#[serde(try_from = "String", into = "String")]
|
||||
#[schemars(with = "String")]
|
||||
#[ts(type = "string")]
|
||||
pub enum ThreadSource {
|
||||
User,
|
||||
Subagent,
|
||||
Feature(String),
|
||||
MemoryConsolidation,
|
||||
}
|
||||
|
||||
impl ThreadSource {
|
||||
pub fn as_str(self) -> &'static str {
|
||||
pub fn as_str(&self) -> &str {
|
||||
match self {
|
||||
ThreadSource::User => "user",
|
||||
ThreadSource::Subagent => "subagent",
|
||||
ThreadSource::Feature(feature) => feature,
|
||||
ThreadSource::MemoryConsolidation => "memory_consolidation",
|
||||
}
|
||||
}
|
||||
@@ -2554,6 +2557,20 @@ impl fmt::Display for ThreadSource {
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<String> for ThreadSource {
|
||||
type Error = String;
|
||||
|
||||
fn try_from(value: String) -> Result<Self, Self::Error> {
|
||||
value.parse()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ThreadSource> for String {
|
||||
fn from(value: ThreadSource) -> Self {
|
||||
value.to_string()
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for ThreadSource {
|
||||
type Err = String;
|
||||
|
||||
@@ -2562,7 +2579,7 @@ impl FromStr for ThreadSource {
|
||||
"user" => Ok(ThreadSource::User),
|
||||
"subagent" => Ok(ThreadSource::Subagent),
|
||||
"memory_consolidation" => Ok(ThreadSource::MemoryConsolidation),
|
||||
other => Err(format!("unknown thread source: {other}")),
|
||||
other => Ok(ThreadSource::Feature(other.to_string())),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4048,6 +4065,18 @@ mod tests {
|
||||
use tempfile::NamedTempFile;
|
||||
use tempfile::TempDir;
|
||||
|
||||
#[test]
|
||||
fn feature_thread_source_serializes_as_its_app_owned_label() -> Result<()> {
|
||||
let source = ThreadSource::Feature("automation".to_string());
|
||||
|
||||
assert_eq!(serde_json::to_value(&source)?, json!("automation"));
|
||||
assert_eq!(
|
||||
serde_json::from_value::<ThreadSource>(json!("automation"))?,
|
||||
source
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn sorted_writable_roots(roots: Vec<WritableRoot>) -> Vec<(PathBuf, Vec<PathBuf>)> {
|
||||
let mut sorted_roots: Vec<(PathBuf, Vec<PathBuf>)> = roots
|
||||
.into_iter()
|
||||
|
||||
Reference in New Issue
Block a user