mirror of
https://github.com/pchuan98/codex.git
synced 2026-07-01 00:31:56 +08:00
Support thread-level originator overrides (#29477)
## Why Work(TPP) threads can be launched from the Desktop app, but if they all keep the Desktop app's default originator then downstream attribution cannot distinguish local Work launches from cloud-backed Work launches. `thread/start.serviceName` already carries that launch signal, while `SessionMeta.originator` is the durable thread-level value that survives resume and fork. This change converts the Desktop Work service names into an effective originator at thread creation time, persists that originator with the thread, and keeps using it for later model requests and memory writes. ## What changed - Map `CODEX_WORK_LOCAL` and `CODEX_WORK_CLOUD` service names to per-thread originators, while preserving `CODEX_INTERNAL_ORIGINATOR_OVERRIDE` as the highest-precedence override. - Persist the effective originator in `SessionMeta.originator`, read it back on resume/fork, and inherit the parent originator for subagent spawns when there is no persisted session metadata. - Handle truncated `SpawnAgentForkMode::LastNTurns` forks by falling back to the live parent originator when the forked history no longer includes `SessionMeta`. - Thread the per-thread originator through Responses headers, websocket/compaction request paths, thread-store creation, rollout metadata, and memory stage-one telemetry. ## Verification - `just test -p codex-core agent::control::tests::spawn_thread_subagent_inherits_parent_originator_without_fork agent::control::tests::spawn_thread_subagent_fork_last_n_turns_inherits_parent_originator_without_session_meta thread_manager::tests::originator_override_precedes_service_name_remapping` - `just test -p codex-core agent::control::tests::resume_thread_subagent_restores_stored_metadata_and_effective_multi_agent_mode` - `just test -p codex-memories-write` - `just fix -p codex-core -p codex-memories-write` - `git diff --check`
This commit is contained in:
@@ -29,6 +29,8 @@ use codex_extension_api::empty_extension_registry;
|
||||
use codex_features::Feature;
|
||||
use codex_login::AuthManager;
|
||||
use codex_login::CodexAuth;
|
||||
use codex_login::default_client::CODEX_INTERNAL_ORIGINATOR_OVERRIDE_ENV_VAR;
|
||||
use codex_login::default_client::originator;
|
||||
use codex_model_provider::create_model_provider;
|
||||
use codex_model_provider_info::ModelProviderInfo;
|
||||
use codex_model_provider_info::OPENAI_PROVIDER_ID;
|
||||
@@ -192,6 +194,28 @@ pub struct StartThreadOptions {
|
||||
pub supports_openai_form_elicitation: bool,
|
||||
}
|
||||
|
||||
fn originator_from_service_name(service_name: Option<&str>) -> Option<String> {
|
||||
let service_name = service_name?.trim();
|
||||
if service_name.eq_ignore_ascii_case("codex_work_desktop") {
|
||||
return Some("codex_work_desktop".to_string());
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
fn effective_originator_value(
|
||||
metrics_service_name: Option<&str>,
|
||||
env_originator: Option<String>,
|
||||
persisted_originator: Option<String>,
|
||||
inherited_originator: Option<String>,
|
||||
default_originator: String,
|
||||
) -> String {
|
||||
originator_from_service_name(metrics_service_name)
|
||||
.or(persisted_originator)
|
||||
.or(inherited_originator)
|
||||
.or(env_originator)
|
||||
.unwrap_or(default_originator)
|
||||
}
|
||||
|
||||
pub(crate) struct ResumeThreadWithHistoryOptions {
|
||||
pub(crate) config: Config,
|
||||
pub(crate) initial_history: InitialHistory,
|
||||
@@ -1220,6 +1244,64 @@ impl ThreadManagerState {
|
||||
}
|
||||
}
|
||||
|
||||
async fn inherited_originator_for_parent_thread(
|
||||
&self,
|
||||
session_source: &SessionSource,
|
||||
parent_thread_id: Option<ThreadId>,
|
||||
forked_from_thread_id: Option<ThreadId>,
|
||||
) -> Option<String> {
|
||||
let inherited_thread_id = match session_source {
|
||||
SessionSource::SubAgent(SubAgentSource::ThreadSpawn {
|
||||
parent_thread_id, ..
|
||||
}) => Some(*parent_thread_id),
|
||||
_ => parent_thread_id.or(forked_from_thread_id),
|
||||
};
|
||||
let thread = self.get_thread(inherited_thread_id?).await.ok()?;
|
||||
let originator = thread.config_snapshot().await.originator;
|
||||
(!originator.is_empty()).then_some(originator)
|
||||
}
|
||||
|
||||
async fn effective_originator(
|
||||
&self,
|
||||
initial_history: &InitialHistory,
|
||||
metrics_service_name: Option<&str>,
|
||||
session_source: &SessionSource,
|
||||
parent_thread_id: Option<ThreadId>,
|
||||
forked_from_thread_id: Option<ThreadId>,
|
||||
) -> String {
|
||||
let persisted_originator = initial_history.get_session_originator();
|
||||
let inherited_originator = match initial_history {
|
||||
InitialHistory::New | InitialHistory::Cleared => {
|
||||
self.inherited_originator_for_parent_thread(
|
||||
session_source,
|
||||
parent_thread_id,
|
||||
forked_from_thread_id,
|
||||
)
|
||||
.await
|
||||
}
|
||||
InitialHistory::Forked(_) if persisted_originator.is_none() => {
|
||||
self.inherited_originator_for_parent_thread(
|
||||
session_source,
|
||||
parent_thread_id,
|
||||
forked_from_thread_id,
|
||||
)
|
||||
.await
|
||||
}
|
||||
InitialHistory::Resumed(_) | InitialHistory::Forked(_) => None,
|
||||
};
|
||||
|
||||
let env_originator = std::env::var(CODEX_INTERNAL_ORIGINATOR_OVERRIDE_ENV_VAR)
|
||||
.is_ok()
|
||||
.then(|| originator().value);
|
||||
effective_originator_value(
|
||||
metrics_service_name,
|
||||
env_originator,
|
||||
persisted_originator,
|
||||
inherited_originator,
|
||||
originator().value,
|
||||
)
|
||||
}
|
||||
|
||||
/// Spawn a new thread with no history using a provided config.
|
||||
pub(crate) async fn spawn_new_thread(
|
||||
&self,
|
||||
@@ -1466,6 +1548,15 @@ impl ThreadManagerState {
|
||||
forked_from_thread_id,
|
||||
)
|
||||
.await;
|
||||
let originator = self
|
||||
.effective_originator(
|
||||
&initial_history,
|
||||
metrics_service_name.as_deref(),
|
||||
&session_source,
|
||||
parent_thread_id,
|
||||
forked_from_thread_id,
|
||||
)
|
||||
.await;
|
||||
let CodexSpawnOk {
|
||||
codex, thread_id, ..
|
||||
} = Box::pin(Codex::spawn(CodexSpawnArgs {
|
||||
@@ -1484,6 +1575,7 @@ impl ThreadManagerState {
|
||||
forked_from_thread_id,
|
||||
parent_thread_id,
|
||||
thread_source,
|
||||
originator,
|
||||
agent_control,
|
||||
dynamic_tools,
|
||||
metrics_service_name,
|
||||
|
||||
Reference in New Issue
Block a user