Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions codex-rs/app-server-protocol/schema/json/ClientRequest.json
Original file line number Diff line number Diff line change
Expand Up @@ -3186,10 +3186,27 @@
"type": "null"
}
]
},
"sessionStartSource": {
"anyOf": [
{
"$ref": "#/definitions/ThreadStartSource"
},
{
"type": "null"
}
]
}
},
"type": "object"
},
"ThreadStartSource": {
"enum": [
"startup",
"clear"
],
"type": "string"
},
"ThreadUnarchiveParams": {
"properties": {
"threadId": {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14167,6 +14167,16 @@
"type": "null"
}
]
},
"sessionStartSource": {
"anyOf": [
{
"$ref": "#/definitions/v2/ThreadStartSource"
},
{
"type": "null"
}
]
}
},
"title": "ThreadStartParams",
Expand Down Expand Up @@ -14234,6 +14244,13 @@
"title": "ThreadStartResponse",
"type": "object"
},
"ThreadStartSource": {
"enum": [
"startup",
"clear"
],
"type": "string"
},
"ThreadStartedNotification": {
"$schema": "http://json-schema.org/draft-07/schema#",
"properties": {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12022,6 +12022,16 @@
"type": "null"
}
]
},
"sessionStartSource": {
"anyOf": [
{
"$ref": "#/definitions/ThreadStartSource"
},
{
"type": "null"
}
]
}
},
"title": "ThreadStartParams",
Expand Down Expand Up @@ -12089,6 +12099,13 @@
"title": "ThreadStartResponse",
"type": "object"
},
"ThreadStartSource": {
"enum": [
"startup",
"clear"
],
"type": "string"
},
"ThreadStartedNotification": {
"$schema": "http://json-schema.org/draft-07/schema#",
"properties": {
Expand Down
17 changes: 17 additions & 0 deletions codex-rs/app-server-protocol/schema/json/v2/ThreadStartParams.json
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,13 @@
"flex"
],
"type": "string"
},
"ThreadStartSource": {
"enum": [
"startup",
"clear"
],
"type": "string"
}
},
"properties": {
Expand Down Expand Up @@ -210,6 +217,16 @@
"type": "null"
}
]
},
"sessionStartSource": {
"anyOf": [
{
"$ref": "#/definitions/ThreadStartSource"
},
{
"type": "null"
}
]
}
},
"title": "ThreadStartParams",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,13 @@ import type { JsonValue } from "../serde_json/JsonValue";
import type { ApprovalsReviewer } from "./ApprovalsReviewer";
import type { AskForApproval } from "./AskForApproval";
import type { SandboxMode } from "./SandboxMode";
import type { ThreadStartSource } from "./ThreadStartSource";

export type ThreadStartParams = {model?: string | null, modelProvider?: string | null, serviceTier?: ServiceTier | null | null, cwd?: string | null, approvalPolicy?: AskForApproval | null, /**
* Override where approval requests are routed for review on this thread
* and subsequent turns.
*/
approvalsReviewer?: ApprovalsReviewer | null, sandbox?: SandboxMode | null, config?: { [key in string]?: JsonValue } | null, serviceName?: string | null, baseInstructions?: string | null, developerInstructions?: string | null, personality?: Personality | null, ephemeral?: boolean | null, /**
approvalsReviewer?: ApprovalsReviewer | null, sandbox?: SandboxMode | null, config?: { [key in string]?: JsonValue } | null, serviceName?: string | null, baseInstructions?: string | null, developerInstructions?: string | null, personality?: Personality | null, ephemeral?: boolean | null, sessionStartSource?: ThreadStartSource | null, /**
* If true, opt into emitting raw Responses API items on the event stream.
* This is for internal use only (e.g. Codex Cloud).
*/
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
// GENERATED CODE! DO NOT MODIFY BY HAND!

// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.

export type ThreadStartSource = "startup" | "clear";
1 change: 1 addition & 0 deletions codex-rs/app-server-protocol/schema/typescript/v2/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -310,6 +310,7 @@ export type { ThreadSortKey } from "./ThreadSortKey";
export type { ThreadSourceKind } from "./ThreadSourceKind";
export type { ThreadStartParams } from "./ThreadStartParams";
export type { ThreadStartResponse } from "./ThreadStartResponse";
export type { ThreadStartSource } from "./ThreadStartSource";
export type { ThreadStartedNotification } from "./ThreadStartedNotification";
export type { ThreadStatus } from "./ThreadStatus";
export type { ThreadStatusChangedNotification } from "./ThreadStatusChangedNotification";
Expand Down
10 changes: 10 additions & 0 deletions codex-rs/app-server-protocol/src/protocol/v2.rs
Original file line number Diff line number Diff line change
Expand Up @@ -409,6 +409,14 @@ v2_enum_from_core!(
}
);

