Support clear SessionStart source (#17073)

## Motivation

The `SessionStart` hook already receives `startup` and `resume` sources,
but sessions created from `/clear` previously looked like normal startup
sessions. This makes it impossible for hook authors to distinguish
between these with the matcher.

## Summary

- Add `InitialHistory::Cleared` so `/clear`-created sessions can be
distinguished from ordinary startup sessions.
- Add `SessionStartSource::Clear` and wire it through core, app-server
thread start params, and TUI clear-session flow.
- Update app-server protocol schemas, generated TypeScript, docs, and
related tests.


https://github.com/user-attachments/assets/9cae3cb4-41c7-4d06-b34f-966252442e5c
This commit is contained in:
Abhinav authored and GitHub committed 2026-04-10 16:05:21 -07:00
1 parent 87b9275fff
commit 7999b0f60f
17 files changed
+167 -22

No files matched your search

+6 -5
View File
@@ -599,7 +599,7 @@ impl Codex {
let thread_id = match &conversation_history {
InitialHistory::Resumed(resumed) => Some(resumed.conversation_id),
InitialHistory::Forked(_) => conversation_history.forked_from_id(),
InitialHistory::New => None,
InitialHistory::New | InitialHistory::Cleared => None,
};
match thread_id {
Some(thread_id) => {
@@ -1619,7 +1619,7 @@ impl Session {
let forked_from_id = initial_history.forked_from_id();
let (conversation_id, rollout_params) = match &initial_history {
InitialHistory::New | InitialHistory::Forked(_) => {
InitialHistory::New | InitialHistory::Cleared | InitialHistory::Forked(_) => {
let conversation_id = ThreadId::default();
(
conversation_id,
@@ -1660,14 +1660,14 @@ impl Session {
.count(),
)
.unwrap_or(u64::MAX),
InitialHistory::New | InitialHistory::Forked(_) => 0,
InitialHistory::New | InitialHistory::Cleared | InitialHistory::Forked(_) => 0,
};
let state_builder = match &initial_history {
InitialHistory::Resumed(resumed) => metadata::builder_from_items(
resumed.history.as_slice(),
resumed.rollout_path.as_path(),
),
InitialHistory::New | InitialHistory::Forked(_) => None,
InitialHistory::New | InitialHistory::Cleared | InitialHistory::Forked(_) => None,
};
// Kick off independent async setup tasks in parallel to reduce startup latency.
@@ -2199,6 +2199,7 @@ impl Session {
InitialHistory::New | InitialHistory::Forked(_) => {
codex_hooks::SessionStartSource::Startup
}
InitialHistory::Cleared => codex_hooks::SessionStartSource::Clear,
};
// record_initial_history can emit events. We record only after the SessionConfiguredEvent is emitted.
@@ -2339,7 +2340,7 @@ impl Session {
)
};
match conversation_history {
InitialHistory::New => {
InitialHistory::New | InitialHistory::Cleared => {
// Defer initial context insertion until the first real turn starts so
// turn/start overrides can be merged before we write model-visible context.
self.set_previous_turn_settings(/*previous_turn_settings*/ None)
+5 -2
View File
@@ -474,6 +474,7 @@ impl ThreadManager {
) -> CodexResult<NewThread> {
Box::pin(self.start_thread_with_tools_and_service_name(
config,
InitialHistory::New,
dynamic_tools,
persist_extended_history,
/*metrics_service_name*/ None,
@@ -485,6 +486,7 @@ impl ThreadManager {
pub async fn start_thread_with_tools_and_service_name(
&self,
config: Config,
initial_history: InitialHistory,
dynamic_tools: Vec<codex_protocol::dynamic_tools::DynamicToolSpec>,
persist_extended_history: bool,
metrics_service_name: Option<String>,
@@ -492,7 +494,7 @@ impl ThreadManager {
) -> CodexResult<NewThread> {
Box::pin(self.state.spawn_thread(
config,
InitialHistory::New,
initial_history,
Arc::clone(&self.state.auth_manager),
self.agent_control(),
dynamic_tools,
@@ -668,6 +670,7 @@ impl ThreadManager {
ForkSnapshot::Interrupted => {
let history = match history {
InitialHistory::New => InitialHistory::New,
InitialHistory::Cleared => InitialHistory::Cleared,
InitialHistory::Forked(history) => InitialHistory::Forked(history),
InitialHistory::Resumed(resumed) => InitialHistory::Forked(resumed.history),
};
@@ -1070,7 +1073,7 @@ fn append_interrupted_boundary(history: InitialHistory, turn_id: Option<String>)
}));
match history {
InitialHistory::New => InitialHistory::Forked(vec![
InitialHistory::New | InitialHistory::Cleared => InitialHistory::Forked(vec![
RolloutItem::ResponseItem(interrupted_turn_history_marker()),
aborted_event,
]),