({
activeWorkspace,
onWorkspaceConnected,
onDebug,
ensureWorkspaceRuntimeCodexArgs,
model,
effort,
serviceTier,
collaborationMode,
accessMode,
onSelectServiceTier,
reviewDeliveryMode = "inline",
steerEnabled = false,
threadTitleAutogenerationEnabled = false,
chatHistoryScrollbackItems,
customPrompts = [],
onMessageActivity,
threadSortKey = "updated_at",
onThreadCodexMetadataDetected,
}: UseThreadsOptions)
| 75 | const CASCADE_ARCHIVE_SKIP_TTL_MS = 120_000; |
| 76 | |
| 77 | export function useThreads({ |
| 78 | activeWorkspace, |
| 79 | onWorkspaceConnected, |
| 80 | onDebug, |
| 81 | ensureWorkspaceRuntimeCodexArgs, |
| 82 | model, |
| 83 | effort, |
| 84 | serviceTier, |
| 85 | collaborationMode, |
| 86 | accessMode, |
| 87 | onSelectServiceTier, |
| 88 | reviewDeliveryMode = "inline", |
| 89 | steerEnabled = false, |
| 90 | threadTitleAutogenerationEnabled = false, |
| 91 | chatHistoryScrollbackItems, |
| 92 | customPrompts = [], |
| 93 | onMessageActivity, |
| 94 | threadSortKey = "updated_at", |
| 95 | onThreadCodexMetadataDetected, |
| 96 | }: UseThreadsOptions) { |
| 97 | const maxItemsPerThread = |
| 98 | chatHistoryScrollbackItems === undefined |
| 99 | ? CHAT_SCROLLBACK_DEFAULT |
| 100 | : chatHistoryScrollbackItems; |
| 101 | |
| 102 | const [state, dispatch] = useReducer( |
| 103 | threadReducer, |
| 104 | maxItemsPerThread, |
| 105 | (initialMaxItemsPerThread) => ({ |
| 106 | ...initialState, |
| 107 | maxItemsPerThread: initialMaxItemsPerThread, |
| 108 | }), |
| 109 | ); |
| 110 | useEffect(() => { |
| 111 | dispatch({ type: "setMaxItemsPerThread", maxItemsPerThread }); |
| 112 | }, [dispatch, maxItemsPerThread]); |
| 113 | const loadedThreadsRef = useRef<Record<string, boolean>>({}); |
| 114 | const replaceOnResumeRef = useRef<Record<string, boolean>>({}); |
| 115 | const pendingInterruptsRef = useRef<Set<string>>(new Set()); |
| 116 | const planByThreadRef = useRef(state.planByThread); |
| 117 | const itemsByThreadRef = useRef(state.itemsByThread); |
| 118 | const threadsByWorkspaceRef = useRef(state.threadsByWorkspace); |
| 119 | const activeTurnIdByThreadRef = useRef(state.activeTurnIdByThread); |
| 120 | const subagentThreadByWorkspaceThreadRef = useRef<Record<string, true>>({}); |
| 121 | const threadParentByIdRef = useRef(state.threadParentById); |
| 122 | const cascadeArchiveSkipRef = useRef<Record<string, number>>({}); |
| 123 | const subagentHydrationInFlightRef = useRef<Record<string, true>>({}); |
| 124 | planByThreadRef.current = state.planByThread; |
| 125 | itemsByThreadRef.current = state.itemsByThread; |
| 126 | threadsByWorkspaceRef.current = state.threadsByWorkspace; |
| 127 | activeTurnIdByThreadRef.current = state.activeTurnIdByThread; |
| 128 | threadParentByIdRef.current = state.threadParentById; |
| 129 | const rateLimitsByWorkspaceRef = useRef(state.rateLimitsByWorkspace); |
| 130 | rateLimitsByWorkspaceRef.current = state.rateLimitsByWorkspace; |
| 131 | const { approvalAllowlistRef, handleApprovalDecision, handleApprovalRemember } = |
| 132 | useThreadApprovals({ dispatch, onDebug }); |
| 133 | const { handleUserInputSubmit } = useThreadUserInput({ dispatch }); |
| 134 | const { |
no test coverage detected