#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq, JsonSchema, TS)]
#[serde(rename_all = "camelCase")]
#[ts(rename_all = "camelCase", export_to = "v2/")]
pub enum ThreadStartSource {
Startup,
Clear,
}

#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, JsonSchema, TS)]
#[serde(rename_all = "camelCase")]
#[ts(export_to = "v2/")]
Expand Down Expand Up @@ -2611,6 +2619,8 @@ pub struct ThreadStartParams {
pub personality: Option<Personality>,
#[ts(optional = nullable)]
pub ephemeral: Option<bool>,
#[ts(optional = nullable)]
pub session_start_source: Option<ThreadStartSource>,
#[experimental("thread/start.dynamicTools")]
#[ts(optional = nullable)]
pub dynamic_tools: Option<Vec<DynamicToolSpec>>,
Expand Down
3 changes: 2 additions & 1 deletion codex-rs/app-server/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@ Example with notification opt-out:

## API Overview

- `thread/start` — create a new thread; emits `thread/started` (including the current `thread.status`) and auto-subscribes you to turn/item events for that thread. When the request includes a `cwd` and the resolved sandbox is `workspace-write` or full access, app-server also marks that project as trusted in the user `config.toml`.
- `thread/start` — create a new thread; emits `thread/started` (including the current `thread.status`) and auto-subscribes you to turn/item events for that thread. When the request includes a `cwd` and the resolved sandbox is `workspace-write` or full access, app-server also marks that project as trusted in the user `config.toml`. Pass `sessionStartSource: "clear"` when starting a replacement thread after clearing the current session so `SessionStart` hooks receive `source: "clear"` instead of the default `"startup"`.
- `thread/resume` — reopen an existing thread by id so subsequent `turn/start` calls append to it.
- `thread/fork` — fork an existing thread into a new thread id by copying the stored history; if the source thread is currently mid-turn, the fork records the same interruption marker as `turn/interrupt` instead of inheriting an unmarked partial turn suffix. The returned `thread.forkedFromId` points at the source thread when known. Accepts `ephemeral: true` for an in-memory temporary fork, emits `thread/started` (including the current `thread.status`), and auto-subscribes you to turn/item events for the new thread.
- `thread/list` — page through stored rollouts; supports cursor-based pagination and optional `modelProviders`, `sourceKinds`, `archived`, `cwd`, and `searchTerm` filters. Each returned `thread` includes `status` (`ThreadStatus`), defaulting to `notLoaded` when the thread is not currently loaded.
Expand Down Expand Up @@ -212,6 +212,7 @@ Start a fresh thread when you need a new Codex conversation.
"sandbox": "workspaceWrite",
"personality": "friendly",
"serviceName": "my_app_server_client", // optional metrics tag (`service_name`)
"sessionStartSource": "startup", // optional: "startup" (default) or "clear"
// Experimental: requires opt-in
"dynamicTools": [
{
Expand Down
13 changes: 11 additions & 2 deletions codex-rs/app-server/src/codex_message_processor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2083,6 +2083,7 @@ impl CodexMessageProcessor {
experimental_raw_events,
personality,
ephemeral,
session_start_source,
persist_extended_history,
} = params;
let mut typesafe_overrides = self.build_thread_config_overrides(
Expand Down Expand Up @@ -2124,6 +2125,7 @@ impl CodexMessageProcessor {
config,
typesafe_overrides,
dynamic_tools,
session_start_source,
persist_extended_history,
service_name,
experimental_raw_events,
Expand Down Expand Up @@ -2199,6 +2201,7 @@ impl CodexMessageProcessor {
config_overrides: Option<HashMap<String, serde_json::Value>>,
typesafe_overrides: ConfigOverrides,
dynamic_tools: Option<Vec<ApiDynamicToolSpec>>,
session_start_source: Option<codex_app_server_protocol::ThreadStartSource>,
persist_extended_history: bool,
service_name: Option<String>,
experimental_raw_events: bool,
Expand Down Expand Up @@ -2322,6 +2325,12 @@ impl CodexMessageProcessor {
.thread_manager
.start_thread_with_tools_and_service_name(
config,
match session_start_source
.unwrap_or(codex_app_server_protocol::ThreadStartSource::Startup)
{
codex_app_server_protocol::ThreadStartSource::Startup => InitialHistory::New,
codex_app_server_protocol::ThreadStartSource::Clear => InitialHistory::Cleared,
},
core_dynamic_tools,
persist_extended_history,
service_name,
Expand Down Expand Up @@ -4239,7 +4248,7 @@ impl CodexMessageProcessor {
thread.preview = preview_from_rollout_items(items);
Ok(thread)
}
InitialHistory::New => Err(format!(
InitialHistory::New | InitialHistory::Cleared => Err(format!(
"failed to build resume response for thread {thread_id}: initial history missing"
)),
};
Expand Down Expand Up @@ -8856,7 +8865,7 @@ pub(crate) async fn read_rollout_items_from_rollout(
path: &Path,
) -> std::io::Result<Vec<RolloutItem>> {
let items = match RolloutRecorder::get_rollout_history(path).await? {
InitialHistory::New => Vec::new(),
InitialHistory::New | InitialHistory::Cleared => Vec::new(),
InitialHistory::Forked(items) => items,
InitialHistory::Resumed(resumed) => resumed.history,
};
Expand Down
1 change: 1 addition & 0 deletions codex-rs/app-server/tests/suite/v2/skills_list.rs
Original file line number Diff line number Diff line change
Expand Up @@ -241,6 +241,7 @@ async fn skills_changed_notification_is_emitted_after_skill_change() -> Result<(
developer_instructions: None,
personality: None,
ephemeral: None,
session_start_source: None,
dynamic_tools: None,
mock_experimental_field: None,
experimental_raw_events: false,
Expand Down
11 changes: 6 additions & 5 deletions codex-rs/core/src/codex.rs
Original file line number Diff line number Diff line change
Expand Up @@ -591,7 +591,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) => {
Expand Down Expand Up @@ -1530,7 +1530,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,
Expand Down Expand Up @@ -1571,14 +1571,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.
Expand Down Expand Up @@ -2111,6 +2111,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.
Expand Down Expand Up @@ -2245,7 +2246,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)
Expand Down
7 changes: 5 additions & 2 deletions codex-rs/core/src/thread_manager.rs
Original file line number Diff line number Diff line change
Expand Up @@ -469,6 +469,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,
Expand All @@ -480,14 +481,15 @@ 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>,
parent_trace: Option<W3cTraceContext>,
) -> CodexResult<NewThread> {
Box::pin(self.state.spawn_thread(
config,
InitialHistory::New,
initial_history,
Arc::clone(&self.state.auth_manager),
self.agent_control(),
dynamic_tools,
Expand Down Expand Up @@ -663,6 +665,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),
};
Expand Down Expand Up @@ -1064,7 +1067,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,
]),
Expand Down
2 changes: 2 additions & 0 deletions codex-rs/hooks/src/events/session_start.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,15 @@ use crate::schema::SessionStartCommandInput;
pub enum SessionStartSource {
Startup,
Resume,
Clear,
}

impl SessionStartSource {
pub fn as_str(self) -> &'static str {
match self {
Self::Startup => "startup",
Self::Resume => "resume",
Self::Clear => "clear",
}
}
}
Expand Down
Loading
Loading