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,417 @@
1
+ import { useState } from 'react'
2
+ import { useAppStore } from '../../store'
3
+ import { hubClient } from '../../services/hub-client'
4
+ import { Avatar, Button, Dialog, IconButton, Icons, SectionHeader, AlertDialog, PromptDialog, CopyDialog } from '../ui'
5
+ import { AccountUsagePanel } from './account-usage-panel'
6
+ import { SettingsDialog } from './settings-dialog'
7
+
8
+ interface SidebarProps {
9
+ onNavigate?: () => void
10
+ }
11
+
12
+ export function Sidebar({ onNavigate }: SidebarProps) {
13
+ const [isAdding, setIsAdding] = useState(false)
14
+ const [isRemoving, setIsRemoving] = useState(false)
15
+ const [showPrompt, setShowPrompt] = useState(false)
16
+ const [showUsage, setShowUsage] = useState(false)
17
+ const [showSettings, setShowSettings] = useState(false)
18
+ const [authPendingId, setAuthPendingId] = useState<string | null>(null)
19
+ const [removeDialog, setRemoveDialog] = useState<{ open: boolean; accountId: string; name: string }>({
20
+ open: false,
21
+ accountId: '',
22
+ name: '',
23
+ })
24
+ const [alertDialog, setAlertDialog] = useState<{ open: boolean; title: string; message: string; variant: 'info' | 'warning' | 'error' }>({
25
+ open: false,
26
+ title: '',
27
+ message: '',
28
+ variant: 'info',
29
+ })
30
+ const [copyDialog, setCopyDialog] = useState<{ open: boolean; url: string }>({
31
+ open: false,
32
+ url: '',
33
+ })
34
+ const {
35
+ accounts,
36
+ selectedAccountId,
37
+ setSelectedAccountId,
38
+ addAccount,
39
+ removeAccount,
40
+ updateAccount,
41
+ connectionStatus,
42
+ showAnalytics,
43
+ setShowAnalytics,
44
+ showReviews,
45
+ setShowReviews,
46
+ } = useAppStore()
47
+
48
+ const getStatusColor = (status: 'online' | 'degraded' | 'offline') => {
49
+ switch (status) {
50
+ case 'online': return 'bg-accent-green'
51
+ case 'degraded': return 'bg-yellow-500'
52
+ case 'offline': return 'bg-text-muted'
53
+ }
54
+ }
55
+
56
+ const handleChatgptAuth = async (accountId: string) => {
57
+ if (connectionStatus !== 'connected') {
58
+ setAlertDialog({
59
+ open: true,
60
+ title: 'Not Connected',
61
+ message: 'Backend not connected. Start the hub and refresh the page.',
62
+ variant: 'error',
63
+ })
64
+ return
65
+ }
66
+ setAuthPendingId(accountId)
67
+ updateAccount(accountId, (prev) => ({ ...prev, status: 'degraded' }))
68
+ try {
69
+ const login = (await hubClient.request(accountId, 'account/login/start', {
70
+ type: 'chatgpt',
71
+ })) as { authUrl?: string }
72
+ if (login?.authUrl) {
73
+ const opened = window.open(login.authUrl, '_blank', 'noopener,noreferrer')
74
+ if (!opened) {
75
+ setCopyDialog({ open: true, url: login.authUrl })
76
+ }
77
+ }
78
+ } catch {
79
+ setAlertDialog({
80
+ open: true,
81
+ title: 'Sign In Failed',
82
+ message: 'Unable to start ChatGPT sign-in. Please try again.',
83
+ variant: 'error',
84
+ })
85
+ } finally {
86
+ setAuthPendingId(null)
87
+ }
88
+ }
89
+
90
+ return (
91
+ <aside className="w-full md:w-60 bg-bg-secondary border-r border-border flex flex-col h-full">
92
+ <div className="p-3 border-b border-border">
93
+ <div className="flex items-center justify-between gap-2.5">
94
+ <div>
95
+ <h1 className="text-sm font-semibold text-text-primary leading-none">better-codex</h1>
96
+ </div>
97
+ {onNavigate && (
98
+ <IconButton
99
+ icon={<Icons.X className="w-4 h-4 text-text-muted" />}
100
+ size="sm"
101
+ onClick={onNavigate}
102
+ className="md:hidden"
103
+ />
104
+ )}
105
+ </div>
106
+ </div>
107
+
108
+ <div className="flex-1 overflow-y-auto p-2">
109
+ <div className="mb-3">
110
+ <SectionHeader>Accounts</SectionHeader>
111
+ <div className="space-y-0.5">
112
+ {accounts.map((account) => (
113
+ <div
114
+ key={account.id}
115
+ onClick={() => {
116
+ setSelectedAccountId(account.id === selectedAccountId ? null : account.id)
117
+ onNavigate?.()
118
+ }}
119
+ onKeyDown={(event) => {
120
+ if (event.key === 'Enter' || event.key === ' ') {
121
+ event.preventDefault()
122
+ setSelectedAccountId(account.id === selectedAccountId ? null : account.id)
123
+ onNavigate?.()
124
+ }
125
+ }}
126
+ role="button"
127
+ tabIndex={0}
128
+ className={`w-full flex items-center gap-2.5 px-2 py-2 rounded-lg transition-colors text-left focus:outline-none focus-visible:ring-1 focus-visible:ring-text-muted ${
129
+ selectedAccountId === account.id
130
+ ? 'bg-bg-elevated border border-border'
131
+ : 'hover:bg-bg-hover border border-transparent'
132
+ }`}
133
+ >
134
+ <div className="relative">
135
+ <Avatar name={account.name} size="sm" />
136
+ <div className={`absolute -bottom-0.5 -right-0.5 w-2.5 h-2.5 rounded-full border-2 border-bg-secondary ${getStatusColor(account.status)}`} />
137
+ </div>
138
+ <div className="flex-1 min-w-0">
139
+ <div className="text-xs font-medium text-text-primary truncate">{account.name}</div>
140
+ <div className="text-[10px] text-text-muted">{account.plan}</div>
141
+ </div>
142
+ <div className="flex items-center gap-1.5">
143
+ {account.status !== 'online' && (
144
+ <button
145
+ type="button"
146
+ onClick={(e) => {
147
+ e.stopPropagation()
148
+ void handleChatgptAuth(account.id)
149
+ }}
150
+ disabled={authPendingId === account.id}
151
+ className="text-[10px] text-accent-green hover:text-text-primary transition-colors disabled:opacity-50 disabled:cursor-not-allowed"
152
+ >
153
+ Sign in
154
+ </button>
155
+ )}
156
+ <button
157
+ type="button"
158
+ onClick={(e) => {
159
+ e.stopPropagation()
160
+ setSelectedAccountId(account.id)
161
+ setShowUsage(true)
162
+ }}
163
+ className="text-[10px] text-text-muted hover:text-text-primary transition-colors"
164
+ >
165
+ {account.rateLimit}%
166
+ </button>
167
+ <button
168
+ type="button"
169
+ onClick={(e) => {
170
+ e.stopPropagation()
171
+ setRemoveDialog({ open: true, accountId: account.id, name: account.name })
172
+ }}
173
+ disabled={account.id === 'default' || isRemoving}
174
+ className="p-1.5 rounded-lg transition-colors hover:bg-bg-hover disabled:opacity-50 disabled:cursor-not-allowed"
175
+ >
176
+ <Icons.Trash className="w-3.5 h-3.5 text-text-muted" />
177
+ </button>
178
+ </div>
179
+ </div>
180
+ ))}
181
+ </div>
182
+ </div>
183
+
184
+ {showUsage && selectedAccountId && (
185
+ <div className="mb-3">
186
+ <div className="flex items-center justify-between mb-2">
187
+ <SectionHeader>Usage</SectionHeader>
188
+ <button
189
+ onClick={() => setShowUsage(false)}
190
+ className="p-1 rounded hover:bg-bg-hover"
191
+ >
192
+ <Icons.X className="w-3 h-3 text-text-muted" />
193
+ </button>
194
+ </div>
195
+ <AccountUsagePanel />
196
+ </div>
197
+ )}
198
+
199
+ <button
200
+ className="w-full flex items-center justify-center gap-1.5 px-2 py-2 rounded-lg border border-dashed border-border text-xs text-text-muted hover:text-text-secondary hover:border-text-muted transition-colors"
201
+ onClick={() => {
202
+ if (isAdding) return
203
+ if (connectionStatus !== 'connected') {
204
+ setAlertDialog({
205
+ open: true,
206
+ title: 'Not Connected',
207
+ message: 'Backend not connected. Start the hub and refresh the page.',
208
+ variant: 'error',
209
+ })
210
+ return
211
+ }
212
+ setShowPrompt(true)
213
+ }}
214
+ >
215
+ <Icons.Plus className="w-3.5 h-3.5" />
216
+ Add Account
217
+ </button>
218
+
219
+ <PromptDialog
220
+ open={showPrompt}
221
+ onClose={() => setShowPrompt(false)}
222
+ onSubmit={async (name) => {
223
+ if (isAdding) return
224
+ setIsAdding(true)
225
+ try {
226
+ const profile = await hubClient.createProfile(name)
227
+ addAccount({
228
+ id: profile.id,
229
+ name: profile.name,
230
+ email: '',
231
+ plan: 'Unknown',
232
+ status: 'offline',
233
+ rateLimit: 0,
234
+ })
235
+ setSelectedAccountId(profile.id)
236
+ await hubClient.startProfile(profile.id)
237
+ updateAccount(profile.id, (prev) => ({ ...prev, status: 'degraded' }))
238
+ const login = (await hubClient.request(profile.id, 'account/login/start', {
239
+ type: 'chatgpt',
240
+ })) as { authUrl?: string }
241
+ if (login?.authUrl) {
242
+ const opened = window.open(login.authUrl, '_blank', 'noopener,noreferrer')
243
+ if (!opened) {
244
+ setCopyDialog({ open: true, url: login.authUrl })
245
+ }
246
+ }
247
+ } catch {
248
+ setAlertDialog({
249
+ open: true,
250
+ title: 'Error',
251
+ message: 'Failed to create account. Please try again.',
252
+ variant: 'error',
253
+ })
254
+ } finally {
255
+ setIsAdding(false)
256
+ }
257
+ }}
258
+ title="Add Account"
259
+ placeholder="Account name..."
260
+ submitLabel="Create"
261
+ />
262
+
263
+ <AlertDialog
264
+ open={alertDialog.open}
265
+ onClose={() => setAlertDialog((prev) => ({ ...prev, open: false }))}
266
+ title={alertDialog.title}
267
+ message={alertDialog.message}
268
+ variant={alertDialog.variant}
269
+ />
270
+
271
+ <CopyDialog
272
+ open={copyDialog.open}
273
+ onClose={() => setCopyDialog({ open: false, url: '' })}
274
+ title="Sign In"
275
+ message="Open this URL in your browser to sign in to your OpenAI account:"
276
+ copyText={copyDialog.url}
277
+ />
278
+
279
+ <Dialog
280
+ open={removeDialog.open}
281
+ onClose={() => setRemoveDialog({ open: false, accountId: '', name: '' })}
282
+ title="Remove Account"
283
+ >
284
+ <p className="text-sm text-text-secondary leading-relaxed mb-4">
285
+ Remove <span className="text-text-primary font-medium">{removeDialog.name}</span> from the hub? This
286
+ does not delete any local sessions.
287
+ </p>
288
+ <div className="flex justify-end gap-2">
289
+ <Button
290
+ variant="ghost"
291
+ size="sm"
292
+ onClick={() => setRemoveDialog({ open: false, accountId: '', name: '' })}
293
+ >
294
+ Cancel
295
+ </Button>
296
+ <Button
297
+ variant="primary"
298
+ size="sm"
299
+ disabled={isRemoving}
300
+ onClick={async () => {
301
+ if (!removeDialog.accountId) {
302
+ return
303
+ }
304
+ if (removeDialog.accountId === 'default') {
305
+ setAlertDialog({
306
+ open: true,
307
+ title: 'Default Profile',
308
+ message: 'The default profile cannot be removed.',
309
+ variant: 'warning',
310
+ })
311
+ return
312
+ }
313
+ if (connectionStatus !== 'connected') {
314
+ setAlertDialog({
315
+ open: true,
316
+ title: 'Not Connected',
317
+ message: 'Backend not connected. Start the hub and refresh the page.',
318
+ variant: 'error',
319
+ })
320
+ return
321
+ }
322
+ const nextSelection = accounts.find((item) => item.id !== removeDialog.accountId)?.id ?? null
323
+ setIsRemoving(true)
324
+ try {
325
+ await hubClient.deleteProfile(removeDialog.accountId)
326
+ removeAccount(removeDialog.accountId)
327
+ if (selectedAccountId === removeDialog.accountId) {
328
+ setSelectedAccountId(nextSelection)
329
+ }
330
+ setRemoveDialog({ open: false, accountId: '', name: '' })
331
+ } catch {
332
+ setAlertDialog({
333
+ open: true,
334
+ title: 'Error',
335
+ message: 'Failed to remove account. Please try again.',
336
+ variant: 'error',
337
+ })
338
+ } finally {
339
+ setIsRemoving(false)
340
+ }
341
+ }}
342
+ >
343
+ Remove
344
+ </Button>
345
+ </div>
346
+ </Dialog>
347
+
348
+ <div className="mt-4">
349
+ <SectionHeader>Workspaces</SectionHeader>
350
+ <div className="space-y-0.5">
351
+ <NavItem
352
+ icon={<Icons.Grid className="w-4 h-4" />}
353
+ label="Multi-account"
354
+ active={!showAnalytics && !showReviews}
355
+ onClick={() => {
356
+ setShowAnalytics(false)
357
+ setShowReviews(false)
358
+ onNavigate?.()
359
+ }}
360
+ />
361
+ <NavItem
362
+ icon={<Icons.Clipboard className="w-4 h-4" />}
363
+ label="Reviews"
364
+ active={showReviews}
365
+ onClick={() => {
366
+ setShowReviews(true)
367
+ onNavigate?.()
368
+ }}
369
+ />
370
+ <NavItem icon={<Icons.Archive className="w-4 h-4" />} label="Archives" />
371
+ <NavItem icon={<Icons.Bolt className="w-4 h-4" />} label="Automations" />
372
+ <NavItem
373
+ icon={<Icons.BarChart className="w-4 h-4" />}
374
+ label="Analytics"
375
+ active={showAnalytics}
376
+ onClick={() => {
377
+ setShowAnalytics(true)
378
+ onNavigate?.()
379
+ }}
380
+ />
381
+ </div>
382
+ </div>
383
+ </div>
384
+
385
+ <div className="p-2 border-t border-border">
386
+ <div className="flex items-center gap-1">
387
+ <IconButton
388
+ icon={<Icons.Settings className="w-4 h-4 text-text-muted" />}
389
+ size="sm"
390
+ onClick={() => setShowSettings(true)}
391
+ />
392
+ <IconButton
393
+ icon={<Icons.Help className="w-4 h-4 text-text-muted" />}
394
+ size="sm"
395
+ />
396
+ </div>
397
+ </div>
398
+
399
+ <SettingsDialog open={showSettings} onClose={() => setShowSettings(false)} />
400
+ </aside>
401
+ )
402
+ }
403
+
404
+ function NavItem({ icon, label, active = false, onClick }: { icon: React.ReactNode; label: string; active?: boolean; onClick?: () => void }) {
405
+ return (
406
+ <button
407
+ onClick={onClick}
408
+ className={`w-full flex items-center gap-2.5 px-2 py-1.5 rounded-lg transition-colors text-left ${
409
+ active
410
+ ? 'bg-bg-elevated border border-border'
411
+ : 'hover:bg-bg-hover border border-transparent'
412
+ }`}>
413
+ <span className={active ? 'text-text-secondary' : 'text-text-muted'}>{icon}</span>
414
+ <span className={`text-xs ${active ? 'text-text-primary' : 'text-text-muted'}`}>{label}</span>
415
+ </button>
416
+ )
417
+ }