better-codex 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (405) hide show
  1. package/README.md +26 -0
  2. package/apps/backend/README.md +46 -0
  3. package/apps/backend/bun.lock +64 -0
  4. package/apps/backend/package.json +18 -0
  5. package/apps/backend/scripts/generate-protocol.ts +32 -0
  6. package/apps/backend/src/analytics/service.ts +219 -0
  7. package/apps/backend/src/analytics/store.ts +284 -0
  8. package/apps/backend/src/config.ts +98 -0
  9. package/apps/backend/src/core/app-server.ts +131 -0
  10. package/apps/backend/src/core/jsonrpc.ts +166 -0
  11. package/apps/backend/src/protocol/AbsolutePathBuf.ts +14 -0
  12. package/apps/backend/src/protocol/AddConversationListenerParams.ts +6 -0
  13. package/apps/backend/src/protocol/AddConversationSubscriptionResponse.ts +5 -0
  14. package/apps/backend/src/protocol/AgentMessageContent.ts +5 -0
  15. package/apps/backend/src/protocol/AgentMessageContentDeltaEvent.ts +5 -0
  16. package/apps/backend/src/protocol/AgentMessageDeltaEvent.ts +5 -0
  17. package/apps/backend/src/protocol/AgentMessageEvent.ts +5 -0
  18. package/apps/backend/src/protocol/AgentMessageItem.ts +6 -0
  19. package/apps/backend/src/protocol/AgentReasoningDeltaEvent.ts +5 -0
  20. package/apps/backend/src/protocol/AgentReasoningEvent.ts +5 -0
  21. package/apps/backend/src/protocol/AgentReasoningRawContentDeltaEvent.ts +5 -0
  22. package/apps/backend/src/protocol/AgentReasoningRawContentEvent.ts +5 -0
  23. package/apps/backend/src/protocol/AgentReasoningSectionBreakEvent.ts +5 -0
  24. package/apps/backend/src/protocol/Annotations.ts +9 -0
  25. package/apps/backend/src/protocol/ApplyPatchApprovalParams.ts +21 -0
  26. package/apps/backend/src/protocol/ApplyPatchApprovalRequestEvent.ts +23 -0
  27. package/apps/backend/src/protocol/ApplyPatchApprovalResponse.ts +6 -0
  28. package/apps/backend/src/protocol/ArchiveConversationParams.ts +6 -0
  29. package/apps/backend/src/protocol/ArchiveConversationResponse.ts +5 -0
  30. package/apps/backend/src/protocol/AskForApproval.ts +9 -0
  31. package/apps/backend/src/protocol/AudioContent.ts +9 -0
  32. package/apps/backend/src/protocol/AuthMode.ts +5 -0
  33. package/apps/backend/src/protocol/AuthStatusChangeNotification.ts +9 -0
  34. package/apps/backend/src/protocol/BackgroundEventEvent.ts +5 -0
  35. package/apps/backend/src/protocol/BlobResourceContents.ts +5 -0
  36. package/apps/backend/src/protocol/CallToolResult.ts +10 -0
  37. package/apps/backend/src/protocol/CancelLoginChatGptParams.ts +5 -0
  38. package/apps/backend/src/protocol/CancelLoginChatGptResponse.ts +5 -0
  39. package/apps/backend/src/protocol/ClientInfo.ts +5 -0
  40. package/apps/backend/src/protocol/ClientNotification.ts +5 -0
  41. package/apps/backend/src/protocol/ClientRequest.ts +46 -0
  42. package/apps/backend/src/protocol/CodexErrorInfo.ts +8 -0
  43. package/apps/backend/src/protocol/ContentBlock.ts +10 -0
  44. package/apps/backend/src/protocol/ContentItem.ts +5 -0
  45. package/apps/backend/src/protocol/ContextCompactedEvent.ts +5 -0
  46. package/apps/backend/src/protocol/ConversationGitInfo.ts +5 -0
  47. package/apps/backend/src/protocol/ConversationId.ts +5 -0
  48. package/apps/backend/src/protocol/ConversationSummary.ts +8 -0
  49. package/apps/backend/src/protocol/CreditsSnapshot.ts +5 -0
  50. package/apps/backend/src/protocol/CustomPrompt.ts +5 -0
  51. package/apps/backend/src/protocol/DeprecationNoticeEvent.ts +13 -0
  52. package/apps/backend/src/protocol/ElicitationRequestEvent.ts +6 -0
  53. package/apps/backend/src/protocol/EmbeddedResource.ts +13 -0
  54. package/apps/backend/src/protocol/EmbeddedResourceResource.ts +7 -0
  55. package/apps/backend/src/protocol/ErrorEvent.ts +6 -0
  56. package/apps/backend/src/protocol/EventMsg.ts +60 -0
  57. package/apps/backend/src/protocol/ExecApprovalRequestEvent.ts +32 -0
  58. package/apps/backend/src/protocol/ExecCommandApprovalParams.ts +12 -0
  59. package/apps/backend/src/protocol/ExecCommandApprovalResponse.ts +6 -0
  60. package/apps/backend/src/protocol/ExecCommandBeginEvent.ts +35 -0
  61. package/apps/backend/src/protocol/ExecCommandEndEvent.ts +59 -0
  62. package/apps/backend/src/protocol/ExecCommandOutputDeltaEvent.ts +18 -0
  63. package/apps/backend/src/protocol/ExecCommandSource.ts +5 -0
  64. package/apps/backend/src/protocol/ExecOneOffCommandParams.ts +6 -0
  65. package/apps/backend/src/protocol/ExecOneOffCommandResponse.ts +5 -0
  66. package/apps/backend/src/protocol/ExecOutputStream.ts +5 -0
  67. package/apps/backend/src/protocol/ExecPolicyAmendment.ts +12 -0
  68. package/apps/backend/src/protocol/ExitedReviewModeEvent.ts +6 -0
  69. package/apps/backend/src/protocol/FileChange.ts +5 -0
  70. package/apps/backend/src/protocol/ForcedLoginMethod.ts +5 -0
  71. package/apps/backend/src/protocol/FunctionCallOutputContentItem.ts +9 -0
  72. package/apps/backend/src/protocol/FunctionCallOutputPayload.ts +15 -0
  73. package/apps/backend/src/protocol/FuzzyFileSearchParams.ts +5 -0
  74. package/apps/backend/src/protocol/FuzzyFileSearchResponse.ts +6 -0
  75. package/apps/backend/src/protocol/FuzzyFileSearchResult.ts +8 -0
  76. package/apps/backend/src/protocol/GetAuthStatusParams.ts +5 -0
  77. package/apps/backend/src/protocol/GetAuthStatusResponse.ts +6 -0
  78. package/apps/backend/src/protocol/GetConversationSummaryParams.ts +6 -0
  79. package/apps/backend/src/protocol/GetConversationSummaryResponse.ts +6 -0
  80. package/apps/backend/src/protocol/GetHistoryEntryResponseEvent.ts +10 -0
  81. package/apps/backend/src/protocol/GetUserAgentResponse.ts +5 -0
  82. package/apps/backend/src/protocol/GetUserSavedConfigResponse.ts +6 -0
  83. package/apps/backend/src/protocol/GhostCommit.ts +8 -0
  84. package/apps/backend/src/protocol/GitDiffToRemoteParams.ts +5 -0
  85. package/apps/backend/src/protocol/GitDiffToRemoteResponse.ts +6 -0
  86. package/apps/backend/src/protocol/GitSha.ts +5 -0
  87. package/apps/backend/src/protocol/HistoryEntry.ts +5 -0
  88. package/apps/backend/src/protocol/ImageContent.ts +9 -0
  89. package/apps/backend/src/protocol/InitializeParams.ts +6 -0
  90. package/apps/backend/src/protocol/InitializeResponse.ts +5 -0
  91. package/apps/backend/src/protocol/InputItem.ts +5 -0
  92. package/apps/backend/src/protocol/InterruptConversationParams.ts +6 -0
  93. package/apps/backend/src/protocol/InterruptConversationResponse.ts +6 -0
  94. package/apps/backend/src/protocol/ItemCompletedEvent.ts +7 -0
  95. package/apps/backend/src/protocol/ItemStartedEvent.ts +7 -0
  96. package/apps/backend/src/protocol/ListConversationsParams.ts +5 -0
  97. package/apps/backend/src/protocol/ListConversationsResponse.ts +6 -0
  98. package/apps/backend/src/protocol/ListCustomPromptsResponseEvent.ts +9 -0
  99. package/apps/backend/src/protocol/ListSkillsResponseEvent.ts +9 -0
  100. package/apps/backend/src/protocol/LocalShellAction.ts +6 -0
  101. package/apps/backend/src/protocol/LocalShellExecAction.ts +5 -0
  102. package/apps/backend/src/protocol/LocalShellStatus.ts +5 -0
  103. package/apps/backend/src/protocol/LoginApiKeyParams.ts +5 -0
  104. package/apps/backend/src/protocol/LoginApiKeyResponse.ts +5 -0
  105. package/apps/backend/src/protocol/LoginChatGptCompleteNotification.ts +8 -0
  106. package/apps/backend/src/protocol/LoginChatGptResponse.ts +5 -0
  107. package/apps/backend/src/protocol/LogoutChatGptResponse.ts +5 -0
  108. package/apps/backend/src/protocol/McpAuthStatus.ts +5 -0
  109. package/apps/backend/src/protocol/McpInvocation.ts +18 -0
  110. package/apps/backend/src/protocol/McpListToolsResponseEvent.ts +25 -0
  111. package/apps/backend/src/protocol/McpStartupCompleteEvent.ts +6 -0
  112. package/apps/backend/src/protocol/McpStartupFailure.ts +5 -0
  113. package/apps/backend/src/protocol/McpStartupStatus.ts +5 -0
  114. package/apps/backend/src/protocol/McpStartupUpdateEvent.ts +14 -0
  115. package/apps/backend/src/protocol/McpToolCallBeginEvent.ts +10 -0
  116. package/apps/backend/src/protocol/McpToolCallEndEvent.ts +15 -0
  117. package/apps/backend/src/protocol/NetworkAccess.ts +8 -0
  118. package/apps/backend/src/protocol/NewConversationParams.ts +8 -0
  119. package/apps/backend/src/protocol/NewConversationResponse.ts +7 -0
  120. package/apps/backend/src/protocol/ParsedCommand.ts +12 -0
  121. package/apps/backend/src/protocol/PatchApplyBeginEvent.ts +23 -0
  122. package/apps/backend/src/protocol/PatchApplyEndEvent.ts +31 -0
  123. package/apps/backend/src/protocol/PlanItemArg.ts +6 -0
  124. package/apps/backend/src/protocol/PlanType.ts +5 -0
  125. package/apps/backend/src/protocol/Profile.ts +9 -0
  126. package/apps/backend/src/protocol/README.md +11 -0
  127. package/apps/backend/src/protocol/RateLimitSnapshot.ts +8 -0
  128. package/apps/backend/src/protocol/RateLimitWindow.ts +17 -0
  129. package/apps/backend/src/protocol/RawResponseItemEvent.ts +6 -0
  130. package/apps/backend/src/protocol/ReasoningContentDeltaEvent.ts +5 -0
  131. package/apps/backend/src/protocol/ReasoningEffort.ts +8 -0
  132. package/apps/backend/src/protocol/ReasoningItem.ts +5 -0
  133. package/apps/backend/src/protocol/ReasoningItemContent.ts +5 -0
  134. package/apps/backend/src/protocol/ReasoningItemReasoningSummary.ts +5 -0
  135. package/apps/backend/src/protocol/ReasoningRawContentDeltaEvent.ts +5 -0
  136. package/apps/backend/src/protocol/ReasoningSummary.ts +10 -0
  137. package/apps/backend/src/protocol/RemoveConversationListenerParams.ts +5 -0
  138. package/apps/backend/src/protocol/RemoveConversationSubscriptionResponse.ts +5 -0
  139. package/apps/backend/src/protocol/RequestId.ts +5 -0
  140. package/apps/backend/src/protocol/Resource.ts +9 -0
  141. package/apps/backend/src/protocol/ResourceLink.ts +11 -0
  142. package/apps/backend/src/protocol/ResourceTemplate.ts +9 -0
  143. package/apps/backend/src/protocol/ResponseItem.ts +17 -0
  144. package/apps/backend/src/protocol/ResumeConversationParams.ts +8 -0
  145. package/apps/backend/src/protocol/ResumeConversationResponse.ts +7 -0
  146. package/apps/backend/src/protocol/ReviewCodeLocation.ts +9 -0
  147. package/apps/backend/src/protocol/ReviewDecision.ts +9 -0
  148. package/apps/backend/src/protocol/ReviewFinding.ts +9 -0
  149. package/apps/backend/src/protocol/ReviewLineRange.ts +8 -0
  150. package/apps/backend/src/protocol/ReviewOutputEvent.ts +9 -0
  151. package/apps/backend/src/protocol/ReviewRequest.ts +9 -0
  152. package/apps/backend/src/protocol/ReviewTarget.ts +9 -0
  153. package/apps/backend/src/protocol/Role.ts +8 -0
  154. package/apps/backend/src/protocol/SandboxMode.ts +5 -0
  155. package/apps/backend/src/protocol/SandboxPolicy.ts +35 -0
  156. package/apps/backend/src/protocol/SandboxSettings.ts +6 -0
  157. package/apps/backend/src/protocol/SendUserMessageParams.ts +7 -0
  158. package/apps/backend/src/protocol/SendUserMessageResponse.ts +5 -0
  159. package/apps/backend/src/protocol/SendUserTurnParams.ts +11 -0
  160. package/apps/backend/src/protocol/SendUserTurnResponse.ts +5 -0
  161. package/apps/backend/src/protocol/ServerNotification.ts +36 -0
  162. package/apps/backend/src/protocol/ServerRequest.ts +13 -0
  163. package/apps/backend/src/protocol/SessionConfiguredEvent.ts +48 -0
  164. package/apps/backend/src/protocol/SessionConfiguredNotification.ts +8 -0
  165. package/apps/backend/src/protocol/SessionSource.ts +6 -0
  166. package/apps/backend/src/protocol/SetDefaultModelParams.ts +6 -0
  167. package/apps/backend/src/protocol/SetDefaultModelResponse.ts +5 -0
  168. package/apps/backend/src/protocol/SkillErrorInfo.ts +5 -0
  169. package/apps/backend/src/protocol/SkillMetadata.ts +6 -0
  170. package/apps/backend/src/protocol/SkillScope.ts +5 -0
  171. package/apps/backend/src/protocol/SkillsListEntry.ts +7 -0
  172. package/apps/backend/src/protocol/StepStatus.ts +5 -0
  173. package/apps/backend/src/protocol/StreamErrorEvent.ts +6 -0
  174. package/apps/backend/src/protocol/SubAgentSource.ts +5 -0
  175. package/apps/backend/src/protocol/TaskCompleteEvent.ts +5 -0
  176. package/apps/backend/src/protocol/TaskStartedEvent.ts +5 -0
  177. package/apps/backend/src/protocol/TerminalInteractionEvent.ts +17 -0
  178. package/apps/backend/src/protocol/TextContent.ts +9 -0
  179. package/apps/backend/src/protocol/TextResourceContents.ts +5 -0
  180. package/apps/backend/src/protocol/TokenCountEvent.ts +7 -0
  181. package/apps/backend/src/protocol/TokenUsage.ts +5 -0
  182. package/apps/backend/src/protocol/TokenUsageInfo.ts +6 -0
  183. package/apps/backend/src/protocol/Tool.ts +11 -0
  184. package/apps/backend/src/protocol/ToolAnnotations.ts +15 -0
  185. package/apps/backend/src/protocol/ToolInputSchema.ts +9 -0
  186. package/apps/backend/src/protocol/ToolOutputSchema.ts +10 -0
  187. package/apps/backend/src/protocol/Tools.ts +5 -0
  188. package/apps/backend/src/protocol/TurnAbortReason.ts +5 -0
  189. package/apps/backend/src/protocol/TurnAbortedEvent.ts +6 -0
  190. package/apps/backend/src/protocol/TurnDiffEvent.ts +5 -0
  191. package/apps/backend/src/protocol/TurnItem.ts +9 -0
  192. package/apps/backend/src/protocol/UndoCompletedEvent.ts +5 -0
  193. package/apps/backend/src/protocol/UndoStartedEvent.ts +5 -0
  194. package/apps/backend/src/protocol/UpdatePlanArgs.ts +6 -0
  195. package/apps/backend/src/protocol/UserInfoResponse.ts +5 -0
  196. package/apps/backend/src/protocol/UserInput.ts +8 -0
  197. package/apps/backend/src/protocol/UserMessageEvent.ts +5 -0
  198. package/apps/backend/src/protocol/UserMessageItem.ts +6 -0
  199. package/apps/backend/src/protocol/UserSavedConfig.ts +14 -0
  200. package/apps/backend/src/protocol/Verbosity.ts +9 -0
  201. package/apps/backend/src/protocol/ViewImageToolCallEvent.ts +13 -0
  202. package/apps/backend/src/protocol/WarningEvent.ts +5 -0
  203. package/apps/backend/src/protocol/WebSearchAction.ts +5 -0
  204. package/apps/backend/src/protocol/WebSearchBeginEvent.ts +5 -0
  205. package/apps/backend/src/protocol/WebSearchEndEvent.ts +5 -0
  206. package/apps/backend/src/protocol/WebSearchItem.ts +5 -0
  207. package/apps/backend/src/protocol/index.ts +198 -0
  208. package/apps/backend/src/protocol/serde_json/JsonValue.ts +5 -0
  209. package/apps/backend/src/protocol/v2/Account.ts +6 -0
  210. package/apps/backend/src/protocol/v2/AccountLoginCompletedNotification.ts +5 -0
  211. package/apps/backend/src/protocol/v2/AccountRateLimitsUpdatedNotification.ts +6 -0
  212. package/apps/backend/src/protocol/v2/AccountUpdatedNotification.ts +6 -0
  213. package/apps/backend/src/protocol/v2/AgentMessageDeltaNotification.ts +5 -0
  214. package/apps/backend/src/protocol/v2/ApprovalDecision.ts +6 -0
  215. package/apps/backend/src/protocol/v2/AskForApproval.ts +5 -0
  216. package/apps/backend/src/protocol/v2/CancelLoginAccountParams.ts +5 -0
  217. package/apps/backend/src/protocol/v2/CancelLoginAccountResponse.ts +6 -0
  218. package/apps/backend/src/protocol/v2/CancelLoginAccountStatus.ts +5 -0
  219. package/apps/backend/src/protocol/v2/CodexErrorInfo.ts +11 -0
  220. package/apps/backend/src/protocol/v2/CommandAction.ts +5 -0
  221. package/apps/backend/src/protocol/v2/CommandExecParams.ts +6 -0
  222. package/apps/backend/src/protocol/v2/CommandExecResponse.ts +5 -0
  223. package/apps/backend/src/protocol/v2/CommandExecutionOutputDeltaNotification.ts +5 -0
  224. package/apps/backend/src/protocol/v2/CommandExecutionRequestApprovalParams.ts +14 -0
  225. package/apps/backend/src/protocol/v2/CommandExecutionRequestApprovalResponse.ts +6 -0
  226. package/apps/backend/src/protocol/v2/CommandExecutionStatus.ts +5 -0
  227. package/apps/backend/src/protocol/v2/Config.ts +15 -0
  228. package/apps/backend/src/protocol/v2/ConfigBatchWriteParams.ts +10 -0
  229. package/apps/backend/src/protocol/v2/ConfigEdit.ts +7 -0
  230. package/apps/backend/src/protocol/v2/ConfigLayer.ts +7 -0
  231. package/apps/backend/src/protocol/v2/ConfigLayerMetadata.ts +6 -0
  232. package/apps/backend/src/protocol/v2/ConfigLayerSource.ts +6 -0
  233. package/apps/backend/src/protocol/v2/ConfigReadParams.ts +5 -0
  234. package/apps/backend/src/protocol/v2/ConfigReadResponse.ts +8 -0
  235. package/apps/backend/src/protocol/v2/ConfigValueWriteParams.ts +11 -0
  236. package/apps/backend/src/protocol/v2/ConfigWriteResponse.ts +12 -0
  237. package/apps/backend/src/protocol/v2/ContextCompactedNotification.ts +5 -0
  238. package/apps/backend/src/protocol/v2/CreditsSnapshot.ts +5 -0
  239. package/apps/backend/src/protocol/v2/DeprecationNoticeNotification.ts +13 -0
  240. package/apps/backend/src/protocol/v2/ErrorNotification.ts +6 -0
  241. package/apps/backend/src/protocol/v2/ExecPolicyAmendment.ts +5 -0
  242. package/apps/backend/src/protocol/v2/FeedbackUploadParams.ts +5 -0
  243. package/apps/backend/src/protocol/v2/FeedbackUploadResponse.ts +5 -0
  244. package/apps/backend/src/protocol/v2/FileChangeOutputDeltaNotification.ts +5 -0
  245. package/apps/backend/src/protocol/v2/FileChangeRequestApprovalParams.ts +14 -0
  246. package/apps/backend/src/protocol/v2/FileChangeRequestApprovalResponse.ts +6 -0
  247. package/apps/backend/src/protocol/v2/FileUpdateChange.ts +6 -0
  248. package/apps/backend/src/protocol/v2/GetAccountParams.ts +5 -0
  249. package/apps/backend/src/protocol/v2/GetAccountRateLimitsResponse.ts +6 -0
  250. package/apps/backend/src/protocol/v2/GetAccountResponse.ts +6 -0
  251. package/apps/backend/src/protocol/v2/GitInfo.ts +5 -0
  252. package/apps/backend/src/protocol/v2/ItemCompletedNotification.ts +6 -0
  253. package/apps/backend/src/protocol/v2/ItemStartedNotification.ts +6 -0
  254. package/apps/backend/src/protocol/v2/ListMcpServerStatusParams.ts +13 -0
  255. package/apps/backend/src/protocol/v2/ListMcpServerStatusResponse.ts +11 -0
  256. package/apps/backend/src/protocol/v2/LoginAccountParams.ts +5 -0
  257. package/apps/backend/src/protocol/v2/LoginAccountResponse.ts +9 -0
  258. package/apps/backend/src/protocol/v2/LogoutAccountResponse.ts +5 -0
  259. package/apps/backend/src/protocol/v2/McpAuthStatus.ts +5 -0
  260. package/apps/backend/src/protocol/v2/McpServerOauthLoginCompletedNotification.ts +5 -0
  261. package/apps/backend/src/protocol/v2/McpServerOauthLoginParams.ts +5 -0
  262. package/apps/backend/src/protocol/v2/McpServerOauthLoginResponse.ts +5 -0
  263. package/apps/backend/src/protocol/v2/McpServerStatus.ts +9 -0
  264. package/apps/backend/src/protocol/v2/McpToolCallError.ts +5 -0
  265. package/apps/backend/src/protocol/v2/McpToolCallProgressNotification.ts +5 -0
  266. package/apps/backend/src/protocol/v2/McpToolCallResult.ts +7 -0
  267. package/apps/backend/src/protocol/v2/McpToolCallStatus.ts +5 -0
  268. package/apps/backend/src/protocol/v2/MergeStrategy.ts +5 -0
  269. package/apps/backend/src/protocol/v2/Model.ts +7 -0
  270. package/apps/backend/src/protocol/v2/ModelListParams.ts +13 -0
  271. package/apps/backend/src/protocol/v2/ModelListResponse.ts +11 -0
  272. package/apps/backend/src/protocol/v2/NetworkAccess.ts +5 -0
  273. package/apps/backend/src/protocol/v2/OverriddenMetadata.ts +7 -0
  274. package/apps/backend/src/protocol/v2/PatchApplyStatus.ts +5 -0
  275. package/apps/backend/src/protocol/v2/PatchChangeKind.ts +5 -0
  276. package/apps/backend/src/protocol/v2/ProfileV2.ts +10 -0
  277. package/apps/backend/src/protocol/v2/RateLimitSnapshot.ts +8 -0
  278. package/apps/backend/src/protocol/v2/RateLimitWindow.ts +5 -0
  279. package/apps/backend/src/protocol/v2/RawResponseItemCompletedNotification.ts +6 -0
  280. package/apps/backend/src/protocol/v2/ReasoningEffortOption.ts +6 -0
  281. package/apps/backend/src/protocol/v2/ReasoningSummaryPartAddedNotification.ts +5 -0
  282. package/apps/backend/src/protocol/v2/ReasoningSummaryTextDeltaNotification.ts +5 -0
  283. package/apps/backend/src/protocol/v2/ReasoningTextDeltaNotification.ts +5 -0
  284. package/apps/backend/src/protocol/v2/ReviewDelivery.ts +5 -0
  285. package/apps/backend/src/protocol/v2/ReviewStartParams.ts +12 -0
  286. package/apps/backend/src/protocol/v2/ReviewStartResponse.ts +13 -0
  287. package/apps/backend/src/protocol/v2/ReviewTarget.ts +9 -0
  288. package/apps/backend/src/protocol/v2/SandboxMode.ts +5 -0
  289. package/apps/backend/src/protocol/v2/SandboxPolicy.ts +7 -0
  290. package/apps/backend/src/protocol/v2/SandboxWorkspaceWrite.ts +5 -0
  291. package/apps/backend/src/protocol/v2/SessionSource.ts +5 -0
  292. package/apps/backend/src/protocol/v2/SkillErrorInfo.ts +5 -0
  293. package/apps/backend/src/protocol/v2/SkillMetadata.ts +6 -0
  294. package/apps/backend/src/protocol/v2/SkillScope.ts +5 -0
  295. package/apps/backend/src/protocol/v2/SkillsListEntry.ts +7 -0
  296. package/apps/backend/src/protocol/v2/SkillsListParams.ts +13 -0
  297. package/apps/backend/src/protocol/v2/SkillsListResponse.ts +6 -0
  298. package/apps/backend/src/protocol/v2/TerminalInteractionNotification.ts +5 -0
  299. package/apps/backend/src/protocol/v2/Thread.ts +46 -0
  300. package/apps/backend/src/protocol/v2/ThreadArchiveParams.ts +5 -0
  301. package/apps/backend/src/protocol/v2/ThreadArchiveResponse.ts +5 -0
  302. package/apps/backend/src/protocol/v2/ThreadItem.ts +48 -0
  303. package/apps/backend/src/protocol/v2/ThreadListParams.ts +18 -0
  304. package/apps/backend/src/protocol/v2/ThreadListResponse.ts +11 -0
  305. package/apps/backend/src/protocol/v2/ThreadResumeParams.ts +35 -0
  306. package/apps/backend/src/protocol/v2/ThreadResumeResponse.ts +9 -0
  307. package/apps/backend/src/protocol/v2/ThreadStartParams.ts +15 -0
  308. package/apps/backend/src/protocol/v2/ThreadStartResponse.ts +9 -0
  309. package/apps/backend/src/protocol/v2/ThreadStartedNotification.ts +6 -0
  310. package/apps/backend/src/protocol/v2/ThreadTokenUsage.ts +6 -0
  311. package/apps/backend/src/protocol/v2/ThreadTokenUsageUpdatedNotification.ts +6 -0
  312. package/apps/backend/src/protocol/v2/TokenUsageBreakdown.ts +5 -0
  313. package/apps/backend/src/protocol/v2/ToolsV2.ts +5 -0
  314. package/apps/backend/src/protocol/v2/Turn.ts +18 -0
  315. package/apps/backend/src/protocol/v2/TurnCompletedNotification.ts +6 -0
  316. package/apps/backend/src/protocol/v2/TurnDiffUpdatedNotification.ts +9 -0
  317. package/apps/backend/src/protocol/v2/TurnError.ts +6 -0
  318. package/apps/backend/src/protocol/v2/TurnInterruptParams.ts +5 -0
  319. package/apps/backend/src/protocol/v2/TurnInterruptResponse.ts +5 -0
  320. package/apps/backend/src/protocol/v2/TurnPlanStep.ts +6 -0
  321. package/apps/backend/src/protocol/v2/TurnPlanStepStatus.ts +5 -0
  322. package/apps/backend/src/protocol/v2/TurnPlanUpdatedNotification.ts +6 -0
  323. package/apps/backend/src/protocol/v2/TurnStartParams.ts +34 -0
  324. package/apps/backend/src/protocol/v2/TurnStartResponse.ts +6 -0
  325. package/apps/backend/src/protocol/v2/TurnStartedNotification.ts +6 -0
  326. package/apps/backend/src/protocol/v2/TurnStatus.ts +5 -0
  327. package/apps/backend/src/protocol/v2/UserInput.ts +5 -0
  328. package/apps/backend/src/protocol/v2/WindowsWorldWritableWarningNotification.ts +5 -0
  329. package/apps/backend/src/protocol/v2/WriteStatus.ts +5 -0
  330. package/apps/backend/src/protocol/v2/index.ts +123 -0
  331. package/apps/backend/src/reviews/service.ts +27 -0
  332. package/apps/backend/src/reviews/store.ts +124 -0
  333. package/apps/backend/src/server.ts +531 -0
  334. package/apps/backend/src/services/profile-store.ts +114 -0
  335. package/apps/backend/src/services/supervisor.ts +102 -0
  336. package/apps/backend/src/thread-index/service.ts +75 -0
  337. package/apps/backend/src/thread-index/store.ts +195 -0
  338. package/apps/backend/src/ws/messages.ts +73 -0
  339. package/apps/backend/tsconfig.json +20 -0
  340. package/apps/web/README.md +24 -0
  341. package/apps/web/bun.lock +1062 -0
  342. package/apps/web/eslint.config.js +23 -0
  343. package/apps/web/index.html +16 -0
  344. package/apps/web/package.json +38 -0
  345. package/apps/web/src/app.tsx +83 -0
  346. package/apps/web/src/components/composer/slash-command-menu.tsx +47 -0
  347. package/apps/web/src/components/index.ts +2 -0
  348. package/apps/web/src/components/layout/account-usage-panel.tsx +167 -0
  349. package/apps/web/src/components/layout/analytics-view.tsx +296 -0
  350. package/apps/web/src/components/layout/index.ts +7 -0
  351. package/apps/web/src/components/layout/mobile-header.tsx +56 -0
  352. package/apps/web/src/components/layout/reviews-view.tsx +848 -0
  353. package/apps/web/src/components/layout/session-view.tsx +1374 -0
  354. package/apps/web/src/components/layout/settings-dialog.tsx +322 -0
  355. package/apps/web/src/components/layout/side-bar.tsx +417 -0
  356. package/apps/web/src/components/layout/thread-list.tsx +488 -0
  357. package/apps/web/src/components/layout/virtualized-message-list.tsx +748 -0
  358. package/apps/web/src/components/loading/startup-ascii.ts +652 -0
  359. package/apps/web/src/components/loading/startup-loader.tsx +37 -0
  360. package/apps/web/src/components/session-view/file-mention-menu.tsx +46 -0
  361. package/apps/web/src/components/session-view/session-auth-banner.tsx +61 -0
  362. package/apps/web/src/components/session-view/session-composer.tsx +328 -0
  363. package/apps/web/src/components/session-view/session-dialogs.tsx +280 -0
  364. package/apps/web/src/components/session-view/session-empty.tsx +47 -0
  365. package/apps/web/src/components/session-view/session-header.tsx +49 -0
  366. package/apps/web/src/components/ui/avatar.tsx +19 -0
  367. package/apps/web/src/components/ui/badge.tsx +21 -0
  368. package/apps/web/src/components/ui/button.tsx +47 -0
  369. package/apps/web/src/components/ui/collapsible-content.tsx +114 -0
  370. package/apps/web/src/components/ui/contribution-graph.tsx +182 -0
  371. package/apps/web/src/components/ui/dialog-box.tsx +203 -0
  372. package/apps/web/src/components/ui/icon-button.tsx +32 -0
  373. package/apps/web/src/components/ui/icons.tsx +187 -0
  374. package/apps/web/src/components/ui/index.tsx +15 -0
  375. package/apps/web/src/components/ui/input.tsx +43 -0
  376. package/apps/web/src/components/ui/markdown-stream.tsx +21 -0
  377. package/apps/web/src/components/ui/mobile-drawer.tsx +124 -0
  378. package/apps/web/src/components/ui/section-header.tsx +13 -0
  379. package/apps/web/src/components/ui/select.tsx +217 -0
  380. package/apps/web/src/components/ui/shimmer.tsx +138 -0
  381. package/apps/web/src/components/ui/status-dot.tsx +24 -0
  382. package/apps/web/src/config.ts +5 -0
  383. package/apps/web/src/hooks/index.ts +3 -0
  384. package/apps/web/src/hooks/use-analytics.ts +122 -0
  385. package/apps/web/src/hooks/use-hub-connection.ts +587 -0
  386. package/apps/web/src/hooks/use-mobile.ts +76 -0
  387. package/apps/web/src/hooks/use-thread-history.ts +210 -0
  388. package/apps/web/src/index.css +269 -0
  389. package/apps/web/src/main.tsx +10 -0
  390. package/apps/web/src/services/hub-client.ts +358 -0
  391. package/apps/web/src/store/index.ts +528 -0
  392. package/apps/web/src/types/index.ts +119 -0
  393. package/apps/web/src/utils/account-refresh.ts +168 -0
  394. package/apps/web/src/utils/approval-policy.ts +53 -0
  395. package/apps/web/src/utils/init-prompt.ts +41 -0
  396. package/apps/web/src/utils/item-format.ts +170 -0
  397. package/apps/web/src/utils/prompt-expander.ts +62 -0
  398. package/apps/web/src/utils/reasoning-summary.ts +48 -0
  399. package/apps/web/src/utils/slash-commands.ts +98 -0
  400. package/apps/web/tsconfig.app.json +28 -0
  401. package/apps/web/tsconfig.json +7 -0
  402. package/apps/web/tsconfig.node.json +26 -0
  403. package/apps/web/vite.config.ts +8 -0
  404. package/bin/better-codex.cjs +199 -0
  405. package/package.json +20 -0
