repoburg 1.3.201 → 1.3.204

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 (325) hide show
  1. package/agent-pane/backend/dist/index.js +2714 -32395
  2. package/agent-pane/backend/dist/server.js +45304 -0
  3. package/askuser-after-step5-postgres-selected.png +0 -0
  4. package/askuser-after-step7-answer-sent.png +0 -0
  5. package/askuser-card-visible.png +0 -0
  6. package/backend/dist/src/agent-runtime/agent-runtime-event-emitter.d.ts +16 -0
  7. package/backend/dist/src/agent-runtime/agent-runtime-event-emitter.js +87 -0
  8. package/backend/dist/src/agent-runtime/agent-runtime-event-emitter.js.map +1 -1
  9. package/backend/dist/src/agent-runtime/agent-runtime.types.d.ts +16 -1
  10. package/backend/dist/src/app.controller.d.ts +1 -0
  11. package/backend/dist/src/app.service.d.ts +1 -0
  12. package/backend/dist/src/app.service.js +4 -1
  13. package/backend/dist/src/app.service.js.map +1 -1
  14. package/backend/dist/src/interactive-chat/chat.service.d.ts +4 -0
  15. package/backend/dist/src/interactive-chat/chat.service.js +21 -1
  16. package/backend/dist/src/interactive-chat/chat.service.js.map +1 -1
  17. package/backend/dist/src/llm-orchestration/action-handlers/invoke-subagent.handler.js +12 -1
  18. package/backend/dist/src/llm-orchestration/action-handlers/invoke-subagent.handler.js.map +1 -1
  19. package/backend/dist/src/llm-orchestration/llm-orchestration.interfaces.d.ts +1 -0
  20. package/backend/dist/src/llm-orchestration/llm-orchestration.interfaces.js +1 -0
  21. package/backend/dist/src/llm-orchestration/llm-orchestration.interfaces.js.map +1 -1
  22. package/backend/dist/src/llm-orchestration/llm-turn-processor.service.js +2 -0
  23. package/backend/dist/src/llm-orchestration/llm-turn-processor.service.js.map +1 -1
  24. package/backend/dist/tsconfig.build.tsbuildinfo +1 -1
  25. package/daemon/dist/agentPane.js +128 -0
  26. package/daemon/dist/agentPane.spec.js +183 -0
  27. package/daemon/dist/api/services.js +4 -0
  28. package/daemon/dist/index.js +28 -8
  29. package/daemon/dist/serviceManager.js +30 -11
  30. package/daemon/dist/serviceManager.spec.js +26 -0
  31. package/daemon/dist/workspace-validation.js +63 -0
  32. package/daemon/dist/workspace-validation.spec.js +63 -0
  33. package/daemon/package.json +1 -1
  34. package/package.json +15 -2
  35. package/r2-step4-apple-banana.png +0 -0
  36. package/r2-step5-after-rewind-apple-last.png +0 -0
  37. package/r2-step7-apple-cherry.png +0 -0
  38. package/r2-step8-cherry-menu-no-restore.png +0 -0
  39. package/r3-step3-live-task-card-running.png +0 -0
  40. package/r3-step4-after-completion.png +0 -0
  41. package/r3-step5-persistence-after-reload.png +0 -0
  42. package/r3-step6-tasks-tab-card.png +0 -0
  43. package/reverify-answered-state-answer-sent.png +0 -0
  44. package/reverify-card-visible-after-completion.png +0 -0
  45. package/rewind-step3-apple-banana-turns.png +0 -0
  46. package/rewind-step5-ui-unchanged-after-confirmed-rewind.png +0 -0
  47. package/rewind-step6-apple-menu-no-restore.png +0 -0
  48. package/rewind-step7-final-turnlist-unchanged.png +0 -0
  49. package/rewind-step7-negative-error-toast.png +0 -0
  50. package/rewind-step7-negative-error-toast2.png +0 -0
  51. package/subagent-r2-step5-turn-after-reload-no-card.png +0 -0
  52. package/subagent-r2-step6-tasks-panel-card.png +0 -0
  53. package/subagent-step3-tasks-tab-done-card.png +0 -0
  54. package/subagent-step4-turn-stuck-no-cards.png +0 -0
  55. package/agent-pane/.prettierignore +0 -5
  56. package/agent-pane/.prettierrc +0 -8
  57. package/agent-pane/backend/coverage/base.css +0 -224
  58. package/agent-pane/backend/coverage/block-navigation.js +0 -87
  59. package/agent-pane/backend/coverage/clover.xml +0 -887
  60. package/agent-pane/backend/coverage/coverage-final.json +0 -10
  61. package/agent-pane/backend/coverage/favicon.png +0 -0
  62. package/agent-pane/backend/coverage/index.html +0 -176
  63. package/agent-pane/backend/coverage/prettify.css +0 -1
  64. package/agent-pane/backend/coverage/prettify.js +0 -2
  65. package/agent-pane/backend/coverage/sort-arrow-sprite.png +0 -0
  66. package/agent-pane/backend/coverage/sorter.js +0 -210
  67. package/agent-pane/backend/coverage/src/codex/codex-provider.ts.html +0 -1402
  68. package/agent-pane/backend/coverage/src/codex/index.html +0 -131
  69. package/agent-pane/backend/coverage/src/codex/item-mapper.ts.html +0 -331
  70. package/agent-pane/backend/coverage/src/db/index.html +0 -131
  71. package/agent-pane/backend/coverage/src/db/index.ts.html +0 -436
  72. package/agent-pane/backend/coverage/src/db/schema.ts.html +0 -175
  73. package/agent-pane/backend/coverage/src/index.html +0 -116
  74. package/agent-pane/backend/coverage/src/index.ts.html +0 -262
  75. package/agent-pane/backend/coverage/src/routes/index.html +0 -146
  76. package/agent-pane/backend/coverage/src/routes/sessions.ts.html +0 -991
  77. package/agent-pane/backend/coverage/src/routes/turns.ts.html +0 -256
  78. package/agent-pane/backend/coverage/src/routes/workspaces.ts.html +0 -406
  79. package/agent-pane/backend/coverage/src/ws/index.html +0 -116
  80. package/agent-pane/backend/coverage/src/ws/index.ts.html +0 -238
  81. package/agent-pane/backend/data.db +0 -0
  82. package/agent-pane/backend/data.db-shm +0 -0
  83. package/agent-pane/backend/data.db-wal +0 -0
  84. package/agent-pane/backend/package-lock.json +0 -5945
  85. package/agent-pane/backend/package.json +0 -33
  86. package/agent-pane/backend/scripts/backfill-from-jsonl.ts +0 -206
  87. package/agent-pane/backend/scripts/import-session-from-jsonl.ts +0 -264
  88. package/agent-pane/backend/src/claude/claude-message-mapper.test.ts +0 -271
  89. package/agent-pane/backend/src/claude/claude-message-mapper.ts +0 -259
  90. package/agent-pane/backend/src/claude/claude-provider.test.ts +0 -455
  91. package/agent-pane/backend/src/claude/claude-provider.ts +0 -1319
  92. package/agent-pane/backend/src/claude/claude-types.ts +0 -45
  93. package/agent-pane/backend/src/codex/app-server-client.test.ts +0 -183
  94. package/agent-pane/backend/src/codex/app-server-client.ts +0 -247
  95. package/agent-pane/backend/src/codex/app-server-types.ts +0 -259
  96. package/agent-pane/backend/src/codex/codex-provider-runtime-config.test.ts +0 -56
  97. package/agent-pane/backend/src/codex/codex-provider.test.ts +0 -60
  98. package/agent-pane/backend/src/codex/codex-provider.ts +0 -619
  99. package/agent-pane/backend/src/codex/item-mapper.test.ts +0 -221
  100. package/agent-pane/backend/src/codex/item-mapper.ts +0 -86
  101. package/agent-pane/backend/src/codex/shared-app-server.ts +0 -25
  102. package/agent-pane/backend/src/db/index.test.ts +0 -125
  103. package/agent-pane/backend/src/db/index.ts +0 -230
  104. package/agent-pane/backend/src/db/schema.ts +0 -34
  105. package/agent-pane/backend/src/index.ts +0 -89
  106. package/agent-pane/backend/src/integration/__fixtures__/cancel-turn.json +0 -29
  107. package/agent-pane/backend/src/integration/__fixtures__/delta-streaming.json +0 -40
  108. package/agent-pane/backend/src/integration/__fixtures__/failed-turn.json +0 -16
  109. package/agent-pane/backend/src/integration/__fixtures__/mcp-tool-call.json +0 -60
  110. package/agent-pane/backend/src/integration/__fixtures__/missing-item-started.json +0 -34
  111. package/agent-pane/backend/src/integration/__fixtures__/multi-item-turn.json +0 -85
  112. package/agent-pane/backend/src/integration/__fixtures__/multi-turn.json +0 -48
  113. package/agent-pane/backend/src/integration/__fixtures__/simple-turn.json +0 -35
  114. package/agent-pane/backend/src/integration/delta-streaming.test.ts +0 -130
  115. package/agent-pane/backend/src/integration/helpers/app-server-mock.ts +0 -272
  116. package/agent-pane/backend/src/integration/helpers/test-app.ts +0 -110
  117. package/agent-pane/backend/src/integration/helpers/ws-capture.ts +0 -64
  118. package/agent-pane/backend/src/integration/item-lifecycle.test.ts +0 -133
  119. package/agent-pane/backend/src/integration/mcp-tool-call.test.ts +0 -102
  120. package/agent-pane/backend/src/integration/missing-item-started.test.ts +0 -91
  121. package/agent-pane/backend/src/integration/multi-turn.test.ts +0 -121
  122. package/agent-pane/backend/src/integration/session-operations.test.ts +0 -175
  123. package/agent-pane/backend/src/integration/threads-claude-fork.test.ts +0 -241
  124. package/agent-pane/backend/src/integration/threads.test.ts +0 -309
  125. package/agent-pane/backend/src/integration/title-generation.test.ts +0 -109
  126. package/agent-pane/backend/src/integration/turn-cancel.test.ts +0 -111
  127. package/agent-pane/backend/src/integration/turn-failure.test.ts +0 -97
  128. package/agent-pane/backend/src/integration/turn-lifecycle.test.ts +0 -141
  129. package/agent-pane/backend/src/providers/index.ts +0 -16
  130. package/agent-pane/backend/src/providers/registry.test.ts +0 -92
  131. package/agent-pane/backend/src/providers/registry.ts +0 -43
  132. package/agent-pane/backend/src/providers/runtime-provider.ts +0 -43
  133. package/agent-pane/backend/src/repoburg/action-mapping.ts +0 -216
  134. package/agent-pane/backend/src/repoburg/agent-runtime-client.test.ts +0 -271
  135. package/agent-pane/backend/src/repoburg/agent-runtime-client.ts +0 -262
  136. package/agent-pane/backend/src/repoburg/backend-resolver.test.ts +0 -271
  137. package/agent-pane/backend/src/repoburg/backend-resolver.ts +0 -246
  138. package/agent-pane/backend/src/repoburg/repoburg-client.test.ts +0 -159
  139. package/agent-pane/backend/src/repoburg/repoburg-client.ts +0 -181
  140. package/agent-pane/backend/src/repoburg/repoburg-provider.test.ts +0 -723
  141. package/agent-pane/backend/src/repoburg/repoburg-provider.ts +0 -422
  142. package/agent-pane/backend/src/repoburg/turn-projector.test.ts +0 -475
  143. package/agent-pane/backend/src/repoburg/turn-projector.ts +0 -537
  144. package/agent-pane/backend/src/routes/files.ts +0 -31
  145. package/agent-pane/backend/src/routes/mcp.ts +0 -33
  146. package/agent-pane/backend/src/routes/models.test.ts +0 -92
  147. package/agent-pane/backend/src/routes/models.ts +0 -16
  148. package/agent-pane/backend/src/routes/plugins.ts +0 -40
  149. package/agent-pane/backend/src/routes/providers.test.ts +0 -86
  150. package/agent-pane/backend/src/routes/providers.ts +0 -86
  151. package/agent-pane/backend/src/routes/sessions.test.ts +0 -558
  152. package/agent-pane/backend/src/routes/sessions.ts +0 -504
  153. package/agent-pane/backend/src/routes/skills.test.ts +0 -114
  154. package/agent-pane/backend/src/routes/skills.ts +0 -70
  155. package/agent-pane/backend/src/routes/threads.ts +0 -323
  156. package/agent-pane/backend/src/routes/turns.test.ts +0 -196
  157. package/agent-pane/backend/src/routes/turns.ts +0 -103
  158. package/agent-pane/backend/src/routes/workspaces.test.ts +0 -197
  159. package/agent-pane/backend/src/routes/workspaces.ts +0 -128
  160. package/agent-pane/backend/src/ws/index.test.ts +0 -107
  161. package/agent-pane/backend/src/ws/index.ts +0 -51
  162. package/agent-pane/backend/tsconfig.json +0 -25
  163. package/agent-pane/backend/vitest.config.ts +0 -20
  164. package/agent-pane/eslint.config.js +0 -33
  165. package/agent-pane/frontend/coverage/base.css +0 -224
  166. package/agent-pane/frontend/coverage/block-navigation.js +0 -87
  167. package/agent-pane/frontend/coverage/clover.xml +0 -2962
  168. package/agent-pane/frontend/coverage/coverage-final.json +0 -37
  169. package/agent-pane/frontend/coverage/favicon.png +0 -0
  170. package/agent-pane/frontend/coverage/index.html +0 -251
  171. package/agent-pane/frontend/coverage/prettify.css +0 -1
  172. package/agent-pane/frontend/coverage/prettify.js +0 -2
  173. package/agent-pane/frontend/coverage/sort-arrow-sprite.png +0 -0
  174. package/agent-pane/frontend/coverage/sorter.js +0 -210
  175. package/agent-pane/frontend/coverage/src/App.tsx.html +0 -217
  176. package/agent-pane/frontend/coverage/src/AppInitializer.tsx.html +0 -223
  177. package/agent-pane/frontend/coverage/src/api/client.ts.html +0 -187
  178. package/agent-pane/frontend/coverage/src/api/index.html +0 -161
  179. package/agent-pane/frontend/coverage/src/api/sessions.ts.html +0 -316
  180. package/agent-pane/frontend/coverage/src/api/websocket.ts.html +0 -544
  181. package/agent-pane/frontend/coverage/src/api/workspaces.ts.html +0 -199
  182. package/agent-pane/frontend/coverage/src/components/CollapsibleToolCalls.tsx.html +0 -226
  183. package/agent-pane/frontend/coverage/src/components/CommandInputBar.tsx.html +0 -382
  184. package/agent-pane/frontend/coverage/src/components/CommandPalette.tsx.html +0 -715
  185. package/agent-pane/frontend/coverage/src/components/IdeWorkspace.tsx.html +0 -160
  186. package/agent-pane/frontend/coverage/src/components/ModelSelector.tsx.html +0 -478
  187. package/agent-pane/frontend/coverage/src/components/NewSessionDialog.tsx.html +0 -472
  188. package/agent-pane/frontend/coverage/src/components/RenderArea.tsx.html +0 -574
  189. package/agent-pane/frontend/coverage/src/components/RightPanel.tsx.html +0 -205
  190. package/agent-pane/frontend/coverage/src/components/RuntimeIcon.tsx.html +0 -133
  191. package/agent-pane/frontend/coverage/src/components/SessionTabs.tsx.html +0 -217
  192. package/agent-pane/frontend/coverage/src/components/StatusBar.tsx.html +0 -271
  193. package/agent-pane/frontend/coverage/src/components/StatusDot.tsx.html +0 -109
  194. package/agent-pane/frontend/coverage/src/components/Toast.tsx.html +0 -313
  195. package/agent-pane/frontend/coverage/src/components/TurnContent.tsx.html +0 -1216
  196. package/agent-pane/frontend/coverage/src/components/WorkspaceSidebar.tsx.html +0 -1138
  197. package/agent-pane/frontend/coverage/src/components/index.html +0 -326
  198. package/agent-pane/frontend/coverage/src/components/panels/ChangesPanel.tsx.html +0 -247
  199. package/agent-pane/frontend/coverage/src/components/panels/LogsPanel.tsx.html +0 -160
  200. package/agent-pane/frontend/coverage/src/components/panels/MetricsPanel.tsx.html +0 -427
  201. package/agent-pane/frontend/coverage/src/components/panels/TurnsPanel.tsx.html +0 -397
  202. package/agent-pane/frontend/coverage/src/components/panels/index.html +0 -161
  203. package/agent-pane/frontend/coverage/src/constants/index.html +0 -116
  204. package/agent-pane/frontend/coverage/src/constants/models.ts.html +0 -406
  205. package/agent-pane/frontend/coverage/src/hooks/index.html +0 -146
  206. package/agent-pane/frontend/coverage/src/hooks/useEscapeKey.ts.html +0 -124
  207. package/agent-pane/frontend/coverage/src/hooks/useOutsideClick.ts.html +0 -142
  208. package/agent-pane/frontend/coverage/src/hooks/useSessionStatus.ts.html +0 -187
  209. package/agent-pane/frontend/coverage/src/index.html +0 -146
  210. package/agent-pane/frontend/coverage/src/main.tsx.html +0 -115
  211. package/agent-pane/frontend/coverage/src/store/index.html +0 -116
  212. package/agent-pane/frontend/coverage/src/store/store.ts.html +0 -2404
  213. package/agent-pane/frontend/coverage/src/test/component-helpers.ts.html +0 -190
  214. package/agent-pane/frontend/coverage/src/test/fixtures.ts.html +0 -367
  215. package/agent-pane/frontend/coverage/src/test/index.html +0 -131
  216. package/agent-pane/frontend/coverage/src/types/index.html +0 -116
  217. package/agent-pane/frontend/coverage/src/types/index.ts.html +0 -466
  218. package/agent-pane/frontend/coverage/src/utils/changes.ts.html +0 -202
  219. package/agent-pane/frontend/coverage/src/utils/duration.ts.html +0 -127
  220. package/agent-pane/frontend/coverage/src/utils/index.html +0 -131
  221. package/agent-pane/frontend/dist/assets/index-34WaZYsW.css +0 -1
  222. package/agent-pane/frontend/dist/assets/index-y7xlT86e.js +0 -362
  223. package/agent-pane/frontend/dist/index.html +0 -19
  224. package/agent-pane/frontend/index.html +0 -18
  225. package/agent-pane/frontend/package-lock.json +0 -4211
  226. package/agent-pane/frontend/package.json +0 -35
  227. package/agent-pane/frontend/src/App.test.tsx +0 -74
  228. package/agent-pane/frontend/src/App.tsx +0 -44
  229. package/agent-pane/frontend/src/AppInitializer.test.tsx +0 -142
  230. package/agent-pane/frontend/src/AppInitializer.tsx +0 -46
  231. package/agent-pane/frontend/src/__tests__/ThreadsPanel.test.tsx +0 -192
  232. package/agent-pane/frontend/src/api/client.test.ts +0 -126
  233. package/agent-pane/frontend/src/api/client.ts +0 -34
  234. package/agent-pane/frontend/src/api/models.ts +0 -7
  235. package/agent-pane/frontend/src/api/providers.ts +0 -17
  236. package/agent-pane/frontend/src/api/sessions.test.ts +0 -245
  237. package/agent-pane/frontend/src/api/sessions.ts +0 -97
  238. package/agent-pane/frontend/src/api/skills.ts +0 -12
  239. package/agent-pane/frontend/src/api/websocket.test.ts +0 -288
  240. package/agent-pane/frontend/src/api/websocket.ts +0 -238
  241. package/agent-pane/frontend/src/api/workspaces.test.ts +0 -112
  242. package/agent-pane/frontend/src/api/workspaces.ts +0 -38
  243. package/agent-pane/frontend/src/components/CollapsibleToolCalls.test.tsx +0 -49
  244. package/agent-pane/frontend/src/components/CollapsibleToolCalls.tsx +0 -56
  245. package/agent-pane/frontend/src/components/CommandInputBar.test.tsx +0 -133
  246. package/agent-pane/frontend/src/components/CommandInputBar.tsx +0 -584
  247. package/agent-pane/frontend/src/components/CommandPalette.test.tsx +0 -226
  248. package/agent-pane/frontend/src/components/CommandPalette.tsx +0 -194
  249. package/agent-pane/frontend/src/components/CommentableText.test.tsx +0 -196
  250. package/agent-pane/frontend/src/components/CommentableText.tsx +0 -379
  251. package/agent-pane/frontend/src/components/IdeWorkspace.test.tsx +0 -39
  252. package/agent-pane/frontend/src/components/IdeWorkspace.tsx +0 -51
  253. package/agent-pane/frontend/src/components/ModelSelector.test.tsx +0 -129
  254. package/agent-pane/frontend/src/components/ModelSelector.tsx +0 -191
  255. package/agent-pane/frontend/src/components/NewSessionDialog.test.tsx +0 -95
  256. package/agent-pane/frontend/src/components/NewSessionDialog.tsx +0 -129
  257. package/agent-pane/frontend/src/components/RenderArea.test.tsx +0 -160
  258. package/agent-pane/frontend/src/components/RenderArea.tsx +0 -170
  259. package/agent-pane/frontend/src/components/ResizeHandle.tsx +0 -29
  260. package/agent-pane/frontend/src/components/RightPanel.test.tsx +0 -43
  261. package/agent-pane/frontend/src/components/RightPanel.tsx +0 -46
  262. package/agent-pane/frontend/src/components/RuntimeIcon.test.tsx +0 -32
  263. package/agent-pane/frontend/src/components/RuntimeIcon.tsx +0 -16
  264. package/agent-pane/frontend/src/components/SessionTabs.test.tsx +0 -72
  265. package/agent-pane/frontend/src/components/SessionTabs.tsx +0 -49
  266. package/agent-pane/frontend/src/components/StatusBar.test.tsx +0 -56
  267. package/agent-pane/frontend/src/components/StatusBar.tsx +0 -129
  268. package/agent-pane/frontend/src/components/StatusDot.test.tsx +0 -25
  269. package/agent-pane/frontend/src/components/StatusDot.tsx +0 -8
  270. package/agent-pane/frontend/src/components/Toast.test.tsx +0 -79
  271. package/agent-pane/frontend/src/components/Toast.tsx +0 -76
  272. package/agent-pane/frontend/src/components/TurnContent.test.tsx +0 -298
  273. package/agent-pane/frontend/src/components/TurnContent.tsx +0 -548
  274. package/agent-pane/frontend/src/components/WorkspaceSidebar.test.tsx +0 -297
  275. package/agent-pane/frontend/src/components/WorkspaceSidebar.tsx +0 -351
  276. package/agent-pane/frontend/src/components/input/OptionSubPopup.tsx +0 -32
  277. package/agent-pane/frontend/src/components/input/SkillMentionPopup.test.tsx +0 -63
  278. package/agent-pane/frontend/src/components/input/SkillMentionPopup.tsx +0 -78
  279. package/agent-pane/frontend/src/components/input/SlashPopup.test.tsx +0 -96
  280. package/agent-pane/frontend/src/components/input/SlashPopup.tsx +0 -82
  281. package/agent-pane/frontend/src/components/panels/ChangesPanel.test.tsx +0 -61
  282. package/agent-pane/frontend/src/components/panels/ChangesPanel.tsx +0 -54
  283. package/agent-pane/frontend/src/components/panels/TasksPanel.test.tsx +0 -97
  284. package/agent-pane/frontend/src/components/panels/TasksPanel.tsx +0 -208
  285. package/agent-pane/frontend/src/components/panels/ThreadsPanel.tsx +0 -382
  286. package/agent-pane/frontend/src/components/panels/TurnsPanel.test.tsx +0 -83
  287. package/agent-pane/frontend/src/components/panels/TurnsPanel.tsx +0 -126
  288. package/agent-pane/frontend/src/constants/models.test.ts +0 -97
  289. package/agent-pane/frontend/src/constants/models.ts +0 -166
  290. package/agent-pane/frontend/src/hooks/useEscapeKey.test.ts +0 -42
  291. package/agent-pane/frontend/src/hooks/useEscapeKey.ts +0 -13
  292. package/agent-pane/frontend/src/hooks/useOutsideClick.test.ts +0 -92
  293. package/agent-pane/frontend/src/hooks/useOutsideClick.ts +0 -19
  294. package/agent-pane/frontend/src/hooks/useProvider.ts +0 -44
  295. package/agent-pane/frontend/src/hooks/useResizable.test.ts +0 -97
  296. package/agent-pane/frontend/src/hooks/useResizable.ts +0 -75
  297. package/agent-pane/frontend/src/hooks/useSessionStatus.test.ts +0 -116
  298. package/agent-pane/frontend/src/hooks/useSessionStatus.ts +0 -34
  299. package/agent-pane/frontend/src/main.test.tsx +0 -37
  300. package/agent-pane/frontend/src/main.tsx +0 -10
  301. package/agent-pane/frontend/src/store/sessionSlice.ts +0 -440
  302. package/agent-pane/frontend/src/store/store.test.ts +0 -1319
  303. package/agent-pane/frontend/src/store/store.ts +0 -37
  304. package/agent-pane/frontend/src/store/threadSlice.ts +0 -136
  305. package/agent-pane/frontend/src/store/uiSlice.ts +0 -133
  306. package/agent-pane/frontend/src/store/workspaceSlice.ts +0 -77
  307. package/agent-pane/frontend/src/store/wsSlice.ts +0 -361
  308. package/agent-pane/frontend/src/styles/app.css +0 -611
  309. package/agent-pane/frontend/src/styles/tokens.css +0 -62
  310. package/agent-pane/frontend/src/test/component-helpers.ts +0 -38
  311. package/agent-pane/frontend/src/test/fixtures.ts +0 -100
  312. package/agent-pane/frontend/src/test-setup.ts +0 -5
  313. package/agent-pane/frontend/src/types/index.test.ts +0 -128
  314. package/agent-pane/frontend/src/types/index.ts +0 -132
  315. package/agent-pane/frontend/src/utils/changes.test.ts +0 -81
  316. package/agent-pane/frontend/src/utils/changes.ts +0 -39
  317. package/agent-pane/frontend/src/utils/duration.test.ts +0 -75
  318. package/agent-pane/frontend/src/utils/duration.ts +0 -14
  319. package/agent-pane/frontend/tsconfig.json +0 -25
  320. package/agent-pane/frontend/vite.config.ts +0 -22
  321. package/agent-pane/frontend/vitest.config.ts +0 -29
  322. package/agent-pane/package-lock.json +0 -1527
  323. package/agent-pane/package.json +0 -19
  324. package/agent-pane/shared/provider-types.ts +0 -67
  325. package/agent-pane/shared/types.ts +0 -167
