mirror of
https://github.com/openai/codex.git
synced 2026-11-13 22:47:20 +00:00
Expose thread originators through the app-server API (#42458)
## What changed - Add the creation-time `originator` to app-server thread responses and `thread/started` notifications. - Persist originators in thread metadata and preserve the first recorded value across list, read, resume, rollout, and SQLite paths. - Add an `originators` allowlist to `thread/list` for hosted backends. The local app-server accepts omitted or empty values but rejects nonempty filters. ## Testing - Cover originator persistence across server restarts and list, read, and resume requests. - Verify local `thread/list` handling for nonempty, omitted, null, and empty originator filters. - Heap-allocate large TUI dispatcher futures in affected tests to keep them within the Windows test-thread stack. GitOrigin-RevId: 73ad6e61b8fdd2d93a22d1f971723a19ec7be3fc
This commit is contained in:
1 parent
8ff74cc9b1
commit
728cb12fe5
75 files changed
+511
-12
No files matched your search
@@ -176,6 +176,7 @@ pub(super) fn stored_thread_from_rollout_item(
|
||||
project_id: item.project_id,
|
||||
cwd: item.cwd.unwrap_or_default(),
|
||||
cli_version: item.cli_version.unwrap_or_default(),
|
||||
originator: item.originator,
|
||||
source,
|
||||
history_mode: item.history_mode,
|
||||
thread_source: None,
|
||||
|
||||
@@ -349,6 +349,11 @@ pub(super) async fn stored_thread_from_sqlite_metadata(
|
||||
.unwrap_or(metadata.history_mode);
|
||||
let name = thread_name_from_metadata(store, &metadata, history_mode).await;
|
||||
let mut thread = stored_thread_from_state_metadata(store, metadata, parent_thread_id);
|
||||
thread.originator = thread.originator.or_else(|| {
|
||||
session_meta
|
||||
.map(|meta| meta.originator)
|
||||
.filter(|originator| !originator.is_empty())
|
||||
});
|
||||
thread.forked_from_id = forked_from_id;
|
||||
thread.history_mode = history_mode;
|
||||
thread.name = name;
|
||||
@@ -397,6 +402,7 @@ pub(super) fn stored_thread_from_state_metadata(
|
||||
project_id: metadata.project_id,
|
||||
cwd: metadata.cwd,
|
||||
cli_version: metadata.cli_version,
|
||||
originator: metadata.originator,
|
||||
source: parse_session_source(&metadata.source),
|
||||
history_mode: metadata.history_mode,
|
||||
thread_source: metadata.thread_source,
|
||||
@@ -496,6 +502,7 @@ fn stored_thread_from_meta_line(
|
||||
project_id: None,
|
||||
cwd: meta_line.meta.cwd,
|
||||
cli_version: meta_line.meta.cli_version,
|
||||
originator: (!meta_line.meta.originator.is_empty()).then_some(meta_line.meta.originator),
|
||||
source: meta_line.meta.source,
|
||||
history_mode: meta_line.meta.history_mode,
|
||||
thread_source: meta_line.meta.thread_source,
|
||||
@@ -916,6 +923,7 @@ mod tests {
|
||||
thread.first_user_message.as_deref(),
|
||||
thread.model_provider.as_str(),
|
||||
thread.model.as_deref(),
|
||||
thread.originator.as_deref(),
|
||||
),
|
||||
(
|
||||
Some("Canonical SQLite name"),
|
||||
@@ -923,6 +931,7 @@ mod tests {
|
||||
Some("Canonical SQLite first user message"),
|
||||
"Canonical SQLite provider",
|
||||
Some("Canonical SQLite model"),
|
||||
Some("test_originator"),
|
||||
)
|
||||
);
|
||||
}
|
||||
@@ -1193,7 +1202,7 @@ mod tests {
|
||||
std::fs::create_dir_all(&day_dir).expect("sessions dir");
|
||||
let rollout_path = day_dir.join(format!("rollout-2025-01-03T12-00-00-{uuid}.jsonl"));
|
||||
let mut file = std::fs::File::create(&rollout_path).expect("session file");
|
||||
let meta = serde_json::json!({
|
||||
let mut meta = serde_json::json!({
|
||||
"timestamp": "2025-01-03T12-00-00",
|
||||
"type": "session_meta",
|
||||
"payload": {
|
||||
@@ -1248,9 +1257,28 @@ mod tests {
|
||||
assert_eq!(thread.model_provider, "sqlite-provider");
|
||||
assert_eq!(thread.cwd, home.path().join("workspace"));
|
||||
assert_eq!(thread.cli_version, "sqlite-cli");
|
||||
assert_eq!(thread.originator.as_deref(), Some("test_originator"));
|
||||
let history = thread.history.expect("history should load");
|
||||
assert_eq!(history.thread_id, thread_id);
|
||||
assert_eq!(history.items.len(), 1);
|
||||
|
||||
for (sqlite_originator, rollout_originator, expected_originator) in [
|
||||
(
|
||||
Some("saved_client"),
|
||||
"test_originator",
|
||||
Some("saved_client"),
|
||||
),
|
||||
(None, "", None),
|
||||
] {
|
||||
meta["payload"]["originator"] = serde_json::json!(rollout_originator);
|
||||
std::fs::write(&metadata.rollout_path, format!("{meta}\n"))
|
||||
.expect("write session meta");
|
||||
metadata.originator = sqlite_originator.map(str::to_owned);
|
||||
let thread = stored_thread_from_sqlite_metadata(&store, metadata.clone())
|
||||
.await
|
||||
.expect("read SQLite and session metadata");
|
||||
assert_eq!(thread.originator.as_deref(), expected_originator);
|
||||
}
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
|
||||
@@ -406,6 +406,7 @@ async fn apply_metadata_update(
|
||||
if let Some(source) = patch.source {
|
||||
metadata.source = enum_to_string(&source);
|
||||
}
|
||||
metadata.originator = metadata.originator.or(patch.originator);
|
||||
if let Some(thread_source) = patch.thread_source {
|
||||
metadata.thread_source = thread_source;
|
||||
}
|
||||
@@ -672,6 +673,7 @@ fn has_observed_metadata_facts(patch: &ThreadMetadataPatch) -> bool {
|
||||
|| patch.reasoning_effort.is_some()
|
||||
|| patch.created_at.is_some()
|
||||
|| patch.source.is_some()
|
||||
|| patch.originator.is_some()
|
||||
|| patch.thread_source.is_some()
|
||||
|| patch.agent_nickname.is_some()
|
||||
|| patch.agent_role.is_some()
|
||||
|
||||
Reference in new issue
Block a user