mirror of
https://github.com/pchuan98/codex.git
synced 2026-07-01 00:31:56 +08:00
core: add extra metadata field to Thread struct (#29675)
# Summary Adds a field Thread.extras that can be used to hold arbitrary metadata specific to a given thread.
This commit is contained in:
committed by
GitHub
Unverified
parent
2cf2a6a844
commit
354807920e
@@ -177,6 +177,7 @@ fn sample_thread_with_metadata(
|
||||
) -> Thread {
|
||||
Thread {
|
||||
id: thread_id.to_string(),
|
||||
extra: None,
|
||||
session_id: format!("session-{thread_id}"),
|
||||
forked_from_id: None,
|
||||
parent_thread_id,
|
||||
|
||||
@@ -277,6 +277,7 @@ fn sample_thread_archive_request() -> ClientRequest {
|
||||
fn sample_thread(thread_id: &str) -> Thread {
|
||||
Thread {
|
||||
id: thread_id.to_string(),
|
||||
extra: None,
|
||||
session_id: format!("session-{thread_id}"),
|
||||
forked_from_id: None,
|
||||
parent_thread_id: None,
|
||||
|
||||
@@ -3765,6 +3765,10 @@
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
"ThreadExtra": {
|
||||
"description": "Extra app-server data for a thread.",
|
||||
"type": "object"
|
||||
},
|
||||
"ThreadGoal": {
|
||||
"properties": {
|
||||
"createdAt": {
|
||||
|
||||
+4
@@ -17052,6 +17052,10 @@
|
||||
"title": "ThreadDeletedNotification",
|
||||
"type": "object"
|
||||
},
|
||||
"ThreadExtra": {
|
||||
"description": "Extra app-server data for a thread.",
|
||||
"type": "object"
|
||||
},
|
||||
"ThreadForkParams": {
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"description": "There are two ways to fork a thread: 1. By thread_id: load the thread from disk by thread_id and fork it into a new thread. 2. By path: load the thread from disk by path and fork it into a new thread.\n\nIf using a non-empty path, the thread_id param will be ignored. Empty string path values are treated as absent.\n\nPrefer using thread_id whenever possible.",
|
||||
|
||||
+4
@@ -14831,6 +14831,10 @@
|
||||
"title": "ThreadDeletedNotification",
|
||||
"type": "object"
|
||||
},
|
||||
"ThreadExtra": {
|
||||
"description": "Extra app-server data for a thread.",
|
||||
"type": "object"
|
||||
},
|
||||
"ThreadForkParams": {
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"description": "There are two ways to fork a thread: 1. By thread_id: load the thread from disk by thread_id and fork it into a new thread. 2. By path: load the thread from disk by path and fork it into a new thread.\n\nIf using a non-empty path, the thread_id param will be ignored. Empty string path values are treated as absent.\n\nPrefer using thread_id whenever possible.",
|
||||
|
||||
@@ -1165,6 +1165,10 @@
|
||||
],
|
||||
"type": "string"
|
||||
},
|
||||
"ThreadExtra": {
|
||||
"description": "Extra app-server data for a thread.",
|
||||
"type": "object"
|
||||
},
|
||||
"ThreadId": {
|
||||
"type": "string"
|
||||
},
|
||||
|
||||
@@ -972,6 +972,10 @@
|
||||
],
|
||||
"type": "string"
|
||||
},
|
||||
"ThreadExtra": {
|
||||
"description": "Extra app-server data for a thread.",
|
||||
"type": "object"
|
||||
},
|
||||
"ThreadId": {
|
||||
"type": "string"
|
||||
},
|
||||
|
||||
+4
@@ -972,6 +972,10 @@
|
||||
],
|
||||
"type": "string"
|
||||
},
|
||||
"ThreadExtra": {
|
||||
"description": "Extra app-server data for a thread.",
|
||||
"type": "object"
|
||||
},
|
||||
"ThreadId": {
|
||||
"type": "string"
|
||||
},
|
||||
|
||||
@@ -972,6 +972,10 @@
|
||||
],
|
||||
"type": "string"
|
||||
},
|
||||
"ThreadExtra": {
|
||||
"description": "Extra app-server data for a thread.",
|
||||
"type": "object"
|
||||
},
|
||||
"ThreadId": {
|
||||
"type": "string"
|
||||
},
|
||||
|
||||
@@ -1165,6 +1165,10 @@
|
||||
],
|
||||
"type": "string"
|
||||
},
|
||||
"ThreadExtra": {
|
||||
"description": "Extra app-server data for a thread.",
|
||||
"type": "object"
|
||||
},
|
||||
"ThreadId": {
|
||||
"type": "string"
|
||||
},
|
||||
|
||||
@@ -972,6 +972,10 @@
|
||||
],
|
||||
"type": "string"
|
||||
},
|
||||
"ThreadExtra": {
|
||||
"description": "Extra app-server data for a thread.",
|
||||
"type": "object"
|
||||
},
|
||||
"ThreadId": {
|
||||
"type": "string"
|
||||
},
|
||||
|
||||
@@ -1165,6 +1165,10 @@
|
||||
],
|
||||
"type": "string"
|
||||
},
|
||||
"ThreadExtra": {
|
||||
"description": "Extra app-server data for a thread.",
|
||||
"type": "object"
|
||||
},
|
||||
"ThreadId": {
|
||||
"type": "string"
|
||||
},
|
||||
|
||||
@@ -972,6 +972,10 @@
|
||||
],
|
||||
"type": "string"
|
||||
},
|
||||
"ThreadExtra": {
|
||||
"description": "Extra app-server data for a thread.",
|
||||
"type": "object"
|
||||
},
|
||||
"ThreadId": {
|
||||
"type": "string"
|
||||
},
|
||||
|
||||
@@ -972,6 +972,10 @@
|
||||
],
|
||||
"type": "string"
|
||||
},
|
||||
"ThreadExtra": {
|
||||
"description": "Extra app-server data for a thread.",
|
||||
"type": "object"
|
||||
},
|
||||
"ThreadId": {
|
||||
"type": "string"
|
||||
},
|
||||
|
||||
+22
-43
@@ -8,91 +8,70 @@ import type { ThreadSource } from "./ThreadSource";
|
||||
import type { ThreadStatus } from "./ThreadStatus";
|
||||
import type { Turn } from "./Turn";
|
||||
|
||||
export type Thread = {
|
||||
/**
|
||||
export type Thread = {/**
|
||||
* Identifier for this thread. Codex-generated thread IDs are UUIDv7.
|
||||
*/
|
||||
id: string,
|
||||
/**
|
||||
id: string, /**
|
||||
* Session id shared by threads that belong to the same session tree.
|
||||
*/
|
||||
sessionId: string,
|
||||
/**
|
||||
sessionId: string, /**
|
||||
* Source thread id when this thread was created by forking another thread.
|
||||
*/
|
||||
forkedFromId: string | null,
|
||||
/**
|
||||
forkedFromId: string | null, /**
|
||||
* The ID of the parent thread. This will only be set if this thread is a subagent.
|
||||
*/
|
||||
parentThreadId: string | null,
|
||||
/**
|
||||
parentThreadId: string | null, /**
|
||||
* Usually the first user message in the thread, if available.
|
||||
*/
|
||||
preview: string,
|
||||
/**
|
||||
preview: string, /**
|
||||
* Whether the thread is ephemeral and should not be materialized on disk.
|
||||
*/
|
||||
ephemeral: boolean,
|
||||
/**
|
||||
ephemeral: boolean, /**
|
||||
* Model provider used for this thread (for example, 'openai').
|
||||
*/
|
||||
modelProvider: string,
|
||||
/**
|
||||
modelProvider: string, /**
|
||||
* Unix timestamp (in seconds) when the thread was created.
|
||||
*/
|
||||
createdAt: number,
|
||||
/**
|
||||
createdAt: number, /**
|
||||
* Unix timestamp (in seconds) when the thread was last updated.
|
||||
*/
|
||||
updatedAt: number,
|
||||
/**
|
||||
updatedAt: number, /**
|
||||
* Unix timestamp (in seconds) used for thread recency ordering.
|
||||
*/
|
||||
recencyAt: number | null,
|
||||
/**
|
||||
recencyAt: number | null, /**
|
||||
* Current runtime status for the thread.
|
||||
*/
|
||||
status: ThreadStatus,
|
||||
/**
|
||||
status: ThreadStatus, /**
|
||||
* [UNSTABLE] Path to the thread on disk.
|
||||
*/
|
||||
path: string | null,
|
||||
/**
|
||||
path: string | null, /**
|
||||
* Working directory captured for the thread.
|
||||
*/
|
||||
cwd: AbsolutePathBuf,
|
||||
/**
|
||||
cwd: AbsolutePathBuf, /**
|
||||
* Version of the CLI that created the thread.
|
||||
*/
|
||||
cliVersion: string,
|
||||
/**
|
||||
cliVersion: string, /**
|
||||
* Origin of the thread (CLI, VSCode, codex exec, codex app-server, etc.).
|
||||
*/
|
||||
source: SessionSource,
|
||||
/**
|
||||
source: SessionSource, /**
|
||||
* Optional analytics source classification for this thread.
|
||||
*/
|
||||
threadSource: ThreadSource | null,
|
||||
/**
|
||||
threadSource: ThreadSource | null, /**
|
||||
* Optional random unique nickname assigned to an AgentControl-spawned sub-agent.
|
||||
*/
|
||||
agentNickname: string | null,
|
||||
/**
|
||||
agentNickname: string | null, /**
|
||||
* Optional role (agent_role) assigned to an AgentControl-spawned sub-agent.
|
||||
*/
|
||||
agentRole: string | null,
|
||||
/**
|
||||
agentRole: string | null, /**
|
||||
* Optional Git metadata captured when the thread was created.
|
||||
*/
|
||||
gitInfo: GitInfo | null,
|
||||
/**
|
||||
gitInfo: GitInfo | null, /**
|
||||
* Optional user-facing thread title.
|
||||
*/
|
||||
name: string | null,
|
||||
/**
|
||||
name: string | null, /**
|
||||
* Only populated on `thread/resume`, `thread/rollback`, `thread/fork`, and `thread/read`
|
||||
* (when `includeTurns` is true) responses.
|
||||
* For all other responses and notifications returning a Thread,
|
||||
* the turns field will be an empty list.
|
||||
*/
|
||||
turns: Array<Turn>, };
|
||||
turns: Array<Turn>};
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
// 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.
|
||||
|
||||
/**
|
||||
* Extra app-server data for a thread.
|
||||
*/
|
||||
export type ThreadExtra = Record<string, never>;
|
||||
@@ -395,6 +395,7 @@ export type { ThreadCompactStartResponse } from "./ThreadCompactStartResponse";
|
||||
export type { ThreadDeleteParams } from "./ThreadDeleteParams";
|
||||
export type { ThreadDeleteResponse } from "./ThreadDeleteResponse";
|
||||
export type { ThreadDeletedNotification } from "./ThreadDeletedNotification";
|
||||
export type { ThreadExtra } from "./ThreadExtra";
|
||||
export type { ThreadForkParams } from "./ThreadForkParams";
|
||||
export type { ThreadForkResponse } from "./ThreadForkResponse";
|
||||
export type { ThreadGoal } from "./ThreadGoal";
|
||||
|
||||
@@ -2573,6 +2573,7 @@ mod tests {
|
||||
response: v2::ThreadStartResponse {
|
||||
thread: v2::Thread {
|
||||
id: "67e55044-10b1-426f-9247-bb680e5fe0c8".to_string(),
|
||||
extra: None,
|
||||
session_id: "67e55044-10b1-426f-9247-bb680e5fe0c7".to_string(),
|
||||
forked_from_id: None,
|
||||
parent_thread_id: None,
|
||||
@@ -2622,6 +2623,7 @@ mod tests {
|
||||
"response": {
|
||||
"thread": {
|
||||
"id": "67e55044-10b1-426f-9247-bb680e5fe0c8",
|
||||
"extra": null,
|
||||
"sessionId": "67e55044-10b1-426f-9247-bb680e5fe0c7",
|
||||
"forkedFromId": null,
|
||||
"parentThreadId": null,
|
||||
|
||||
@@ -168,6 +168,7 @@ fn thread_resume_response_round_trips_initial_turns_page() {
|
||||
let response = ThreadResumeResponse {
|
||||
thread: Thread {
|
||||
id: "thr_123".to_string(),
|
||||
extra: None,
|
||||
session_id: "thr_123".to_string(),
|
||||
forked_from_id: None,
|
||||
parent_thread_id: None,
|
||||
|
||||
@@ -2,6 +2,7 @@ use super::CodexErrorInfo;
|
||||
use super::ThreadItem;
|
||||
use super::ThreadStatus;
|
||||
use super::TurnStatus;
|
||||
use codex_experimental_api_macros::ExperimentalApi;
|
||||
use codex_protocol::protocol::SessionSource as CoreSessionSource;
|
||||
use codex_protocol::protocol::SubAgentSource as CoreSubAgentSource;
|
||||
use codex_protocol::protocol::ThreadSource as CoreThreadSource;
|
||||
@@ -120,6 +121,12 @@ impl From<ThreadSource> for CoreThreadSource {
|
||||
}
|
||||
}
|
||||
|
||||
/// Extra app-server data for a thread.
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, JsonSchema, TS)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
#[ts(rename_all = "camelCase", export_to = "v2/")]
|
||||
pub struct ThreadExtra {}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, JsonSchema, TS)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
#[ts(export_to = "v2/")]
|
||||
@@ -129,12 +136,15 @@ pub struct GitInfo {
|
||||
pub origin_url: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS, ExperimentalApi)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
#[ts(export_to = "v2/")]
|
||||
pub struct Thread {
|
||||
/// Identifier for this thread. Codex-generated thread IDs are UUIDv7.
|
||||
pub id: String,
|
||||
/// Optional implementation-specific thread data.
|
||||
#[experimental("thread.extra")]
|
||||
pub extra: Option<ThreadExtra>,
|
||||
/// Session id shared by threads that belong to the same session tree.
|
||||
pub session_id: String,
|
||||
/// Source thread id when this thread was created by forking another thread.
|
||||
|
||||
@@ -4156,6 +4156,7 @@ pub(crate) fn thread_from_stored_thread(
|
||||
let thread_id = thread.thread_id.to_string();
|
||||
let thread = Thread {
|
||||
id: thread_id.clone(),
|
||||
extra: None,
|
||||
session_id: thread_id,
|
||||
forked_from_id: thread.forked_from_id.map(|id| id.to_string()),
|
||||
parent_thread_id: thread.parent_thread_id.map(|id| id.to_string()),
|
||||
@@ -4366,6 +4367,7 @@ fn build_thread_from_snapshot(
|
||||
let now = time::OffsetDateTime::now_utc().unix_timestamp();
|
||||
Thread {
|
||||
id: thread_id.to_string(),
|
||||
extra: None,
|
||||
session_id,
|
||||
forked_from_id: None,
|
||||
parent_thread_id: config_snapshot.parent_thread_id.map(|id| id.to_string()),
|
||||
|
||||
@@ -183,6 +183,7 @@ mod tests {
|
||||
fn test_thread(items: Vec<ThreadItem>) -> Thread {
|
||||
Thread {
|
||||
id: "thread-1".to_string(),
|
||||
extra: None,
|
||||
session_id: "session-1".to_string(),
|
||||
forked_from_id: None,
|
||||
parent_thread_id: None,
|
||||
|
||||
@@ -314,6 +314,7 @@ pub(crate) fn summary_to_thread(
|
||||
let thread_id = conversation_id.to_string();
|
||||
Thread {
|
||||
id: thread_id.clone(),
|
||||
extra: None,
|
||||
session_id: thread_id,
|
||||
forked_from_id: None,
|
||||
parent_thread_id: None,
|
||||
|
||||
@@ -889,6 +889,7 @@ mod tests {
|
||||
fn test_thread(thread_id: &str, source: codex_app_server_protocol::SessionSource) -> Thread {
|
||||
Thread {
|
||||
id: thread_id.to_string(),
|
||||
extra: None,
|
||||
session_id: thread_id.to_string(),
|
||||
forked_from_id: None,
|
||||
parent_thread_id: None,
|
||||
|
||||
@@ -334,6 +334,7 @@ async fn resume_lookup_model_providers_filters_only_last_lookup() {
|
||||
fn turn_items_for_thread_returns_matching_turn_items() {
|
||||
let thread = AppServerThread {
|
||||
id: "thread-1".to_string(),
|
||||
extra: None,
|
||||
session_id: "thread-1".to_string(),
|
||||
forked_from_id: None,
|
||||
parent_thread_id: None,
|
||||
@@ -753,6 +754,7 @@ fn sample_thread_start_response() -> ThreadStartResponse {
|
||||
ThreadStartResponse {
|
||||
thread: codex_app_server_protocol::Thread {
|
||||
id: "67e55044-10b1-426f-9247-bb680e5fe0c8".to_string(),
|
||||
extra: None,
|
||||
session_id: "67e55044-10b1-426f-9247-bb680e5fe0c7".to_string(),
|
||||
forked_from_id: None,
|
||||
parent_thread_id: None,
|
||||
|
||||
@@ -128,6 +128,7 @@ mod tests {
|
||||
fn test_thread(thread_id: ThreadId, source: SessionSource) -> Thread {
|
||||
Thread {
|
||||
id: thread_id.to_string(),
|
||||
extra: None,
|
||||
session_id: thread_id.to_string(),
|
||||
forked_from_id: None,
|
||||
parent_thread_id: None,
|
||||
|
||||
@@ -2874,6 +2874,7 @@ async fn inactive_thread_started_notification_initializes_replay_session() -> Re
|
||||
ServerNotification::ThreadStarted(ThreadStartedNotification {
|
||||
thread: Thread {
|
||||
id: agent_thread_id.to_string(),
|
||||
extra: None,
|
||||
session_id: agent_thread_id.to_string(),
|
||||
forked_from_id: None,
|
||||
parent_thread_id: None,
|
||||
@@ -2967,6 +2968,7 @@ async fn inactive_thread_started_notification_preserves_primary_model_when_path_
|
||||
ServerNotification::ThreadStarted(ThreadStartedNotification {
|
||||
thread: Thread {
|
||||
id: agent_thread_id.to_string(),
|
||||
extra: None,
|
||||
session_id: agent_thread_id.to_string(),
|
||||
forked_from_id: None,
|
||||
parent_thread_id: None,
|
||||
@@ -3027,6 +3029,7 @@ async fn thread_read_session_state_does_not_reuse_primary_permission_profile() {
|
||||
|
||||
let thread = Thread {
|
||||
id: read_thread_id.to_string(),
|
||||
extra: None,
|
||||
session_id: read_thread_id.to_string(),
|
||||
forked_from_id: None,
|
||||
parent_thread_id: None,
|
||||
@@ -5640,6 +5643,7 @@ async fn thread_rollback_response_discards_queued_active_thread_events() {
|
||||
&ThreadRollbackResponse {
|
||||
thread: Thread {
|
||||
id: thread_id.to_string(),
|
||||
extra: None,
|
||||
session_id: thread_id.to_string(),
|
||||
forked_from_id: None,
|
||||
parent_thread_id: None,
|
||||
|
||||
@@ -409,6 +409,7 @@ mod tests {
|
||||
};
|
||||
let read_thread = Thread {
|
||||
id: read_thread_id.to_string(),
|
||||
extra: None,
|
||||
session_id: read_thread_id.to_string(),
|
||||
forked_from_id: None,
|
||||
parent_thread_id: None,
|
||||
|
||||
@@ -2320,6 +2320,7 @@ mod tests {
|
||||
let response = ThreadResumeResponse {
|
||||
thread: codex_app_server_protocol::Thread {
|
||||
id: thread_id.to_string(),
|
||||
extra: None,
|
||||
session_id: ThreadId::new().to_string(),
|
||||
forked_from_id: Some(forked_from_id.to_string()),
|
||||
parent_thread_id: None,
|
||||
|
||||
@@ -5720,6 +5720,7 @@ session_picker_view = "dense"
|
||||
let thread_id = ThreadId::new();
|
||||
let thread = Thread {
|
||||
id: thread_id.to_string(),
|
||||
extra: None,
|
||||
session_id: thread_id.to_string(),
|
||||
forked_from_id: None,
|
||||
parent_thread_id: None,
|
||||
@@ -5756,6 +5757,7 @@ session_picker_view = "dense"
|
||||
let thread_id = ThreadId::new();
|
||||
let thread = Thread {
|
||||
id: thread_id.to_string(),
|
||||
extra: None,
|
||||
session_id: thread_id.to_string(),
|
||||
forked_from_id: None,
|
||||
parent_thread_id: None,
|
||||
@@ -5826,6 +5828,7 @@ session_picker_view = "dense"
|
||||
let thread_id = ThreadId::new();
|
||||
let thread = Thread {
|
||||
id: thread_id.to_string(),
|
||||
extra: None,
|
||||
session_id: thread_id.to_string(),
|
||||
forked_from_id: None,
|
||||
parent_thread_id: None,
|
||||
@@ -5885,6 +5888,7 @@ session_picker_view = "dense"
|
||||
let thread_id = ThreadId::new();
|
||||
let thread = Thread {
|
||||
id: thread_id.to_string(),
|
||||
extra: None,
|
||||
session_id: thread_id.to_string(),
|
||||
forked_from_id: None,
|
||||
parent_thread_id: None,
|
||||
|
||||
Reference in New Issue
Block a user