@@ -0,0 +1,488 @@
1
+ import { useState, useEffect } from 'react'
2
+ import { useAppStore } from '../../store'
3
+ import { hubClient } from '../../services/hub-client'
4
+ import { Badge, Button, Icons, IconButton, Input, StatusDot } from '../ui'
5
+ import type { TabType, ReasoningEffort, Thread } from '../../types'
6
+ import { normalizeApprovalPolicy } from '../../utils/approval-policy'
7
+
8
+ interface ThreadListProps {
9
+ onThreadSelect?: (threadId: string) => void
10
+ }
11
+
12
+ export function ThreadList({ onThreadSelect }: ThreadListProps) {
13
+ const [isCreating, setIsCreating] = useState(false)
14
+ const [searchQuery, setSearchQuery] = useState('')
15
+ const [searchResults, setSearchResults] = useState<Thread[] | null>(null)
16
+ const [filterAccountId, setFilterAccountId] = useState<string | null>(null)
17
+ const [filterModel, setFilterModel] = useState<string | null>(null)
18
+ const [filterDays, setFilterDays] = useState<number | null>(null)
19
+ const [showAccountMenu, setShowAccountMenu] = useState(false)
20
+ const [showModelMenu, setShowModelMenu] = useState(false)
21
+ const [showDateMenu, setShowDateMenu] = useState(false)
22
+
23
+ const {
24
+ accounts,
25
+ threads,
26
+ selectedAccountId,
27
+ selectedThreadId,
28
+ setSelectedThreadId,
29
+ addThread,
30
+ updateThread,
31
+ activeTab,
32
+ setActiveTab,
33
+ modelsByAccount,
34
+ setThreadModel,
35
+ setThreadEffort,
36
+ setThreadApproval,
37
+ connectionStatus,
38
+ } = useAppStore()
39
+
40
+ useEffect(() => {
41
+ const handleClickOutside = () => {
42
+ setShowAccountMenu(false)
43
+ setShowModelMenu(false)
44
+ setShowDateMenu(false)
45
+ }
46
+
47
+ if (showAccountMenu || showModelMenu || showDateMenu) {
48
+ document.addEventListener('click', handleClickOutside)
49
+ return () => document.removeEventListener('click', handleClickOutside)
50
+ }
51
+ }, [showAccountMenu, showModelMenu, showDateMenu])
52
+
53
+ const accountThreads = selectedAccountId
54
+ ? threads.filter(t => t.accountId === selectedAccountId)
55
+ : threads
56
+
57
+ const allModels = Array.from(new Set(threads.map(t => t.model))).filter(Boolean)
58
+
59
+ const baseThreads = searchResults ?? accountThreads
60
+
61
+ const filteredThreads = baseThreads.filter((thread) => {
62
+ if (activeTab === 'archive') {
63
+ if (thread.status !== 'archived') return false
64
+ } else if (activeTab === 'reviews') {
65
+ return false
66
+ } else {
67
+ if (thread.status === 'archived') return false
68
+ }
69
+
70
+ if (filterAccountId && thread.accountId !== filterAccountId) return false
71
+
72
+ if (filterModel && thread.model !== filterModel) return false
73
+
74
+ if (filterDays !== null && thread.createdAt) {
75
+ const threadDate = new Date(thread.createdAt)
76
+ const cutoffDate = new Date()
77
+ cutoffDate.setDate(cutoffDate.getDate() - filterDays)
78
+ if (threadDate < cutoffDate) return false
79
+ }
80
+
81
+ return true
82
+ })
83
+
84
+ const selectedAccount = accounts.find(a => a.id === selectedAccountId)
85
+ const accountModels = selectedAccountId ? modelsByAccount[selectedAccountId] || [] : []
86
+ const defaultModel = accountModels.find((model) => model.isDefault) ?? accountModels[0]
87
+ const defaultEffort = defaultModel?.defaultReasoningEffort
88
+ const isAccountReady = selectedAccount?.status === 'online'
89
+
90
+ useEffect(() => {
91
+ const trimmed = searchQuery.trim()
92
+ if (!trimmed) {
93
+ setSearchResults(null)
94
+ return
95
+ }
96
+ let cancelled = false
97
+ const timeoutId = window.setTimeout(async () => {
98
+ try {
99
+ const now = Math.floor(Date.now() / 1000)
100
+ const createdAfter = filterDays ? now - filterDays * 24 * 60 * 60 : undefined
101
+ const status = activeTab === 'archive' ? 'archived' : activeTab === 'sessions' ? 'active' : undefined
102
+ const profileId = filterAccountId ?? selectedAccountId ?? undefined
103
+ const results = await hubClient.searchThreads({
104
+ query: trimmed,
105
+ profileId,
106
+ model: filterModel ?? undefined,
107
+ status,
108
+ createdAfter,
109
+ limit: 100,
110
+ })
111
+ if (cancelled) {
112
+ return
113
+ }
114
+ const mapped = results.map((row) => ({
115
+ id: row.threadId,
116
+ accountId: row.profileId,
117
+ title: row.preview?.trim() || 'Untitled session',
118
+ preview: row.preview?.trim() || 'No preview available yet.',
119
+ model: row.modelProvider ?? 'unknown',
120
+ createdAt: row.createdAt
121
+ ? new Date(row.createdAt * 1000).toLocaleDateString('en-US', {
122
+ month: 'short',
123
+ day: 'numeric',
124
+ })
125
+ : '',
126
+ status: row.status,
127
+ messageCount: 0,
128
+ }))
129
+ setSearchResults(mapped)
130
+ } catch {
131
+ if (!cancelled) {
132
+ setSearchResults([])
133
+ }
134
+ }
135
+ }, 200)
136
+
137
+ return () => {
138
+ cancelled = true
139
+ window.clearTimeout(timeoutId)
140
+ }
141
+ }, [searchQuery, filterAccountId, filterModel, filterDays, activeTab, selectedAccountId])
142
+
143
+ const archiveThread = async (threadId: string, accountId: string) => {
144
+ try {
145
+ await hubClient.request(accountId, 'thread/archive', { threadId })
146
+ updateThread(threadId, { status: 'archived' })
147
+ } catch {
148
+ // TODO: surface error state.
149
+ }
150
+ }
151
+
152
+ const tabs: { key: TabType; label: string }[] = [
153
+ { key: 'sessions', label: 'Sessions' },
154
+ { key: 'reviews', label: 'Reviews' },
155
+ { key: 'archive', label: 'Archive' },
156
+ ]
157
+
158
+ return (
159
+ <div className="w-full md:w-80 bg-bg-primary border-r border-border flex flex-col h-full">
160
+ <div className="p-3 border-b border-border space-y-2">
161
+ <div className="flex items-center gap-2">
162
+ <Input
163
+ placeholder="Search sessions..."
164
+ icon={<Icons.Search className="w-4 h-4" />}
165
+ className="flex-1"
166
+ value={searchQuery}
167
+ onChange={(value) => setSearchQuery(value)}
168
+ />
169
+ {onThreadSelect && (
170
+ <IconButton
171
+ icon={<Icons.X className="w-4 h-4 text-text-muted" />}
172
+ size="sm"
173
+ onClick={() => onThreadSelect('')}
174
+ className="md:hidden"
175
+ />
176
+ )}
177
+ </div>
178
+
179
+ <div className="flex gap-1">
180
+ {tabs.map((tab) => (
181
+ <button
182
+ key={tab.key}
183
+ onClick={() => setActiveTab(tab.key)}
184
+ className={`px-2.5 py-1 rounded-md text-xs font-medium transition-colors ${
185
+ activeTab === tab.key
186
+ ? 'bg-bg-elevated text-text-primary'
187
+ : 'text-text-muted hover:text-text-secondary'
188
+ }`}
189
+ >
190
+ {tab.label}
191
+ </button>
192
+ ))}
193
+ </div>
194
+ </div>
195
+
196
+ <div className="px-3 py-2 border-b border-border flex gap-1.5 flex-wrap">
197
+ <FilterChip
198
+ label={filterAccountId ? accounts.find(a => a.id === filterAccountId)?.name ?? 'Account' : 'All accounts'}
199
+ open={showAccountMenu}
200
+ onClick={(e) => {
201
+ e.stopPropagation()
202
+ setShowAccountMenu(!showAccountMenu)
203
+ setShowModelMenu(false)
204
+ setShowDateMenu(false)
205
+ }}
206
+ onClear={filterAccountId ? () => setFilterAccountId(null) : undefined}
207
+ >
208
+ {showAccountMenu && (
209
+ <div className="absolute top-full left-0 mt-1 bg-bg-elevated border border-border rounded-lg shadow-lg py-1 z-10 min-w-[140px]">
210
+ <button
211
+ onClick={() => {
212
+ setFilterAccountId(null)
213
+ setShowAccountMenu(false)
214
+ }}
215
+ className="w-full text-left px-3 py-1.5 text-[10px] text-text-primary hover:bg-bg-hover"
216
+ >
217
+ All accounts
218
+ </button>
219
+ {accounts.map((account) => (
220
+ <button
221
+ key={account.id}
222
+ onClick={() => {
223
+ setFilterAccountId(account.id)
224
+ setShowAccountMenu(false)
225
+ }}
226
+ className="w-full text-left px-3 py-1.5 text-[10px] text-text-primary hover:bg-bg-hover"
227
+ >
228
+ {account.name}
229
+ </button>
230
+ ))}
231
+ </div>
232
+ )}
233
+ </FilterChip>
234
+
235
+ <FilterChip
236
+ label={filterModel ?? 'Any model'}
237
+ open={showModelMenu}
238
+ onClick={(e) => {
239
+ e.stopPropagation()
240
+ setShowModelMenu(!showModelMenu)
241
+ setShowAccountMenu(false)
242
+ setShowDateMenu(false)
243
+ }}
244
+ onClear={filterModel ? () => setFilterModel(null) : undefined}
245
+ >
246
+ {showModelMenu && (
247
+ <div className="absolute top-full left-0 mt-1 bg-bg-elevated border border-border rounded-lg shadow-lg py-1 z-10 min-w-[140px]">
248
+ <button
249
+ onClick={() => {
250
+ setFilterModel(null)
251
+ setShowModelMenu(false)
252
+ }}
253
+ className="w-full text-left px-3 py-1.5 text-[10px] text-text-primary hover:bg-bg-hover"
254
+ >
255
+ Any model
256
+ </button>
257
+ {allModels.map((model) => (
258
+ <button
259
+ key={model}
260
+ onClick={() => {
261
+ setFilterModel(model)
262
+ setShowModelMenu(false)
263
+ }}
264
+ className="w-full text-left px-3 py-1.5 text-[10px] text-text-primary hover:bg-bg-hover"
265
+ >
266
+ {model}
267
+ </button>
268
+ ))}
269
+ </div>
270
+ )}
271
+ </FilterChip>
272
+
273
+ <FilterChip
274
+ label={filterDays === null ? 'All time' : filterDays === 1 ? 'Today' : filterDays === 7 ? 'Last 7 days' : filterDays === 30 ? 'Last 30 days' : `Last ${filterDays} days`}
275
+ open={showDateMenu}
276
+ onClick={(e) => {
277
+ e.stopPropagation()
278
+ setShowDateMenu(!showDateMenu)
279
+ setShowAccountMenu(false)
280
+ setShowModelMenu(false)
281
+ }}
282
+ onClear={filterDays !== null ? () => setFilterDays(null) : undefined}
283
+ >
284
+ {showDateMenu && (
285
+ <div className="absolute top-full left-0 mt-1 bg-bg-elevated border border-border rounded-lg shadow-lg py-1 z-10 min-w-[120px]">
286
+ <button
287
+ onClick={() => {
288
+ setFilterDays(null)
289
+ setShowDateMenu(false)
290
+ }}
291
+ className="w-full text-left px-3 py-1.5 text-[10px] text-text-primary hover:bg-bg-hover"
292
+ >
293
+ All time
294
+ </button>
295
+ <button
296
+ onClick={() => {
297
+ setFilterDays(1)
298
+ setShowDateMenu(false)
299
+ }}
300
+ className="w-full text-left px-3 py-1.5 text-[10px] text-text-primary hover:bg-bg-hover"
301
+ >
302
+ Today
303
+ </button>
304
+ <button
305
+ onClick={() => {
306
+ setFilterDays(7)
307
+ setShowDateMenu(false)
308
+ }}
309
+ className="w-full text-left px-3 py-1.5 text-[10px] text-text-primary hover:bg-bg-hover"
310
+ >
311
+ Last 7 days
312
+ </button>
313
+ <button
314
+ onClick={() => {
315
+ setFilterDays(30)
316
+ setShowDateMenu(false)
317
+ }}
318
+ className="w-full text-left px-3 py-1.5 text-[10px] text-text-primary hover:bg-bg-hover"
319
+ >
320
+ Last 30 days
321
+ </button>
322
+ </div>
323
+ )}
324
+ </FilterChip>
325
+ </div>
326
+
327
+ <div className="p-2 border-b border-border">
328
+ <Button
329
+ variant="primary"
330
+ fullWidth
331
+ disabled={!selectedAccountId || connectionStatus !== 'connected' || isCreating || !isAccountReady}
332
+ onClick={async () => {
333
+ if (!selectedAccountId || isCreating || !isAccountReady) {
334
+ return
335
+ }
336
+ setIsCreating(true)
337
+ try {
338
+ const params: { model?: string } = {}
339
+ if (defaultModel?.id) {
340
+ params.model = defaultModel.id
341
+ }
342
+ const result = (await hubClient.request(selectedAccountId, 'thread/start', params)) as {
343
+ thread?: {
344
+ id: string
345
+ preview?: string
346
+ modelProvider?: string
347
+ createdAt?: number
348
+ }
349
+ reasoningEffort?: string | null
350
+ approvalPolicy?: string | null
351
+ }
352
+ if (result.thread) {
353
+ addThread({
354
+ id: result.thread.id,
355
+ accountId: selectedAccountId,
356
+ title: result.thread.preview?.trim() || 'Untitled session',
357
+ preview: result.thread.preview?.trim() || 'New session started',
358
+ model: result.thread.modelProvider ?? 'unknown',
359
+ createdAt: result.thread.createdAt
360
+ ? new Date(result.thread.createdAt * 1000).toLocaleDateString('en-US', {
361
+ month: 'short',
362
+ day: 'numeric',
363
+ })
364
+ : '',
365
+ status: 'idle',
366
+ messageCount: 0,
367
+ })
368
+ setSelectedThreadId(result.thread.id)
369
+ onThreadSelect?.(result.thread.id)
370
+ if (defaultModel?.id) {
371
+ setThreadModel(result.thread.id, defaultModel.id)
372
+ }
373
+ const effort = (result.reasoningEffort ?? defaultEffort) as ReasoningEffort | null
374
+ if (effort) {
375
+ setThreadEffort(result.thread.id, effort)
376
+ }
377
+ const approvalPolicy = normalizeApprovalPolicy(result.approvalPolicy)
378
+ if (approvalPolicy) {
379
+ setThreadApproval(result.thread.id, approvalPolicy)
380
+ }
381
+ }
382
+ } catch {
383
+ // TODO: surface error state.
384
+ } finally {
385
+ setIsCreating(false)
386
+ }
387
+ }}
388
+ >
389
+ <Icons.Plus className="w-4 h-4" />
390
+ New Session
391
+ </Button>
392
+ {selectedAccount && !isAccountReady && (
393
+ <p className="mt-2 text-[10px] text-text-muted text-center">
394
+ Authenticate this account to start a new session.
395
+ </p>
396
+ )}
397
+ </div>
398
+
399
+ <div className="flex-1 overflow-y-auto touch-scroll p-1.5">
400
+ {filteredThreads.map((thread) => {
401
+ const account = accounts.find(a => a.id === thread.accountId)
402
+ const isSelected = selectedThreadId === thread.id
403
+ return (
404
+ <div key={thread.id} className="relative group">
405
+ <button
406
+ onClick={() => {
407
+ setSelectedThreadId(thread.id)
408
+ onThreadSelect?.(thread.id)
409
+ }}
410
+ className={`w-full text-left p-2.5 pr-10 rounded-lg mb-0.5 transition-colors ${
411
+ isSelected
412
+ ? 'bg-bg-elevated border border-border'
413
+ : 'hover:bg-bg-hover active:bg-bg-elevated border border-transparent'
414
+ }`}
415
+ >
416
+ <div className="flex items-start justify-between gap-2 mb-1">
417
+ <h3 className="text-xs font-medium text-text-primary truncate leading-tight flex-1 min-w-0">{thread.title}</h3>
418
+ {thread.status === 'active' && <StatusDot status="active" pulse />}
419
+ </div>
420
+ <p className="text-[10px] text-text-muted truncate mb-1.5">{thread.preview}</p>
421
+ <div className="flex items-center gap-1.5 text-[10px] text-text-muted">
422
+ <Badge>{account?.name}</Badge>
423
+ <span>·</span>
424
+ <span>{thread.model}</span>
425
+ {/* <span className="ml-auto">{thread.messageCount}</span> */}
426
+ </div>
427
+ </button>
428
+ {activeTab !== 'archive' && (
429
+ <div className="absolute top-1.5 right-1.5 opacity-0 group-hover:opacity-100 transition-opacity">
430
+ <IconButton
431
+ icon={<Icons.Archive className="w-3.5 h-3.5 text-text-muted" />}
432
+ size="sm"
433
+ onClick={(e) => {
434
+ e?.stopPropagation()
435
+ void archiveThread(thread.id, thread.accountId)
436
+ }}
437
+ />
438
+ </div>
439
+ )}
440
+ </div>
441
+ )
442
+ })}
443
+ </div>
444
+ </div>
445
+ )
446
+ }
447
+
448
+ function FilterChip({
449
+ label,
450
+ open,
451
+ onClick,
452
+ onClear,
453
+ children
454
+ }: {
455
+ label: string
456
+ open?: boolean
457
+ onClick?: (e: React.MouseEvent) => void
458
+ onClear?: () => void
459
+ children?: React.ReactNode
460
+ }) {
461
+ return (
462
+ <div className="relative">
463
+ <button
464
+ onClick={(e) => {
465
+ if (onClear) {
466
+ e.stopPropagation()
467
+ onClear()
468
+ } else {
469
+ onClick?.(e)
470
+ }
471
+ }}
472
+ className={`flex items-center gap-1 px-2 py-1 rounded-md border text-[10px] transition-colors ${
473
+ open
474
+ ? 'bg-bg-elevated border-border text-text-primary'
475
+ : 'bg-bg-tertiary border-border text-text-secondary hover:bg-bg-hover'
476
+ }`}
477
+ >
478
+ {label}
479
+ {onClear ? (
480
+ <Icons.X className="w-2.5 h-2.5" />
481
+ ) : (
482
+ <Icons.ChevronDown className={`w-2.5 h-2.5 transition-transform ${open ? 'rotate-180' : ''}`} />
483
+ )}
484
+ </button>
485
+ {children}
486
+ </div>
487
+ )
488
+ }