mirror of
https://github.com/openai/codex.git
synced 2026-11-14 14:49:31 +00:00
Add app-server APIs for stored thread attachments (#44564)
## What changed - Add `thread/attachment/add`, `thread/attachment/list`, and `thread/attachment/remove` to manage durable resource references without loading the owning thread or changing conversation history. - Identify attachments by thread, `attachmentType`, and `identityKey`. Repeated adds return the existing attachment; repeated removals succeed without emitting another update. Listing supports cursor pagination. - Broadcast `thread/attachment/updated` after creation or deletion, after responding to the requester. Serialize attachment mutations with thread lifecycle operations and reject stores that do not support attachments. - Update protocol schemas, TypeScript and Python bindings, documentation, and TUI notification handling so attachment updates do not add untracked threads to the agent picker. ## Testing Add coverage for unloaded threads, pagination, idempotent addition and removal, reattachment, invalid inputs, unsupported stores, response-before-notification ordering across clients, and TUI notification routing. GitOrigin-RevId: 0d29cf903afd4e931a70a9cf96300475213b34aa
This commit is contained in:
1 parent
94697375cb
commit
3319d9b296
47 files changed
+2341
-6
No files matched your search
@@ -670,6 +670,21 @@ client_request_definitions! {
|
||||
serialization: thread_id(params.thread_id),
|
||||
response: v2::ThreadMetadataUpdateResponse,
|
||||
},
|
||||
ThreadAttachmentAdd => "thread/attachment/add" {
|
||||
params: v2::ThreadAttachmentAddParams,
|
||||
serialization: thread_id(params.thread_id),
|
||||
response: v2::ThreadAttachmentAddResponse,
|
||||
},
|
||||
ThreadAttachmentList => "thread/attachment/list" {
|
||||
params: v2::ThreadAttachmentListParams,
|
||||
serialization: None,
|
||||
response: v2::ThreadAttachmentListResponse,
|
||||
},
|
||||
ThreadAttachmentRemove => "thread/attachment/remove" {
|
||||
params: v2::ThreadAttachmentRemoveParams,
|
||||
serialization: thread_id(params.thread_id),
|
||||
response: v2::ThreadAttachmentRemoveResponse,
|
||||
},
|
||||
ThreadSectionMove => "thread/section/move" {
|
||||
params: v2::ThreadSectionMoveParams,
|
||||
serialization: thread_id(params.thread_id),
|
||||
@@ -1901,6 +1916,7 @@ server_notification_definitions! {
|
||||
ThreadReverted => "thread/reverted" (v2::ThreadRevertedNotification),
|
||||
SkillsChanged => "skills/changed" (v2::SkillsChangedNotification),
|
||||
ThreadNameUpdated => "thread/name/updated" (v2::ThreadNameUpdatedNotification),
|
||||
ThreadAttachmentUpdated => "thread/attachment/updated" (v2::ThreadAttachmentUpdatedNotification),
|
||||
ThreadGoalUpdated => "thread/goal/updated" (v2::ThreadGoalUpdatedNotification),
|
||||
ThreadGoalCleared => "thread/goal/cleared" (v2::ThreadGoalClearedNotification),
|
||||
#[experimental("thread/queue/changed")]
|
||||
|
||||
@@ -31,6 +31,7 @@ mod realtime;
|
||||
mod remote_control;
|
||||
mod review;
|
||||
mod thread;
|
||||
mod thread_attachment;
|
||||
mod thread_data;
|
||||
mod thread_usage;
|
||||
mod turn;
|
||||
@@ -69,6 +70,7 @@ pub use remote_control::*;
|
||||
pub use review::*;
|
||||
pub use shared::*;
|
||||
pub use thread::*;
|
||||
pub use thread_attachment::*;
|
||||
pub use thread_data::*;
|
||||
pub use thread_usage::*;
|
||||
pub use turn::*;
|
||||
|
||||
@@ -0,0 +1,107 @@
|
||||
//! Wire contracts for managing the current set of attachments on a thread.
|
||||
|
||||
use crate::JsonSchema;
|
||||
use crate::TS;
|
||||
use serde::Deserialize;
|
||||
use serde::Serialize;
|
||||
use serde_json::Value as JsonValue;
|
||||
|
||||
/// An independently persisted attachment associated with a thread.
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
#[ts(export_to = "v2/")]
|
||||
pub struct ThreadAttachment {
|
||||
pub id: String,
|
||||
pub attachment_type: String,
|
||||
pub identity_key: String,
|
||||
pub payload: JsonValue,
|
||||
#[ts(type = "number")]
|
||||
pub created_at: i64,
|
||||
}
|
||||
|
||||
/// Parameters for creating or locating an attachment on its owning thread.
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
#[ts(export_to = "v2/")]
|
||||
pub struct ThreadAttachmentAddParams {
|
||||
pub thread_id: String,
|
||||
pub attachment_type: String,
|
||||
pub identity_key: String,
|
||||
pub payload: JsonValue,
|
||||
}
|
||||
|
||||
/// Result of attempting to associate an attachment with a thread.
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq, JsonSchema, TS)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
#[ts(rename_all = "camelCase", export_to = "v2/")]
|
||||
pub enum ThreadAttachmentAddOutcome {
|
||||
Created,
|
||||
Existing,
|
||||
}
|
||||
|
||||
/// The created or existing attachment.
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
#[ts(export_to = "v2/")]
|
||||
pub struct ThreadAttachmentAddResponse {
|
||||
pub outcome: ThreadAttachmentAddOutcome,
|
||||
pub attachment: ThreadAttachment,
|
||||
}
|
||||
|
||||
/// Parameters for listing attachments from one thread.
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, Default, PartialEq, Eq, JsonSchema, TS)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
#[ts(export_to = "v2/")]
|
||||
pub struct ThreadAttachmentListParams {
|
||||
pub thread_id: String,
|
||||
#[ts(optional = nullable)]
|
||||
pub cursor: Option<String>,
|
||||
#[ts(optional = nullable)]
|
||||
pub limit: Option<u32>,
|
||||
}
|
||||
|
||||
/// One page of attachments associated with the requested thread.
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
#[ts(export_to = "v2/")]
|
||||
pub struct ThreadAttachmentListResponse {
|
||||
pub data: Vec<ThreadAttachment>,
|
||||
pub next_cursor: Option<String>,
|
||||
}
|
||||
|
||||
/// Parameters for deleting an attachment by its stable thread-local identity.
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, JsonSchema, TS)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
#[ts(export_to = "v2/")]
|
||||
pub struct ThreadAttachmentRemoveParams {
|
||||
pub thread_id: String,
|
||||
pub attachment_type: String,
|
||||
pub identity_key: String,
|
||||
}
|
||||
|
||||
/// Successful deletion does not return additional attachment data.
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, Default, PartialEq, Eq, JsonSchema, TS)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
#[ts(export_to = "v2/")]
|
||||
pub struct ThreadAttachmentRemoveResponse {}
|
||||
|
||||
/// The persisted attachment change represented by a notification.
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq, JsonSchema, TS)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
#[ts(rename_all = "camelCase", export_to = "v2/")]
|
||||
pub enum ThreadAttachmentOperation {
|
||||
Created,
|
||||
Deleted,
|
||||
}
|
||||
|
||||
/// Notification published after a thread attachment is created or deleted.
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, JsonSchema, TS)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
#[ts(export_to = "v2/")]
|
||||
pub struct ThreadAttachmentUpdatedNotification {
|
||||
pub thread_id: String,
|
||||
pub attachment_type: String,
|
||||
pub identity_key: String,
|
||||
pub attachment_id: String,
|
||||
pub operation: ThreadAttachmentOperation,
|
||||
}
|
||||
Reference in new issue
Block a user