mirror of
https://github.com/openai/codex.git
synced 2026-11-12 21:20:34 +00:00
Allow searching persisted attached threads (#60574)
## Why Title and content searches exclude attached threads, preventing clients from finding their saved conversations alongside ordinary threads. ## What changed - Add `includeAttachedThreads` to `thread/list` and `thread/search`. For `thread/list`, require a non-empty `searchTerm` and preserve the usual provider and source filters. - Expose `attachedToThreadId` on thread responses to identify the owning parent independently of fork and subagent lineage. - Support inclusive searches through SQLite and filesystem fallback, repairing missing database entries with saved thread names during title searches. ## Testing Add regression coverage for opt-in title and content searches, attached-parent metadata after restart, default search filters, filesystem searches without SQLite, and discovery and repair of unindexed title matches. GitOrigin-RevId: ac12ec3c730ad7f1722bdf8e21600d17f12017f0
This commit is contained in:
@@ -2754,6 +2754,7 @@ impl ThreadRequestProcessor {
|
||||
params: ThreadListParams,
|
||||
) -> Result<ThreadListResponse, JSONRPCErrorError> {
|
||||
let ThreadListParams {
|
||||
include_attached_threads,
|
||||
current_account_only,
|
||||
cursor,
|
||||
limit,
|
||||
@@ -2815,6 +2816,15 @@ impl ThreadRequestProcessor {
|
||||
}
|
||||
}
|
||||
let cwd_filters = normalize_thread_list_cwd_filters(cwd)?;
|
||||
if include_attached_threads
|
||||
&& search_term
|
||||
.as_ref()
|
||||
.is_none_or(|term| term.trim().is_empty())
|
||||
{
|
||||
return Err(invalid_request(
|
||||
"includeAttachedThreads requires a non-empty searchTerm",
|
||||
));
|
||||
}
|
||||
let relation_filter = match (parent_thread_id, ancestor_thread_id, attached_to_thread_id) {
|
||||
(Some(_), Some(_), None) => {
|
||||
return Err(invalid_request(
|
||||
@@ -2850,6 +2860,9 @@ impl ThreadRequestProcessor {
|
||||
"attachedToThreadId cannot be combined with parentThreadId or ancestorThreadId",
|
||||
));
|
||||
}
|
||||
(None, None, None) if include_attached_threads => {
|
||||
Some(StoreThreadRelationFilter::IncludeAttached)
|
||||
}
|
||||
(None, None, None) => None,
|
||||
};
|
||||
|
||||
@@ -2926,6 +2939,7 @@ impl ThreadRequestProcessor {
|
||||
params: ThreadSearchParams,
|
||||
) -> Result<ThreadSearchResponse, JSONRPCErrorError> {
|
||||
let ThreadSearchParams {
|
||||
include_attached_threads,
|
||||
current_account_only,
|
||||
cursor,
|
||||
limit,
|
||||
@@ -2961,6 +2975,7 @@ impl ThreadRequestProcessor {
|
||||
let page = self
|
||||
.thread_store
|
||||
.search_threads(StoreSearchThreadsParams {
|
||||
include_attached_threads,
|
||||
account: account.clone(),
|
||||
page_size: remaining.min(THREAD_LIST_MAX_LIMIT),
|
||||
cursor: cursor_obj.clone(),
|
||||
@@ -6078,6 +6093,8 @@ impl ThreadRequestProcessor {
|
||||
let mut items = Vec::with_capacity(requested_page_size);
|
||||
let mut next_cursor: Option<String> = None;
|
||||
|
||||
let parent_scoped =
|
||||
relation_filter.is_some_and(StoreThreadRelationFilter::is_parent_scoped);
|
||||
let model_provider_filter = match model_providers {
|
||||
Some(providers) => {
|
||||
if providers.is_empty() {
|
||||
@@ -6086,15 +6103,14 @@ impl ThreadRequestProcessor {
|
||||
Some(providers)
|
||||
}
|
||||
}
|
||||
None if relation_filter.is_some() => None,
|
||||
None if parent_scoped => None,
|
||||
None => Some(vec![self.config.model_provider_id.clone()]),
|
||||
};
|
||||
let (allowed_sources_vec, source_kind_filter) =
|
||||
if relation_filter.is_some() && source_kinds.is_none() {
|
||||
(Vec::new(), None)
|
||||
} else {
|
||||
compute_source_filters(source_kinds)
|
||||
};
|
||||
let (allowed_sources_vec, source_kind_filter) = if parent_scoped && source_kinds.is_none() {
|
||||
(Vec::new(), None)
|
||||
} else {
|
||||
compute_source_filters(source_kinds)
|
||||
};
|
||||
let allowed_sources = allowed_sources_vec.as_slice();
|
||||
let store_sort_direction = match sort_direction {
|
||||
SortDirection::Asc => StoreSortDirection::Asc,
|
||||
@@ -6752,6 +6768,7 @@ fn thread_from_stored_thread(
|
||||
extra: None,
|
||||
session_id: thread_id,
|
||||
forked_from_id: thread.forked_from_id.map(|id| id.to_string()),
|
||||
attached_to_thread_id: thread.attached_to_thread_id.map(|id| id.to_string()),
|
||||
parent_thread_id: thread.parent_thread_id.map(|id| id.to_string()),
|
||||
preview: thread.preview,
|
||||
ephemeral: false,
|
||||
@@ -6952,6 +6969,7 @@ fn build_thread_from_snapshot(
|
||||
extra: None,
|
||||
session_id,
|
||||
forked_from_id: None,
|
||||
attached_to_thread_id: None,
|
||||
parent_thread_id: config_snapshot.parent_thread_id.map(|id| id.to_string()),
|
||||
preview: String::new(),
|
||||
ephemeral: config_snapshot.ephemeral,
|
||||
|
||||
Reference in New Issue
Block a user