@@ -1,170 +0,0 @@
1
- import { useStore } from '../store/store.ts';
2
- import TurnContent from './TurnContent.tsx';
3
- import { collectFileChanges } from '../utils/changes.ts';
4
- import { memo, useEffect, useRef } from 'react';
5
-
6
- function RenderAreaBase() {
7
- const {
8
- activeSessionId,
9
- sessionDetails,
10
- activeTurnIndex,
11
- activeRightTab,
12
- changesScope,
13
- submittingPrompt,
14
- loading,
15
- selectedChangePath,
16
- setSelectedChangePath,
17
- } = useStore();
18
-
19
- const detail = activeSessionId ? sessionDetails[activeSessionId] : null;
20
- const turns = detail?.turns ?? [];
21
- const renderScrollRef = useRef<HTMLDivElement>(null);
22
-
23
- // Auto-scroll to bottom when session changes or new turns are added
24
- useEffect(() => {
25
- if (renderScrollRef.current) {
26
- renderScrollRef.current.scrollTop = renderScrollRef.current.scrollHeight;
27
- }
28
- }, [activeSessionId, turns.length]);
29
-
30
- // F4: Scroll to selected change path when it changes
31
- useEffect(() => {
32
- if (!selectedChangePath || activeRightTab !== 'changes') return;
33
- const timer = setTimeout(() => {
34
- const sanitized = selectedChangePath.replace(/[^a-zA-Z0-9]/g, '-');
35
- const el = document.getElementById(`diff-${sanitized}`);
36
- if (el) {
37
- el.scrollIntoView({ behavior: 'smooth', block: 'start' });
38
- el.classList.add('diff-highlight');
39
- setTimeout(() => el.classList.remove('diff-highlight'), 1500);
40
- }
41
- setSelectedChangePath(null);
42
- }, 50);
43
- return () => clearTimeout(timer);
44
- }, [selectedChangePath, activeRightTab, setSelectedChangePath]);
45
-
46
- // When Changes tab is active, render file changes from items
47
- if (activeRightTab === 'changes') {
48
- if (!detail) {
49
- return (
50
- <div className="render-scroll">
51
- <div className="empty-state">No session selected</div>
52
- </div>
53
- );
54
- }
55
-
56
- const deduped = collectFileChanges(turns, changesScope, activeTurnIndex);
57
-
58
- if (deduped.length === 0) {
59
- return (
60
- <div className="render-scroll">
61
- <div className="render-content">
62
- <p style={{ color: 'var(--text-muted)', fontStyle: 'italic' }}>No file changes in this scope.</p>
63
- </div>
64
- </div>
65
- );
66
- }
67
-
68
- return (
69
- <div className="render-scroll" ref={renderScrollRef}>
70
- <div className="render-content">
71
- <div style={{ marginBottom: '20px' }}>
72
- <span style={{ fontSize: '16px', fontWeight: 600, color: 'var(--text)' }}>
73
- {changesScope === 'session' ? 'All Session Changes' : 'Current Turn Changes'}
74
- </span>
75
- <span style={{ fontSize: '12px', color: 'var(--text-muted)', marginLeft: '8px' }}>
76
- {deduped.length} file{deduped.length > 1 ? 's' : ''}
77
- </span>
78
- </div>
79
- {deduped.map((c) => {
80
- const fileName = c.path.split('/').pop() || c.path;
81
- const dir = c.path.replace('/' + fileName, '');
82
- const statusLabel = c.kind === 'add' ? 'Added' : c.kind === 'delete' ? 'Deleted' : 'Modified';
83
- const statusClass = c.kind === 'add' ? 'fc-add' : c.kind === 'delete' ? 'fc-del' : 'fc-mod';
84
- const statusChar = c.kind === 'add' ? 'A' : c.kind === 'delete' ? 'D' : 'M';
85
- const sanitizedId = c.path.replace(/[^a-zA-Z0-9]/g, '-');
86
-
87
- return (
88
- <div key={c.path}>
89
- <div className="diff-file-header" id={`diff-${sanitizedId}`}>
90
- <span className={`diff-file-status ${statusClass}`}>{statusChar}</span>
91
- <span className="diff-file-name">
92
- {dir && <span style={{ color: 'var(--text-muted)' }}>{dir}/</span>}
93
- {fileName}
94
- </span>
95
- <span className="diff-file-stats">
96
- <span className={`diff-file-badge ${statusClass}`}>{statusLabel}</span>
97
- </span>
98
- </div>
99
- <p style={{ color: 'var(--text-muted)', fontSize: '12px', fontStyle: 'italic', padding: '8px 16px' }}>
100
- Diff content not available (Codex SDK limitation).
101
- </p>
102
- </div>
103
- );
104
- })}
105
- </div>
106
- </div>
107
- );
108
- }
109
-
110
- // F2: Skeleton loading when fetching session detail for the first time
111
- if (loading && !detail) {
112
- return (
113
- <div className="render-scroll">
114
- <div className="skeleton-bars">
115
- <div className="skeleton-bar" />
116
- <div className="skeleton-bar" />
117
- <div className="skeleton-bar" />
118
- <div className="skeleton-bar" />
119
- <div className="skeleton-bar" />
120
- </div>
121
- </div>
122
- );
123
- }
124
-
125
- if (!detail) {
126
- return (
127
- <div className="render-scroll">
128
- <div className="empty-state">No session selected</div>
129
- </div>
130
- );
131
- }
132
-
133
- // Render ALL turns, scrollable
134
- return (
135
- <div className="render-scroll" ref={renderScrollRef}>
136
- {/* Submitting prompt — shown at top while waiting for API response */}
137
- {submittingPrompt && (
138
- <div className="turn-block">
139
- <div className="turn-group">
140
- <div className="render-content">
141
- <div className="turn-header">
142
- <span className="turn-header-icon" style={{ background: 'var(--amber)', color: '#fff' }}>
143
- U
144
- </span>
145
- <span className="turn-header-title">You</span>
146
- </div>
147
- <p className="user-message">{submittingPrompt}</p>
148
- </div>
149
- <div className="render-content">
150
- <p style={{ color: 'var(--text-muted)', fontStyle: 'italic' }}>Sending…</p>
151
- </div>
152
- </div>
153
- </div>
154
- )}
155
-
156
- {turns.length === 0 && !submittingPrompt && (
157
- <div className="empty-state">No turns yet. Send a message below to start.</div>
158
- )}
159
-
160
- {turns.filter((t) => !t.thread_id).map((turn, idx) => (
161
- <div key={turn.id} id={`turn-${idx}`} className="turn-block">
162
- <TurnContent turn={turn} runtimeType={detail.runtime_type} threads={detail.threads ?? []} />
163
- </div>
164
- ))}
165
- </div>
166
- );
167
- }
168
-
169
- const RenderArea = memo(RenderAreaBase);
170
- export default RenderArea;
@@ -1,29 +0,0 @@
1
- import { useResizable, type ResizeSide } from '../hooks/useResizable.ts';
2
-
3
- interface ResizeHandleProps {
4
- side: ResizeSide;
5
- minWidth: number;
6
- maxWidth: number;
7
- getWidth: () => number;
8
- setWidth: (w: number) => void;
9
- }
10
-
11
- /**
12
- * Thin draggable gutter rendered as its own CSS grid track. Sits between a
13
- * sidebar and the center pane. Dragging adjusts the adjacent sidebar width.
14
- */
15
- export default function ResizeHandle({ side, minWidth, maxWidth, getWidth, setWidth }: ResizeHandleProps) {
16
- const { onPointerDown, dragging } = useResizable({ side, minWidth, maxWidth, getWidth, setWidth });
17
-
18
- return (
19
- <div
20
- className={`resize-handle ${side}${dragging ? ' dragging' : ''}`}
21
- onPointerDown={onPointerDown}
22
- // Gutter shouldn't catch content scroll / text selection; keyboard a11y is non-goal here.
23
- role="separator"
24
- aria-orientation="vertical"
25
- aria-label={`Resize ${side} panel`}
26
- data-resizing={dragging ? '' : undefined}
27
- />
28
- );
29
- }
@@ -1,43 +0,0 @@
1
- import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
2
- import { render, screen, fireEvent, cleanup } from '@testing-library/react';
3
- import RightPanel from './RightPanel.tsx';
4
- import { resetStore } from '../test/component-helpers.ts';
5
-
6
- describe('RightPanel', () => {
7
- beforeEach(() => {
8
- resetStore();
9
- });
10
- afterEach(() => {
11
- cleanup();
12
- });
13
-
14
- it('renders 4 tab buttons', () => {
15
- render(<RightPanel />);
16
- expect(screen.getByText('Turns')).toBeInTheDocument();
17
- expect(screen.getByText('Changes')).toBeInTheDocument();
18
- expect(screen.getByText('Tasks')).toBeInTheDocument();
19
- expect(screen.getByText('Threads')).toBeInTheDocument();
20
- });
21
-
22
- it('active tab has active class', () => {
23
- resetStore({ activeRightTab: 'changes' });
24
- render(<RightPanel />);
25
- const changesTab = screen.getByText('Changes').closest('.right-tab');
26
- expect(changesTab).toHaveClass('active');
27
- });
28
-
29
- it('click on tab calls switchRightTab', () => {
30
- const switchRightTab = vi.fn();
31
- resetStore({ switchRightTab });
32
- render(<RightPanel />);
33
- fireEvent.click(screen.getByText('Changes'));
34
- expect(switchRightTab).toHaveBeenCalledWith('changes');
35
- });
36
-
37
- it('renders TurnsPanel when activeRightTab is turns', () => {
38
- resetStore({ activeRightTab: 'turns' });
39
- render(<RightPanel />);
40
- // TurnsPanel renders "No turns yet" when no active session
41
- expect(screen.getByText('No turns yet')).toBeInTheDocument();
42
- });
43
- });
@@ -1,46 +0,0 @@
1
- import { useStore } from '../store/store.ts';
2
- import TurnsPanel from './panels/TurnsPanel.tsx';
3
- import ChangesPanel from './panels/ChangesPanel.tsx';
4
- import TasksPanel from './panels/TasksPanel.tsx';
5
- import ThreadsPanel from './panels/ThreadsPanel.tsx';
6
- import type { RightTab } from '../types/index.ts';
7
-
8
- export default function RightPanel() {
9
- const { activeRightTab, switchRightTab, activeSessionId, sessionDetails } = useStore();
10
-
11
- // Badge: active background task count for the Tasks tab
12
- const detail = activeSessionId ? sessionDetails[activeSessionId] : null;
13
- const activeTaskCount = detail?.backgroundTasks?.length ?? 0;
14
- const openThreadCount = detail?.threads?.filter((t) => t.status === 'open').length ?? 0;
15
-
16
- const tabs: { id: RightTab; label: string; badge?: number }[] = [
17
- { id: 'turns', label: 'Turns' },
18
- { id: 'changes', label: 'Changes' },
19
- { id: 'tasks', label: 'Tasks', badge: activeTaskCount || undefined },
20
- { id: 'threads', label: 'Threads', badge: openThreadCount || undefined },
21
- ];
22
-
23
- return (
24
- <div className="ide-right">
25
- <div className="right-tabs">
26
- {tabs.map((tab) => (
27
- <div
28
- key={tab.id}
29
- className={`right-tab ${activeRightTab === tab.id ? 'active' : ''}`}
30
- onClick={() => switchRightTab(tab.id)}
31
- >
32
- {tab.label}
33
- {tab.badge ? <span className="right-tab-badge">{tab.badge}</span> : null}
34
- </div>
35
- ))}
36
- </div>
37
-
38
- <div className="right-content">
39
- {activeRightTab === 'turns' && <TurnsPanel />}
40
- {activeRightTab === 'changes' && <ChangesPanel />}
41
- {activeRightTab === 'tasks' && <TasksPanel />}
42
- {activeRightTab === 'threads' && <ThreadsPanel />}
43
- </div>
44
- </div>
45
- );
46
- }
@@ -1,32 +0,0 @@
1
- import { describe, it, expect } from 'vitest';
2
- import { render } from '@testing-library/react';
3
- import RuntimeIcon from './RuntimeIcon.tsx';
4
-
5
- describe('RuntimeIcon', () => {
6
- it('renders span with runtime-icon and iconClass', () => {
7
- const { container } = render(<RuntimeIcon iconClass="ic-codex" iconText="CX" />);
8
- const span = container.firstChild as HTMLElement;
9
- expect(span.tagName).toBe('SPAN');
10
- expect(span.className).toBe('runtime-icon ic-codex');
11
- });
12
-
13
- it('renders iconText as child content', () => {
14
- const { container } = render(<RuntimeIcon iconClass="ic-claude" iconText="CL" />);
15
- const span = container.firstChild as HTMLElement;
16
- expect(span.textContent).toBe('CL');
17
- });
18
-
19
- it('does not set inline style when size is default (20)', () => {
20
- const { container } = render(<RuntimeIcon iconClass="ic-codex" iconText="CX" />);
21
- const span = container.firstChild as HTMLElement;
22
- expect(span.style.cssText).toBe('');
23
- });
24
-
25
- it('sets width, height, and fontSize inline style when size is custom', () => {
26
- const { container } = render(<RuntimeIcon iconClass="ic-codex" iconText="CX" size={14} />);
27
- const span = container.firstChild as HTMLElement;
28
- expect(span.style.width).toBe('14px');
29
- expect(span.style.height).toBe('14px');
30
- expect(span.style.fontSize).toBe('6.3px'); // 14 * 0.45
31
- });
32
- });
@@ -1,16 +0,0 @@
1
- interface Props {
2
- iconClass: string;
3
- iconText: string;
4
- size?: number;
5
- }
6
-
7
- export default function RuntimeIcon({ iconClass, iconText, size = 20 }: Props) {
8
- return (
9
- <span
10
- className={`runtime-icon ${iconClass}`}
11
- style={size !== 20 ? { width: size, height: size, fontSize: size * 0.45 } : undefined}
12
- >
13
- {iconText}
14
- </span>
15
- );
16
- }
@@ -1,72 +0,0 @@
1
- import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
2
- import { render, screen, fireEvent, cleanup } from '@testing-library/react';
3
- import SessionTabs from './SessionTabs.tsx';
4
- import { resetStore } from '../test/component-helpers.ts';
5
- import { makeSession } from '../test/fixtures.ts';
6
-
7
- describe('SessionTabs', () => {
8
- beforeEach(() => {
9
- resetStore();
10
- });
11
- afterEach(() => {
12
- cleanup();
13
- });
14
-
15
- it('renders one tab per openTabs entry', () => {
16
- const s1 = makeSession('s1', { title: 'Session 1' });
17
- const s2 = makeSession('s2', { title: 'Session 2' });
18
- resetStore({
19
- openTabs: ['s1', 's2'],
20
- sessions: [s1, s2],
21
- activeSessionId: 's1',
22
- });
23
- render(<SessionTabs />);
24
- expect(screen.getByText('Session 1')).toBeInTheDocument();
25
- expect(screen.getByText('Session 2')).toBeInTheDocument();
26
- });
27
-
28
- it('active tab has active class', () => {
29
- const s1 = makeSession('s1', { title: 'Tab One' });
30
- resetStore({ openTabs: ['s1'], sessions: [s1], activeSessionId: 's1' });
31
- render(<SessionTabs />);
32
- const tab = screen.getByText('Tab One').closest('.ide-tab');
33
- expect(tab).toHaveClass('active');
34
- });
35
-
36
- it('click on tab calls selectSession', () => {
37
- const selectSession = vi.fn().mockResolvedValue(undefined);
38
- const s1 = makeSession('s1', { title: 'Tab One' });
39
- const s2 = makeSession('s2', { title: 'Tab Two' });
40
- resetStore({ openTabs: ['s1', 's2'], sessions: [s1, s2], activeSessionId: 's1', selectSession });
41
- render(<SessionTabs />);
42
- fireEvent.click(screen.getByText('Tab Two'));
43
- expect(selectSession).toHaveBeenCalledWith('s2');
44
- });
45
-
46
- it('close button calls closeTab with correct id', () => {
47
- const closeTab = vi.fn();
48
- const s1 = makeSession('s1', { title: 'Close Me' });
49
- resetStore({ openTabs: ['s1'], sessions: [s1], activeSessionId: 's1', closeTab });
50
- render(<SessionTabs />);
51
- const closeBtn = screen.getByText('Close Me').closest('.ide-tab')!.querySelector('.ide-tab-close')!;
52
- fireEvent.click(closeBtn);
53
- expect(closeTab).toHaveBeenCalledWith('s1');
54
- });
55
-
56
- it('plus button calls setShowNewSessionDialog(true)', () => {
57
- const setShowNewSessionDialog = vi.fn();
58
- resetStore({ openTabs: [], sessions: [], setShowNewSessionDialog });
59
- render(<SessionTabs />);
60
- const addBtn = document.querySelector('.ide-tab-add')!;
61
- fireEvent.click(addBtn);
62
- expect(setShowNewSessionDialog).toHaveBeenCalledWith(true);
63
- });
64
-
65
- it('missing session in openTabs returns null for that tab', () => {
66
- resetStore({ openTabs: ['s1'], sessions: [], activeSessionId: null });
67
- render(<SessionTabs />);
68
- // No tab content rendered — only the + button
69
- expect(document.querySelectorAll('.ide-tab')).toHaveLength(0);
70
- expect(document.querySelector('.ide-tab-add')).toBeInTheDocument();
71
- });
72
- });
@@ -1,49 +0,0 @@
1
- import { useStore } from '../store/store.ts';
2
- import StatusDot from './StatusDot.tsx';
3
- import RuntimeIcon from './RuntimeIcon.tsx';
4
- import { deriveSessionStatus } from '../hooks/useSessionStatus.ts';
5
- import { X, Plus } from 'lucide-react';
6
-
7
- export default function SessionTabs() {
8
- const openTabs = useStore((s) => s.openTabs);
9
- const activeSessionId = useStore((s) => s.activeSessionId);
10
- const sessions = useStore((s) => s.sessions);
11
- const selectSession = useStore((s) => s.selectSession);
12
- const closeTab = useStore((s) => s.closeTab);
13
- const setShowNewSessionDialog = useStore((s) => s.setShowNewSessionDialog);
14
-
15
- return (
16
- <div className="ide-tabs">
17
- {openTabs.map((sid) => {
18
- const s = sessions.find((x) => x.id === sid);
19
- if (!s) return null;
20
- const status = deriveSessionStatus(s);
21
- const isActive = activeSessionId === sid;
22
- return (
23
- <div
24
- key={sid}
25
- className={`ide-tab ${isActive ? 'active' : ''}`}
26
- onClick={() => selectSession(sid)}
27
- >
28
- <RuntimeIcon iconClass={status.iconClass} iconText={status.iconText} size={14} />
29
- {s.title || sid.slice(0, 8)}
30
- {status.dot && <StatusDot status={status.dot} />}
31
- <span
32
- className="ide-tab-close"
33
- onClick={(e) => {
34
- e.stopPropagation();
35
- closeTab(sid);
36
- }}
37
- >
38
- <X size={12} />
39
- </span>
40
- </div>
41
- );
42
- })}
43
-
44
- <div className="ide-tab-add" title="New Session" onClick={() => setShowNewSessionDialog(true)}>
45
- <Plus size={14} />
46
- </div>
47
- </div>
48
- );
49
- }
@@ -1,56 +0,0 @@
1
- import { describe, it, expect, beforeEach, afterEach } from 'vitest';
2
- import { render, screen, cleanup } from '@testing-library/react';
3
- import StatusBar from './StatusBar.tsx';
4
- import { resetStore } from '../test/component-helpers.ts';
5
- import { makeSession, makeDetail, makeTurn } from '../test/fixtures.ts';
6
-
7
- describe('StatusBar', () => {
8
- beforeEach(() => {
9
- resetStore();
10
- });
11
- afterEach(() => {
12
- cleanup();
13
- });
14
-
15
- it('shows AgentPane and Offline when no session', () => {
16
- render(<StatusBar />);
17
- expect(screen.getByText('AgentPane')).toBeInTheDocument();
18
- expect(screen.getByText('Offline')).toBeInTheDocument();
19
- });
20
-
21
- it('shows Live when wsConnected is true', () => {
22
- resetStore({ wsConnected: true });
23
- render(<StatusBar />);
24
- expect(screen.getByText('Live')).toBeInTheDocument();
25
- });
26
-
27
- it('shows Offline when wsConnected is false', () => {
28
- resetStore({ wsConnected: false });
29
- render(<StatusBar />);
30
- expect(screen.getByText('Offline')).toBeInTheDocument();
31
- });
32
-
33
- it('shows status label for active session', () => {
34
- const session = makeSession('s1', { status: 'active', is_running: 1 });
35
- resetStore({ activeSessionId: 's1', sessions: [session], sessionDetails: { s1: makeDetail('s1') } });
36
- render(<StatusBar />);
37
- expect(screen.getByText('Running')).toBeInTheDocument();
38
- });
39
-
40
- it('shows duration for active session', () => {
41
- const session = makeSession('s1', { status: 'active', is_running: 0 });
42
- const t1 = makeTurn('t1', 's1', {
43
- created_at: '2025-01-01T00:00:00Z',
44
- updated_at: '2025-01-01T00:00:05Z',
45
- });
46
- const detail = makeDetail('s1', [t1]);
47
- resetStore({
48
- activeSessionId: 's1',
49
- sessions: [session],
50
- sessionDetails: { s1: detail },
51
- });
52
- render(<StatusBar />);
53
- // Duration = 5 seconds between first turn created_at and last turn updated_at
54
- expect(screen.getByText('5s')).toBeInTheDocument();
55
- });
56
- });
@@ -1,129 +0,0 @@
1
- import { useStore } from '../store/store.ts';
2
- import StatusDot from './StatusDot.tsx';
3
- import ModelSelector from './ModelSelector.tsx';
4
- import { deriveSessionStatus } from '../hooks/useSessionStatus.ts';
5
- import { calcSessionDuration } from '../utils/duration.ts';
6
- import { Zap, Command, Terminal } from 'lucide-react';
7
- import type { ContextUsage } from '../types/index.ts';
8
-
9
- /** Format a token count as compact K/M (e.g. 30000 → "30K", 1500000 → "1.5M"). */
10
- function formatTokens(n: number): string {
11
- if (n >= 1_000_000) return `${(n / 1_000_000).toFixed(n % 1_000_000 === 0 ? 0 : 1)}M`;
12
- if (n >= 1_000) return `${Math.round(n / 1_000)}K`;
13
- return String(n);
14
- }
15
-
16
- export default function StatusBar() {
17
- const activeSessionId = useStore((s) => s.activeSessionId);
18
- const session = useStore((s) =>
19
- activeSessionId ? (s.sessions.find((x) => x.id === activeSessionId) ?? null) : null,
20
- );
21
- const detail = useStore((s) => (activeSessionId ? (s.sessionDetails[activeSessionId] ?? null) : null));
22
- const wsConnected = useStore((s) => s.wsConnected);
23
- const switchRightTab = useStore((s) => s.switchRightTab);
24
- const contextUsage = detail?.contextUsage ?? null;
25
-
26
- if (!session) {
27
- return (
28
- <div className="ide-statusbar">
29
- <div className="sb-item">
30
- <Zap size={14} /> AgentPane
31
- </div>
32
- <div className="sb-divider">|</div>
33
- <div className="sb-item" title={wsConnected ? 'WebSocket connected' : 'WebSocket disconnected'}>
34
- <span className={`status-dot ${wsConnected ? 'done' : 'error'}`} />
35
- {wsConnected ? 'Live' : 'Offline'}
36
- </div>
37
- <div className="sb-spacer" />
38
- <div className="sb-item">
39
- <Command size={14} /> K
40
- </div>
41
- </div>
42
- );
43
- }
44
-
45
- const status = deriveSessionStatus(session);
46
- const duration = calcSessionDuration(detail, status.isRunning);
47
-
48
- // Count active background tasks from two sources:
49
- // 1. backgroundTasks (from background_tasks_changed WS event — Monitor only)
50
- // 2. in_progress background_task items in turns (from item:started/updated — both Monitor & Subagent)
51
- // Deduplicate by task_id.
52
- const bgTasks = detail?.backgroundTasks ?? [];
53
- const activeTaskIds = new Set<string>();
54
- const activeTaskTypes = new Map<string, string>();
55
-
56
- for (const t of bgTasks) {
57
- activeTaskIds.add(t.task_id);
58
- activeTaskTypes.set(t.task_id, t.task_type);
59
- }
60
-
61
- for (const turn of detail?.turns ?? []) {
62
- for (const item of turn.items) {
63
- if (item.type === 'background_task' && item.status === 'in_progress') {
64
- const taskId = (item.data?.task_id as string) ?? item.external_item_id;
65
- const taskType = (item.data?.task_type as string) ?? 'unknown';
66
- activeTaskIds.add(taskId);
67
- activeTaskTypes.set(taskId, taskType);
68
- }
69
- }
70
- }
71
-
72
- const monitorCount = [...activeTaskTypes.values()].filter((t) => t === 'local_bash').length;
73
-
74
- return (
75
- <div className="ide-statusbar">
76
- <div className="sb-item">
77
- <Zap size={14} /> AgentPane
78
- </div>
79
- <div className="sb-divider">|</div>
80
- <ModelSelector />
81
- {monitorCount > 0 && (
82
- <>
83
- <div className="sb-divider">|</div>
84
- <div
85
- className="sb-item"
86
- style={{ color: 'var(--amber)', cursor: 'pointer' }}
87
- title={`${monitorCount} Monitor task${monitorCount > 1 ? 's' : ''} running — click to view`}
88
- onClick={() => switchRightTab('tasks')}
89
- >
90
- <Terminal size={14} /> {monitorCount}
91
- </div>
92
- </>
93
- )}
94
- <div className="sb-spacer" />
95
- <div className="sb-item" title={wsConnected ? 'WebSocket connected' : 'WebSocket disconnected'}>
96
- <span className={`status-dot ${wsConnected ? 'done' : 'error'}`} />
97
- {wsConnected ? 'Live' : 'Offline'}
98
- </div>
99
- <div className="sb-divider">|</div>
100
- <div className="sb-item" style={session.status === 'failed' ? { color: 'var(--red)' } : undefined}>
101
- <StatusDot status={status.dot} /> {status.label}
102
- </div>
103
- {contextUsage && <ContextBar usage={contextUsage} />}
104
- <div className="sb-divider">|</div>
105
- <div className="sb-item">{duration}</div>
106
- <div className="sb-divider">|</div>
107
- <div className="sb-item">
108
- <Command size={14} /> K
109
- </div>
110
- </div>
111
- );
112
- }
113
-
114
- /** Compact context usage text — "%40 / 400K / 1M" format, hover tooltip. */
115
- function ContextBar({ usage }: { usage: ContextUsage }) {
116
- const colorVar = usage.percentage >= 85 ? 'var(--red)' : usage.percentage >= 60 ? 'var(--amber)' : 'var(--green)';
117
- const tooltip = `${formatTokens(usage.totalTokens)} / ${formatTokens(usage.contextWindow)} tokens`;
118
-
119
- return (
120
- <>
121
- <div className="sb-divider">|</div>
122
- <div className="sb-context" title={tooltip}>
123
- <span className="sb-context-text" style={{ color: colorVar }}>
124
- {Math.round(usage.percentage)}% / {formatTokens(usage.totalTokens)} / {formatTokens(usage.contextWindow)}
125
- </span>
126
- </div>
127
- </>
128
- );
129
- }
@@ -1,25 +0,0 @@
1
- import { describe, it, expect } from 'vitest';
2
- import { render } from '@testing-library/react';
3
- import StatusDot from './StatusDot.tsx';
4
-
5
- describe('StatusDot', () => {
6
- it('renders span with status-dot and status class', () => {
7
- const { container } = render(<StatusDot status="running" />);
8
- const span = container.firstChild as HTMLElement;
9
- expect(span.tagName).toBe('SPAN');
10
- expect(span.className).toBe('status-dot running');
11
- });
12
-
13
- it('does not set inline style when size is default (6)', () => {
14
- const { container } = render(<StatusDot status="error" />);
15
- const span = container.firstChild as HTMLElement;
16
- expect(span.style.cssText).toBe('');
17
- });
18
-
19
- it('sets width and height inline style when size is custom', () => {
20
- const { container } = render(<StatusDot status="queued" size={10} />);
21
- const span = container.firstChild as HTMLElement;
22
- expect(span.style.width).toBe('10px');
23
- expect(span.style.height).toBe('10px');
24
- });
25
- });