@gokulvenkatareddy/cortex 0.1.7

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 (299) hide show
  1. package/README.md +1295 -0
  2. package/apps/octogent/.github/workflows/ci.yml +40 -0
  3. package/apps/octogent/.shims/claude +4 -0
  4. package/apps/octogent/AGENTS.md +71 -0
  5. package/apps/octogent/CONTRIBUTING.md +72 -0
  6. package/apps/octogent/LICENSE +21 -0
  7. package/apps/octogent/README.md +184 -0
  8. package/apps/octogent/apps/api/AGENTS.md +32 -0
  9. package/apps/octogent/apps/api/package.json +19 -0
  10. package/apps/octogent/apps/api/src/agentStateDetection.ts +181 -0
  11. package/apps/octogent/apps/api/src/claudeSessionScanner.ts +235 -0
  12. package/apps/octogent/apps/api/src/claudeSkills.ts +182 -0
  13. package/apps/octogent/apps/api/src/claudeUsage.ts +922 -0
  14. package/apps/octogent/apps/api/src/cli.ts +595 -0
  15. package/apps/octogent/apps/api/src/codeIntelStore.ts +46 -0
  16. package/apps/octogent/apps/api/src/codexUsage.ts +278 -0
  17. package/apps/octogent/apps/api/src/createApiServer/codeIntelRoutes.ts +60 -0
  18. package/apps/octogent/apps/api/src/createApiServer/conversationRoutes.ts +128 -0
  19. package/apps/octogent/apps/api/src/createApiServer/deckRoutes.ts +873 -0
  20. package/apps/octogent/apps/api/src/createApiServer/gitParsers.ts +140 -0
  21. package/apps/octogent/apps/api/src/createApiServer/gitRoutes.ts +214 -0
  22. package/apps/octogent/apps/api/src/createApiServer/miscRoutes.ts +316 -0
  23. package/apps/octogent/apps/api/src/createApiServer/monitorParsers.ts +137 -0
  24. package/apps/octogent/apps/api/src/createApiServer/monitorRoutes.ts +95 -0
  25. package/apps/octogent/apps/api/src/createApiServer/requestHandler.ts +311 -0
  26. package/apps/octogent/apps/api/src/createApiServer/requestParsers.ts +25 -0
  27. package/apps/octogent/apps/api/src/createApiServer/routeHelpers.ts +97 -0
  28. package/apps/octogent/apps/api/src/createApiServer/security.ts +70 -0
  29. package/apps/octogent/apps/api/src/createApiServer/terminalParsers.ts +167 -0
  30. package/apps/octogent/apps/api/src/createApiServer/terminalRoutes.ts +315 -0
  31. package/apps/octogent/apps/api/src/createApiServer/types.ts +24 -0
  32. package/apps/octogent/apps/api/src/createApiServer/uiStateParsers.ts +255 -0
  33. package/apps/octogent/apps/api/src/createApiServer/upgradeHandler.ts +38 -0
  34. package/apps/octogent/apps/api/src/createApiServer/usageRoutes.ts +84 -0
  35. package/apps/octogent/apps/api/src/createApiServer.ts +176 -0
  36. package/apps/octogent/apps/api/src/deck/readDeckTentacles.ts +595 -0
  37. package/apps/octogent/apps/api/src/githubRepoSummary.ts +397 -0
  38. package/apps/octogent/apps/api/src/logging.ts +9 -0
  39. package/apps/octogent/apps/api/src/monitor/defaults.ts +3 -0
  40. package/apps/octogent/apps/api/src/monitor/index.ts +8 -0
  41. package/apps/octogent/apps/api/src/monitor/repository.ts +303 -0
  42. package/apps/octogent/apps/api/src/monitor/service.ts +349 -0
  43. package/apps/octogent/apps/api/src/monitor/types.ts +120 -0
  44. package/apps/octogent/apps/api/src/monitor/xProvider.ts +587 -0
  45. package/apps/octogent/apps/api/src/projectPersistence.ts +377 -0
  46. package/apps/octogent/apps/api/src/prompts/index.ts +10 -0
  47. package/apps/octogent/apps/api/src/prompts/promptResolver.ts +145 -0
  48. package/apps/octogent/apps/api/src/runtimeMetadata.ts +69 -0
  49. package/apps/octogent/apps/api/src/server.ts +80 -0
  50. package/apps/octogent/apps/api/src/setupState.ts +80 -0
  51. package/apps/octogent/apps/api/src/setupStatus.ts +174 -0
  52. package/apps/octogent/apps/api/src/startupPrerequisites.ts +146 -0
  53. package/apps/octogent/apps/api/src/terminalRuntime/channelMessaging.ts +87 -0
  54. package/apps/octogent/apps/api/src/terminalRuntime/claudeTranscript.ts +279 -0
  55. package/apps/octogent/apps/api/src/terminalRuntime/constants.ts +15 -0
  56. package/apps/octogent/apps/api/src/terminalRuntime/conversations.ts +492 -0
  57. package/apps/octogent/apps/api/src/terminalRuntime/gitOperations.ts +341 -0
  58. package/apps/octogent/apps/api/src/terminalRuntime/hookProcessor.ts +405 -0
  59. package/apps/octogent/apps/api/src/terminalRuntime/protocol.ts +46 -0
  60. package/apps/octogent/apps/api/src/terminalRuntime/ptyEnvironment.ts +50 -0
  61. package/apps/octogent/apps/api/src/terminalRuntime/registry.ts +423 -0
  62. package/apps/octogent/apps/api/src/terminalRuntime/sessionRuntime.ts +671 -0
  63. package/apps/octogent/apps/api/src/terminalRuntime/systemClients.ts +432 -0
  64. package/apps/octogent/apps/api/src/terminalRuntime/types.ts +157 -0
  65. package/apps/octogent/apps/api/src/terminalRuntime/worktreeManager.ts +135 -0
  66. package/apps/octogent/apps/api/src/terminalRuntime.ts +567 -0
  67. package/apps/octogent/apps/api/src/usageUtils.ts +16 -0
  68. package/apps/octogent/apps/api/src/ws-shim.d.ts +28 -0
  69. package/apps/octogent/apps/api/tests/agentStateDetection.test.ts +67 -0
  70. package/apps/octogent/apps/api/tests/claudeUsage.test.ts +583 -0
  71. package/apps/octogent/apps/api/tests/codexUsage.test.ts +107 -0
  72. package/apps/octogent/apps/api/tests/createApiServer.test.ts +3207 -0
  73. package/apps/octogent/apps/api/tests/githubRepoSummary.test.ts +100 -0
  74. package/apps/octogent/apps/api/tests/logging.test.ts +33 -0
  75. package/apps/octogent/apps/api/tests/monitorApi.test.ts +467 -0
  76. package/apps/octogent/apps/api/tests/monitorCore.test.ts +104 -0
  77. package/apps/octogent/apps/api/tests/promptResolver.test.ts +109 -0
  78. package/apps/octogent/apps/api/tests/protocol.test.ts +14 -0
  79. package/apps/octogent/apps/api/tests/sessionRuntime.test.ts +608 -0
  80. package/apps/octogent/apps/api/tests/startupPrerequisites.test.ts +70 -0
  81. package/apps/octogent/apps/api/tests/upgradeHandler.test.ts +40 -0
  82. package/apps/octogent/apps/api/tests/xMonitorProvider.test.ts +109 -0
  83. package/apps/octogent/apps/api/tsconfig.json +7 -0
  84. package/apps/octogent/apps/api/vitest.config.ts +7 -0
  85. package/apps/octogent/apps/web/AGENTS.md +38 -0
  86. package/apps/octogent/apps/web/index.html +13 -0
  87. package/apps/octogent/apps/web/package.json +32 -0
  88. package/apps/octogent/apps/web/public/octopus-favicon.svg +26 -0
  89. package/apps/octogent/apps/web/src/App.tsx +646 -0
  90. package/apps/octogent/apps/web/src/app/canvas/types.ts +34 -0
  91. package/apps/octogent/apps/web/src/app/codeIntelAggregation.ts +278 -0
  92. package/apps/octogent/apps/web/src/app/constants.ts +28 -0
  93. package/apps/octogent/apps/web/src/app/conversationNormalizers.ts +135 -0
  94. package/apps/octogent/apps/web/src/app/formatTimestamp.ts +18 -0
  95. package/apps/octogent/apps/web/src/app/githubMetrics.ts +76 -0
  96. package/apps/octogent/apps/web/src/app/githubNormalizers.ts +91 -0
  97. package/apps/octogent/apps/web/src/app/hooks/useAgentRuntimeStates.ts +18 -0
  98. package/apps/octogent/apps/web/src/app/hooks/useBackendLivenessPolling.ts +53 -0
  99. package/apps/octogent/apps/web/src/app/hooks/useCanvasGraphData.ts +449 -0
  100. package/apps/octogent/apps/web/src/app/hooks/useCanvasTransform.ts +260 -0
  101. package/apps/octogent/apps/web/src/app/hooks/useClaudeUsagePolling.ts +40 -0
  102. package/apps/octogent/apps/web/src/app/hooks/useClickOutside.ts +30 -0
  103. package/apps/octogent/apps/web/src/app/hooks/useCodeIntelRuntime.ts +83 -0
  104. package/apps/octogent/apps/web/src/app/hooks/useCodexUsagePolling.ts +35 -0
  105. package/apps/octogent/apps/web/src/app/hooks/useConsoleKeyboardShortcuts.ts +31 -0
  106. package/apps/octogent/apps/web/src/app/hooks/useConversationsRuntime.ts +377 -0
  107. package/apps/octogent/apps/web/src/app/hooks/useForceSimulation.ts +319 -0
  108. package/apps/octogent/apps/web/src/app/hooks/useGitHubPrimaryViewModel.ts +143 -0
  109. package/apps/octogent/apps/web/src/app/hooks/useGithubSummaryPolling.ts +28 -0
  110. package/apps/octogent/apps/web/src/app/hooks/useInitialColumnsHydration.ts +64 -0
  111. package/apps/octogent/apps/web/src/app/hooks/useMonitorRuntime.ts +220 -0
  112. package/apps/octogent/apps/web/src/app/hooks/usePersistedUiState.ts +536 -0
  113. package/apps/octogent/apps/web/src/app/hooks/usePollingData.ts +79 -0
  114. package/apps/octogent/apps/web/src/app/hooks/usePromptLibrary.ts +185 -0
  115. package/apps/octogent/apps/web/src/app/hooks/useTentacleGitLifecycle.ts +530 -0
  116. package/apps/octogent/apps/web/src/app/hooks/useTerminalCompletionNotification.ts +94 -0
  117. package/apps/octogent/apps/web/src/app/hooks/useTerminalMutations.ts +266 -0
  118. package/apps/octogent/apps/web/src/app/hooks/useTerminalStateReconciliation.ts +23 -0
  119. package/apps/octogent/apps/web/src/app/hooks/useUsageHeatmapPolling.ts +43 -0
  120. package/apps/octogent/apps/web/src/app/hooks/useWorkspaceSetup.ts +80 -0
  121. package/apps/octogent/apps/web/src/app/hotkeys.ts +31 -0
  122. package/apps/octogent/apps/web/src/app/monitorNormalizers.ts +145 -0
  123. package/apps/octogent/apps/web/src/app/notificationSounds.ts +164 -0
  124. package/apps/octogent/apps/web/src/app/terminalRuntimeStateStore.ts +261 -0
  125. package/apps/octogent/apps/web/src/app/terminalState.ts +21 -0
  126. package/apps/octogent/apps/web/src/app/types.ts +42 -0
  127. package/apps/octogent/apps/web/src/app/uiStateNormalizers.ts +113 -0
  128. package/apps/octogent/apps/web/src/app/usageNormalizers.ts +58 -0
  129. package/apps/octogent/apps/web/src/components/ActiveAgentsSidebar.tsx +60 -0
  130. package/apps/octogent/apps/web/src/components/ActivityPrimaryView.tsx +21 -0
  131. package/apps/octogent/apps/web/src/components/AgentStateBadge.tsx +47 -0
  132. package/apps/octogent/apps/web/src/components/CanvasPrimaryView.tsx +1532 -0
  133. package/apps/octogent/apps/web/src/components/ClearAllConversationsDialog.tsx +33 -0
  134. package/apps/octogent/apps/web/src/components/CodeIntelArcDiagram.tsx +245 -0
  135. package/apps/octogent/apps/web/src/components/CodeIntelPrimaryView.tsx +104 -0
  136. package/apps/octogent/apps/web/src/components/CodeIntelTreemap.tsx +138 -0
  137. package/apps/octogent/apps/web/src/components/ConsolePrimaryNav.tsx +31 -0
  138. package/apps/octogent/apps/web/src/components/ConversationsPrimaryView.tsx +243 -0
  139. package/apps/octogent/apps/web/src/components/DeckPrimaryView.tsx +613 -0
  140. package/apps/octogent/apps/web/src/components/DeleteTentacleDialog.tsx +91 -0
  141. package/apps/octogent/apps/web/src/components/EmptyOctopus.tsx +715 -0
  142. package/apps/octogent/apps/web/src/components/GitHubPrimaryView.tsx +494 -0
  143. package/apps/octogent/apps/web/src/components/MonitorPrimaryView.tsx +475 -0
  144. package/apps/octogent/apps/web/src/components/PrimaryViewRouter.tsx +99 -0
  145. package/apps/octogent/apps/web/src/components/PromptsPrimaryView.tsx +243 -0
  146. package/apps/octogent/apps/web/src/components/RuntimeStatusStrip.tsx +273 -0
  147. package/apps/octogent/apps/web/src/components/SettingsPrimaryView.tsx +92 -0
  148. package/apps/octogent/apps/web/src/components/SidebarActionPanel.tsx +124 -0
  149. package/apps/octogent/apps/web/src/components/SidebarConversationsList.tsx +279 -0
  150. package/apps/octogent/apps/web/src/components/SidebarPromptsList.tsx +116 -0
  151. package/apps/octogent/apps/web/src/components/TelemetryTape.tsx +106 -0
  152. package/apps/octogent/apps/web/src/components/TentacleGitActionsDialog.tsx +341 -0
  153. package/apps/octogent/apps/web/src/components/Terminal.tsx +524 -0
  154. package/apps/octogent/apps/web/src/components/TerminalPromptPicker.tsx +140 -0
  155. package/apps/octogent/apps/web/src/components/UsageHeatmap.tsx +702 -0
  156. package/apps/octogent/apps/web/src/components/canvas/CanvasTentaclePanel.tsx +485 -0
  157. package/apps/octogent/apps/web/src/components/canvas/CanvasTerminalColumn.tsx +89 -0
  158. package/apps/octogent/apps/web/src/components/canvas/DeleteAllTerminalsDialog.tsx +221 -0
  159. package/apps/octogent/apps/web/src/components/canvas/OctopusNode.tsx +307 -0
  160. package/apps/octogent/apps/web/src/components/canvas/SessionNode.tsx +185 -0
  161. package/apps/octogent/apps/web/src/components/deck/ActionCards.tsx +118 -0
  162. package/apps/octogent/apps/web/src/components/deck/AddTentacleForm.tsx +269 -0
  163. package/apps/octogent/apps/web/src/components/deck/DeckBottomActions.tsx +56 -0
  164. package/apps/octogent/apps/web/src/components/deck/TentaclePod.tsx +334 -0
  165. package/apps/octogent/apps/web/src/components/deck/WorkspaceSetupCard.tsx +105 -0
  166. package/apps/octogent/apps/web/src/components/deck/octopusVisuals.ts +72 -0
  167. package/apps/octogent/apps/web/src/components/terminalReplay.ts +62 -0
  168. package/apps/octogent/apps/web/src/components/terminalWheel.ts +54 -0
  169. package/apps/octogent/apps/web/src/components/ui/ActionButton.tsx +34 -0
  170. package/apps/octogent/apps/web/src/components/ui/ConfirmationDialog.tsx +86 -0
  171. package/apps/octogent/apps/web/src/components/ui/MarkdownContent.tsx +43 -0
  172. package/apps/octogent/apps/web/src/components/ui/SettingsToggle.tsx +34 -0
  173. package/apps/octogent/apps/web/src/components/ui/StatusBadge.tsx +24 -0
  174. package/apps/octogent/apps/web/src/main.tsx +17 -0
  175. package/apps/octogent/apps/web/src/runtime/HttpTerminalSnapshotReader.ts +87 -0
  176. package/apps/octogent/apps/web/src/runtime/runtimeEndpoints.ts +412 -0
  177. package/apps/octogent/apps/web/src/styles/chrome-and-buttons.css +272 -0
  178. package/apps/octogent/apps/web/src/styles/console-canvas-activity.css +358 -0
  179. package/apps/octogent/apps/web/src/styles/console-canvas-canvas.css +1843 -0
  180. package/apps/octogent/apps/web/src/styles/console-canvas-code-intel.css +227 -0
  181. package/apps/octogent/apps/web/src/styles/console-canvas-conversations.css +705 -0
  182. package/apps/octogent/apps/web/src/styles/console-canvas-deck.css +1524 -0
  183. package/apps/octogent/apps/web/src/styles/console-canvas-github.css +541 -0
  184. package/apps/octogent/apps/web/src/styles/console-canvas-monitor.css +595 -0
  185. package/apps/octogent/apps/web/src/styles/console-canvas-pixpack.css +81 -0
  186. package/apps/octogent/apps/web/src/styles/console-canvas-prompts.css +474 -0
  187. package/apps/octogent/apps/web/src/styles/console-canvas-settings.css +207 -0
  188. package/apps/octogent/apps/web/src/styles/console-chrome-status-nav.css +441 -0
  189. package/apps/octogent/apps/web/src/styles/console-overrides-telemetry.css +320 -0
  190. package/apps/octogent/apps/web/src/styles/console-theme-tokens.css +25 -0
  191. package/apps/octogent/apps/web/src/styles/cortex-theme.css +412 -0
  192. package/apps/octogent/apps/web/src/styles/foundation.css +100 -0
  193. package/apps/octogent/apps/web/src/styles/sidebar-and-scrollbars.css +447 -0
  194. package/apps/octogent/apps/web/src/styles/terminal-and-status.css +356 -0
  195. package/apps/octogent/apps/web/src/styles.css +25 -0
  196. package/apps/octogent/apps/web/src/types/ws.d.ts +23 -0
  197. package/apps/octogent/apps/web/tests/CanvasPrimaryView.test.tsx +347 -0
  198. package/apps/octogent/apps/web/tests/HttpTerminalSnapshotReader.test.tsx +54 -0
  199. package/apps/octogent/apps/web/tests/RuntimeStatusStrip.test.tsx +70 -0
  200. package/apps/octogent/apps/web/tests/Terminal.test.tsx +87 -0
  201. package/apps/octogent/apps/web/tests/add-tentacle-form.test.tsx +48 -0
  202. package/apps/octogent/apps/web/tests/app-github-runtime.test.tsx +162 -0
  203. package/apps/octogent/apps/web/tests/app-monitor-runtime.test.tsx +657 -0
  204. package/apps/octogent/apps/web/tests/app-shell-navigation.test.tsx +109 -0
  205. package/apps/octogent/apps/web/tests/app-swarm-refresh.test.tsx +268 -0
  206. package/apps/octogent/apps/web/tests/app-ui-state-persistence.test.tsx +116 -0
  207. package/apps/octogent/apps/web/tests/app-workspace-setup.test.tsx +217 -0
  208. package/apps/octogent/apps/web/tests/canvas-tentacle-panel.test.tsx +195 -0
  209. package/apps/octogent/apps/web/tests/delete-all-terminals-dialog.test.tsx +76 -0
  210. package/apps/octogent/apps/web/tests/githubMetrics.test.tsx +52 -0
  211. package/apps/octogent/apps/web/tests/hotkeys.test.tsx +44 -0
  212. package/apps/octogent/apps/web/tests/runtimeEndpoints.test.tsx +240 -0
  213. package/apps/octogent/apps/web/tests/setup.ts +39 -0
  214. package/apps/octogent/apps/web/tests/tentacle-pod.test.tsx +62 -0
  215. package/apps/octogent/apps/web/tests/terminalReplay.test.ts +71 -0
  216. package/apps/octogent/apps/web/tests/terminalState.test.tsx +49 -0
  217. package/apps/octogent/apps/web/tests/terminalWheel.test.tsx +51 -0
  218. package/apps/octogent/apps/web/tests/test-utils/appTestHarness.ts +48 -0
  219. package/apps/octogent/apps/web/tests/uiPrimitives.test.tsx +31 -0
  220. package/apps/octogent/apps/web/tests/useAgentRuntimeStates.test.tsx +47 -0
  221. package/apps/octogent/apps/web/tsconfig.json +8 -0
  222. package/apps/octogent/apps/web/vite.api.bundle.config.mts +32 -0
  223. package/apps/octogent/apps/web/vite.config.ts +22 -0
  224. package/apps/octogent/bin/octogent +3 -0
  225. package/apps/octogent/biome.json +21 -0
  226. package/apps/octogent/docs/concepts/mental-model.md +79 -0
  227. package/apps/octogent/docs/concepts/runtime-and-api.md +60 -0
  228. package/apps/octogent/docs/concepts/tentacles.md +85 -0
  229. package/apps/octogent/docs/getting-started/installation.md +54 -0
  230. package/apps/octogent/docs/getting-started/quickstart.md +79 -0
  231. package/apps/octogent/docs/guides/inter-agent-messaging.md +43 -0
  232. package/apps/octogent/docs/guides/orchestrating-child-agents.md +49 -0
  233. package/apps/octogent/docs/guides/working-with-todos.md +56 -0
  234. package/apps/octogent/docs/index.md +40 -0
  235. package/apps/octogent/docs/reference/api.md +103 -0
  236. package/apps/octogent/docs/reference/cli.md +71 -0
  237. package/apps/octogent/docs/reference/experimental-features.md +28 -0
  238. package/apps/octogent/docs/reference/filesystem-layout.md +62 -0
  239. package/apps/octogent/docs/reference/troubleshooting.md +49 -0
  240. package/apps/octogent/package.json +35 -0
  241. package/apps/octogent/packages/core/AGENTS.md +31 -0
  242. package/apps/octogent/packages/core/package.json +12 -0
  243. package/apps/octogent/packages/core/src/adapters/InMemoryTerminalSnapshotReader.ts +10 -0
  244. package/apps/octogent/packages/core/src/application/buildTerminalList.ts +13 -0
  245. package/apps/octogent/packages/core/src/domain/agentRuntime.ts +18 -0
  246. package/apps/octogent/packages/core/src/domain/channel.ts +8 -0
  247. package/apps/octogent/packages/core/src/domain/completionSound.ts +14 -0
  248. package/apps/octogent/packages/core/src/domain/conversation.ts +48 -0
  249. package/apps/octogent/packages/core/src/domain/deck.ts +33 -0
  250. package/apps/octogent/packages/core/src/domain/git.ts +32 -0
  251. package/apps/octogent/packages/core/src/domain/monitor.ts +62 -0
  252. package/apps/octogent/packages/core/src/domain/setup.ts +27 -0
  253. package/apps/octogent/packages/core/src/domain/terminal.ts +17 -0
  254. package/apps/octogent/packages/core/src/domain/uiState.ts +22 -0
  255. package/apps/octogent/packages/core/src/domain/usage.ts +60 -0
  256. package/apps/octogent/packages/core/src/index.ts +15 -0
  257. package/apps/octogent/packages/core/src/ports/TerminalSnapshotReader.ts +5 -0
  258. package/apps/octogent/packages/core/src/util/typeCoercion.ts +20 -0
  259. package/apps/octogent/packages/core/tests/buildTerminalList.test.ts +75 -0
  260. package/apps/octogent/packages/core/tsconfig.json +7 -0
  261. package/apps/octogent/packages/core/tsconfig.tsbuildinfo +1 -0
  262. package/apps/octogent/packages/core/vitest.config.ts +7 -0
  263. package/apps/octogent/pnpm-lock.yaml +3212 -0
  264. package/apps/octogent/pnpm-workspace.yaml +3 -0
  265. package/apps/octogent/prompts/meta-prompt-generator.md +223 -0
  266. package/apps/octogent/prompts/octoboss-clean-contexts.md +30 -0
  267. package/apps/octogent/prompts/octoboss-reorganize-tentacles.md +29 -0
  268. package/apps/octogent/prompts/octoboss-reorganize-todos.md +27 -0
  269. package/apps/octogent/prompts/sandbox-init.md +3 -0
  270. package/apps/octogent/prompts/swarm-parent.md +83 -0
  271. package/apps/octogent/prompts/swarm-worker.md +50 -0
  272. package/apps/octogent/prompts/tentacle-context-init.md +1 -0
  273. package/apps/octogent/prompts/tentacle-planner.md +110 -0
  274. package/apps/octogent/prompts/tentacle-reorganize-todos.md +20 -0
  275. package/apps/octogent/prompts/tentacle-update-tentacle.md +18 -0
  276. package/apps/octogent/scripts/build-package.mjs +23 -0
  277. package/apps/octogent/scripts/dev.mjs +158 -0
  278. package/apps/octogent/scripts/smoke-public-install.mjs +271 -0
  279. package/apps/octogent/static/images/octogent-header.png +0 -0
  280. package/apps/octogent/static/images/preview_1.jpg +0 -0
  281. package/apps/octogent/static/images/preview_2.jpg +0 -0
  282. package/apps/octogent/static/images/preview_3.jpg +0 -0
  283. package/apps/octogent/static/images/preview_4.jpg +0 -0
  284. package/apps/octogent/static/images/preview_5.jpg +0 -0
  285. package/apps/octogent/static/images/preview_6.jpg +0 -0
  286. package/apps/octogent/tsconfig.base.json +16 -0
  287. package/bin/AGI +3 -0
  288. package/bin/AGI-install-app +71 -0
  289. package/bin/AGI-ui +16 -0
  290. package/bin/AGI-voice +15 -0
  291. package/bin/AGI-web +16 -0
  292. package/bin/cortex +109 -0
  293. package/bin/cortex-octogent +99 -0
  294. package/bin/import-specifier.mjs +13 -0
  295. package/bin/import-specifier.test.mjs +13 -0
  296. package/bin/octo +150 -0
  297. package/dist/cli.mjs +555650 -0
  298. package/package.json +157 -0
  299. package/scripts/setup-wizard.ts +390 -0
@@ -0,0 +1,278 @@
1
+ export type CodeIntelEvent = {
2
+ ts: string;
3
+ sessionId: string;
4
+ tool: string;
5
+ file: string;
6
+ };
7
+
8
+ /* ── Treemap ────────────────────────────────────────── */
9
+
10
+ export type TreemapNode = {
11
+ name: string;
12
+ path: string;
13
+ value: number;
14
+ children: TreemapNode[];
15
+ };
16
+
17
+ export type TreemapRect = {
18
+ x: number;
19
+ y: number;
20
+ width: number;
21
+ height: number;
22
+ name: string;
23
+ path: string;
24
+ value: number;
25
+ depth: number;
26
+ };
27
+
28
+ /** Build a flat list of files with edit counts, wrapped in a root node for the treemap layout. */
29
+ export const buildTreemapTree = (events: CodeIntelEvent[], workspaceCwd: string): TreemapNode => {
30
+ const counts = new Map<string, number>();
31
+ for (const e of events) {
32
+ const relative = e.file.startsWith(workspaceCwd)
33
+ ? e.file.slice(workspaceCwd.length + 1)
34
+ : e.file;
35
+ counts.set(relative, (counts.get(relative) ?? 0) + 1);
36
+ }
37
+
38
+ const children: TreemapNode[] = [...counts.entries()]
39
+ .map(([path, count]) => ({
40
+ name: path.split("/").pop() ?? path,
41
+ path,
42
+ value: count,
43
+ children: [],
44
+ }))
45
+ .sort((a, b) => b.value - a.value)
46
+ .slice(0, 50);
47
+
48
+ const total = children.reduce((acc, c) => acc + c.value, 0);
49
+
50
+ return { name: "root", path: "", value: total, children };
51
+ };
52
+
53
+ /** Squarified treemap layout — returns flat array of positioned rectangles. */
54
+ export const layoutTreemap = (root: TreemapNode, width: number, height: number): TreemapRect[] => {
55
+ const rects: TreemapRect[] = [];
56
+ if (root.value === 0 || root.children.length === 0) return rects;
57
+
58
+ // Normalize values to areas
59
+ const totalArea = width * height;
60
+ const items = [...root.children]
61
+ .sort((a, b) => b.value - a.value)
62
+ .map((c) => ({ node: c, area: (c.value / root.value) * totalArea }));
63
+
64
+ squarify(items, { x: 0, y: 0, w: width, h: height }, rects);
65
+ return rects;
66
+ };
67
+
68
+ type LayoutItem = { node: TreemapNode; area: number };
69
+ type Rect = { x: number; y: number; w: number; h: number };
70
+
71
+ const squarify = (items: LayoutItem[], rect: Rect, out: TreemapRect[]) => {
72
+ if (items.length === 0) return;
73
+ if (items.length === 1) {
74
+ const [item] = items;
75
+ if (item) {
76
+ pushRect(item, rect, out);
77
+ }
78
+ return;
79
+ }
80
+
81
+ const { x, y, w, h } = rect;
82
+ const shortSide = Math.min(w, h);
83
+ const horizontal = w >= h; // lay row along the short side
84
+
85
+ let row: LayoutItem[] = [];
86
+ let rowArea = 0;
87
+ let best = Number.POSITIVE_INFINITY;
88
+
89
+ for (let i = 0; i < items.length; i++) {
90
+ const item = items[i];
91
+ if (!item) continue;
92
+ const nextArea = rowArea + item.area;
93
+ const nextRow = [...row, item];
94
+ const worst = worstRatio(nextRow, nextArea, shortSide);
95
+
96
+ if (worst <= best) {
97
+ row = nextRow;
98
+ rowArea = nextArea;
99
+ best = worst;
100
+ } else {
101
+ // Lay out current row, recurse on remainder
102
+ const rowThickness = rowArea / shortSide;
103
+ let offset = 0;
104
+
105
+ for (const r of row) {
106
+ const cellLen = r.area / rowThickness;
107
+ if (horizontal) {
108
+ pushRect(r, { x, y: y + offset, w: rowThickness, h: cellLen }, out);
109
+ } else {
110
+ pushRect(r, { x: x + offset, y, w: cellLen, h: rowThickness }, out);
111
+ }
112
+ offset += cellLen;
113
+ }
114
+
115
+ const remaining = items.slice(i);
116
+ if (horizontal) {
117
+ squarify(remaining, { x: x + rowThickness, y, w: w - rowThickness, h }, out);
118
+ } else {
119
+ squarify(remaining, { x, y: y + rowThickness, w, h: h - rowThickness }, out);
120
+ }
121
+ return;
122
+ }
123
+ }
124
+
125
+ // Lay out final row
126
+ const rowThickness = rowArea / shortSide;
127
+ let offset = 0;
128
+ for (const r of row) {
129
+ const cellLen = r.area / rowThickness;
130
+ if (horizontal) {
131
+ pushRect(r, { x, y: y + offset, w: rowThickness, h: cellLen }, out);
132
+ } else {
133
+ pushRect(r, { x: x + offset, y, w: cellLen, h: rowThickness }, out);
134
+ }
135
+ offset += cellLen;
136
+ }
137
+ };
138
+
139
+ const pushRect = (item: LayoutItem, rect: Rect, out: TreemapRect[]) => {
140
+ out.push({
141
+ x: rect.x,
142
+ y: rect.y,
143
+ width: rect.w,
144
+ height: rect.h,
145
+ name: item.node.name,
146
+ path: item.node.path,
147
+ value: item.node.value,
148
+ depth: 1,
149
+ });
150
+ };
151
+
152
+ const worstRatio = (row: LayoutItem[], totalArea: number, shortSide: number): number => {
153
+ const rowLen = totalArea / shortSide; // thickness of the row
154
+ let worst = 0;
155
+ for (const r of row) {
156
+ const cellLen = r.area / rowLen;
157
+ const aspect = rowLen > cellLen ? rowLen / cellLen : cellLen / rowLen;
158
+ if (aspect > worst) worst = aspect;
159
+ }
160
+ return worst;
161
+ };
162
+
163
+ /* ── Coupling (Arc Diagram) ─────────────────────────── */
164
+
165
+ export type CouplingPair = {
166
+ fileA: string;
167
+ fileB: string;
168
+ coSessions: number;
169
+ totalSessions: number;
170
+ strength: number;
171
+ };
172
+
173
+ export type CouplingFile = {
174
+ file: string;
175
+ edits: number;
176
+ sessions: number;
177
+ };
178
+
179
+ export type CouplingData = {
180
+ files: CouplingFile[];
181
+ pairs: CouplingPair[];
182
+ };
183
+
184
+ /** Find files that co-occur in the same session. */
185
+ export const buildCouplingData = (events: CodeIntelEvent[], workspaceCwd: string): CouplingData => {
186
+ // Group files by session
187
+ const sessionFiles = new Map<string, Set<string>>();
188
+ const fileEdits = new Map<string, number>();
189
+ const fileSessions = new Map<string, Set<string>>();
190
+
191
+ for (const e of events) {
192
+ const relative = e.file.startsWith(workspaceCwd)
193
+ ? e.file.slice(workspaceCwd.length + 1)
194
+ : e.file;
195
+
196
+ if (!sessionFiles.has(e.sessionId)) sessionFiles.set(e.sessionId, new Set());
197
+ sessionFiles.get(e.sessionId)?.add(relative);
198
+
199
+ fileEdits.set(relative, (fileEdits.get(relative) ?? 0) + 1);
200
+
201
+ if (!fileSessions.has(relative)) fileSessions.set(relative, new Set());
202
+ fileSessions.get(relative)?.add(e.sessionId);
203
+ }
204
+
205
+ // Build coupling pairs
206
+ const pairKey = (a: string, b: string) => (a < b ? `${a}\0${b}` : `${b}\0${a}`);
207
+ const pairCounts = new Map<string, { fileA: string; fileB: string; count: number }>();
208
+
209
+ for (const files of sessionFiles.values()) {
210
+ const fileList = [...files];
211
+ for (let i = 0; i < fileList.length; i++) {
212
+ for (let j = i + 1; j < fileList.length; j++) {
213
+ const a = fileList[i];
214
+ const b = fileList[j];
215
+ if (!a || !b) continue;
216
+ const key = pairKey(a, b);
217
+ const existing = pairCounts.get(key);
218
+ if (existing) {
219
+ existing.count++;
220
+ } else {
221
+ pairCounts.set(key, { fileA: a < b ? a : b, fileB: a < b ? b : a, count: 1 });
222
+ }
223
+ }
224
+ }
225
+ }
226
+
227
+ const totalSessionCount = sessionFiles.size;
228
+
229
+ const files: CouplingFile[] = [...fileEdits.entries()]
230
+ .map(([file, edits]) => ({
231
+ file,
232
+ edits,
233
+ sessions: fileSessions.get(file)?.size ?? 0,
234
+ }))
235
+ .sort((a, b) => b.edits - a.edits);
236
+
237
+ const pairs: CouplingPair[] = [...pairCounts.values()]
238
+ .map(({ fileA, fileB, count }) => ({
239
+ fileA,
240
+ fileB,
241
+ coSessions: count,
242
+ totalSessions: totalSessionCount,
243
+ strength: totalSessionCount > 0 ? count / totalSessionCount : 0,
244
+ }))
245
+ .filter((p) => p.coSessions >= 1)
246
+ .sort((a, b) => b.coSessions - a.coSessions);
247
+
248
+ return { files, pairs };
249
+ };
250
+
251
+ /* ── Heat color scale (thermal MRI gradient) ────────── */
252
+
253
+ // smolder → accent → fire red
254
+ const MRI_STOPS: [number, number, number][] = [
255
+ [0x2a, 0x18, 0x04], // dark ember
256
+ [0x6e, 0x32, 0x06], // deep burn
257
+ [0xa8, 0x58, 0x08], // warm amber
258
+ [0xd4, 0xa0, 0x17], // primary accent
259
+ [0xe0, 0x7a, 0x0a], // orange fire
260
+ [0xe8, 0x44, 0x08], // hot orange
261
+ [0xd0, 0x18, 0x06], // deep red
262
+ [0xf0, 0x22, 0x06], // fire red
263
+ ];
264
+
265
+ export const heatColor = (value: number, maxValue: number): string => {
266
+ if (maxValue === 0) return "rgb(10,10,46)";
267
+ const ratio = Math.min(value / maxValue, 1);
268
+ const segment = ratio * (MRI_STOPS.length - 1);
269
+ const i = Math.min(Math.floor(segment), MRI_STOPS.length - 2);
270
+ const t = segment - i;
271
+ const a = MRI_STOPS[i];
272
+ const b = MRI_STOPS[i + 1];
273
+ if (!a || !b) return "rgb(10,10,46)";
274
+ const r = Math.round(a[0] + t * (b[0] - a[0]));
275
+ const g = Math.round(a[1] + t * (b[1] - a[1]));
276
+ const bl = Math.round(a[2] + t * (b[2] - a[2]));
277
+ return `rgb(${r},${g},${bl})`;
278
+ };
@@ -0,0 +1,28 @@
1
+ export const CODEX_USAGE_SCAN_INTERVAL_MS = 600_000;
2
+ export const GITHUB_SUMMARY_SCAN_INTERVAL_MS = 60_000;
3
+ export const MONITOR_SCAN_INTERVAL_MS = 60_000;
4
+ export const BACKEND_LIVENESS_SCAN_INTERVAL_MS = 120_000;
5
+ export const UI_STATE_SAVE_DEBOUNCE_MS = 250;
6
+ export const MIN_SIDEBAR_WIDTH = 240;
7
+ export const MAX_SIDEBAR_WIDTH = 520;
8
+
9
+ export const PRIMARY_NAV_ITEMS = [
10
+ { index: 1, label: "Agents" },
11
+ { index: 2, label: "Deck" },
12
+ { index: 3, label: "Activity" },
13
+ { index: 4, label: "Code Intel" },
14
+ { index: 5, label: "Monitor" },
15
+ { index: 6, label: "Conversations" },
16
+ { index: 7, label: "Prompts" },
17
+ { index: 8, label: "Settings" },
18
+ ] as const;
19
+
20
+ export const GITHUB_COMMIT_SERIES_LENGTH = 30;
21
+ export const GITHUB_SPARKLINE_WIDTH = 148;
22
+ export const GITHUB_SPARKLINE_HEIGHT = 36;
23
+ export const GITHUB_OVERVIEW_GRAPH_WIDTH = 640;
24
+ export const GITHUB_OVERVIEW_GRAPH_HEIGHT = 180;
25
+
26
+ export const PRIMARY_NAV_MAX = PRIMARY_NAV_ITEMS.length;
27
+
28
+ export type PrimaryNavIndex = (typeof PRIMARY_NAV_ITEMS)[number]["index"];
@@ -0,0 +1,135 @@
1
+ import { asNumber, asRecord, asString } from "@octogent/core";
2
+
3
+ import type {
4
+ ConversationSessionDetail,
5
+ ConversationSessionSummary,
6
+ ConversationTranscriptEvent,
7
+ ConversationTurn,
8
+ } from "./types";
9
+
10
+ const normalizeConversationTurn = (value: unknown): ConversationTurn | null => {
11
+ const record = asRecord(value);
12
+ if (!record) {
13
+ return null;
14
+ }
15
+
16
+ const turnId = asString(record.turnId);
17
+ const role = record.role;
18
+ const content = asString(record.content);
19
+ const startedAt = asString(record.startedAt);
20
+ const endedAt = asString(record.endedAt);
21
+ if (
22
+ !turnId ||
23
+ (role !== "user" && role !== "assistant") ||
24
+ content === null ||
25
+ !startedAt ||
26
+ !endedAt
27
+ ) {
28
+ return null;
29
+ }
30
+
31
+ return {
32
+ turnId,
33
+ role,
34
+ content,
35
+ startedAt,
36
+ endedAt,
37
+ };
38
+ };
39
+
40
+ const normalizeConversationTranscriptEvent = (
41
+ value: unknown,
42
+ ): ConversationTranscriptEvent | null => {
43
+ const record = asRecord(value);
44
+ if (!record) {
45
+ return null;
46
+ }
47
+
48
+ const eventId = asString(record.eventId);
49
+ const sessionId = asString(record.sessionId);
50
+ const tentacleId = asString(record.tentacleId);
51
+ const timestamp = asString(record.timestamp);
52
+ const type = record.type;
53
+ if (
54
+ !eventId ||
55
+ !sessionId ||
56
+ !tentacleId ||
57
+ !timestamp ||
58
+ (type !== "session_start" &&
59
+ type !== "input_submit" &&
60
+ type !== "output_chunk" &&
61
+ type !== "state_change" &&
62
+ type !== "session_end")
63
+ ) {
64
+ return null;
65
+ }
66
+
67
+ return {
68
+ eventId,
69
+ sessionId,
70
+ tentacleId,
71
+ timestamp,
72
+ type,
73
+ };
74
+ };
75
+
76
+ export const normalizeConversationSessionSummary = (
77
+ value: unknown,
78
+ ): ConversationSessionSummary | null => {
79
+ const record = asRecord(value);
80
+ if (!record) {
81
+ return null;
82
+ }
83
+
84
+ const sessionId = asString(record.sessionId);
85
+ if (!sessionId) {
86
+ return null;
87
+ }
88
+
89
+ const tentacleId = asString(record.tentacleId);
90
+ return {
91
+ sessionId,
92
+ tentacleId,
93
+ startedAt: asString(record.startedAt),
94
+ endedAt: asString(record.endedAt),
95
+ lastEventAt: asString(record.lastEventAt),
96
+ eventCount: Math.max(0, Math.floor(asNumber(record.eventCount) ?? 0)),
97
+ turnCount: Math.max(0, Math.floor(asNumber(record.turnCount) ?? 0)),
98
+ userTurnCount: Math.max(0, Math.floor(asNumber(record.userTurnCount) ?? 0)),
99
+ assistantTurnCount: Math.max(0, Math.floor(asNumber(record.assistantTurnCount) ?? 0)),
100
+ firstUserTurnPreview: asString(record.firstUserTurnPreview),
101
+ lastUserTurnPreview: asString(record.lastUserTurnPreview),
102
+ lastAssistantTurnPreview: asString(record.lastAssistantTurnPreview),
103
+ };
104
+ };
105
+
106
+ export const normalizeConversationSessionDetail = (
107
+ value: unknown,
108
+ ): ConversationSessionDetail | null => {
109
+ const record = asRecord(value);
110
+ if (!record) {
111
+ return null;
112
+ }
113
+
114
+ const summary = normalizeConversationSessionSummary(record);
115
+ if (!summary) {
116
+ return null;
117
+ }
118
+
119
+ const turns = Array.isArray(record.turns)
120
+ ? record.turns
121
+ .map((turn) => normalizeConversationTurn(turn))
122
+ .filter((turn): turn is ConversationTurn => turn !== null)
123
+ : [];
124
+ const events = Array.isArray(record.events)
125
+ ? record.events
126
+ .map((event) => normalizeConversationTranscriptEvent(event))
127
+ .filter((event): event is ConversationTranscriptEvent => event !== null)
128
+ : [];
129
+
130
+ return {
131
+ ...summary,
132
+ turns,
133
+ events,
134
+ };
135
+ };
@@ -0,0 +1,18 @@
1
+ export const formatTimestamp = (value: string | null) => {
2
+ if (!value) {
3
+ return "--";
4
+ }
5
+
6
+ const parsed = Date.parse(value);
7
+ if (!Number.isFinite(parsed)) {
8
+ return value;
9
+ }
10
+
11
+ return new Date(parsed).toLocaleString("en-US", {
12
+ month: "short",
13
+ day: "2-digit",
14
+ hour: "2-digit",
15
+ minute: "2-digit",
16
+ second: "2-digit",
17
+ });
18
+ };
@@ -0,0 +1,76 @@
1
+ import { GITHUB_COMMIT_SERIES_LENGTH } from "./constants";
2
+ import type { GitHubCommitPoint, GitHubCommitSparkPoint, GitHubRepoSummarySnapshot } from "./types";
3
+
4
+ export const formatGitHubCommitHoverLabel = (point: GitHubCommitPoint) => {
5
+ if (point.date.startsWith("n/a-")) {
6
+ return point.count === 1 ? "No date · 1 commit" : `No date · ${point.count} commits`;
7
+ }
8
+
9
+ return point.count === 1 ? `${point.date} · 1 commit` : `${point.date} · ${point.count} commits`;
10
+ };
11
+
12
+ export const buildGitHubStatusPill = (summary: GitHubRepoSummarySnapshot | null) => {
13
+ if (!summary) {
14
+ return "GitHub loading";
15
+ }
16
+
17
+ if (summary.status === "ok") {
18
+ return "GitHub live";
19
+ }
20
+
21
+ if (summary.status === "unavailable") {
22
+ return "GitHub unavailable";
23
+ }
24
+
25
+ return "GitHub error";
26
+ };
27
+
28
+ export const buildGitHubCommitSeries = (summary: GitHubRepoSummarySnapshot | null) => {
29
+ const fallbackSeries = Array.from({ length: GITHUB_COMMIT_SERIES_LENGTH }, (_, index) => ({
30
+ date: `n/a-${index}`,
31
+ count: 0,
32
+ }));
33
+
34
+ if (!summary?.commitsPerDay || summary.commitsPerDay.length === 0) {
35
+ return fallbackSeries;
36
+ }
37
+
38
+ const sorted = [...summary.commitsPerDay]
39
+ .sort((left, right) => left.date.localeCompare(right.date))
40
+ .slice(-GITHUB_COMMIT_SERIES_LENGTH);
41
+
42
+ const firstCommitIndex = sorted.findIndex((point) => point.count > 0);
43
+ if (firstCommitIndex > 0) {
44
+ return sorted.slice(firstCommitIndex);
45
+ }
46
+
47
+ return sorted;
48
+ };
49
+
50
+ export const buildGitHubCommitSparkPoints = (
51
+ series: GitHubCommitPoint[],
52
+ width: number,
53
+ height: number,
54
+ ): GitHubCommitSparkPoint[] => {
55
+ const values = series.map((point) => point.count);
56
+ const minValue = Math.min(...values);
57
+ const maxValue = Math.max(...values);
58
+ const valueRange = Math.max(1, maxValue - minValue);
59
+
60
+ return series.map((point, index) => {
61
+ const x = (index / Math.max(1, series.length - 1)) * width;
62
+ const y = height - ((point.count - minValue) / valueRange) * height;
63
+ return {
64
+ date: point.date,
65
+ count: point.count,
66
+ x,
67
+ y,
68
+ };
69
+ });
70
+ };
71
+
72
+ export const buildGitHubSparkPolylinePoints = (series: GitHubCommitSparkPoint[]) =>
73
+ series.map((point) => `${point.x.toFixed(1)},${point.y.toFixed(1)}`).join(" ");
74
+
75
+ export const buildGitHubCommitCount = (series: GitHubCommitPoint[]) =>
76
+ series.reduce((total, point) => total + point.count, 0);
@@ -0,0 +1,91 @@
1
+ import { asNumber, asRecord, asString } from "@octogent/core";
2
+
3
+ import type { GitHubCommitPoint, GitHubRecentCommit, GitHubRepoSummarySnapshot } from "./types";
4
+
5
+ const normalizeGitHubCommitPoint = (value: unknown): GitHubCommitPoint | null => {
6
+ const record = asRecord(value);
7
+ if (!record) {
8
+ return null;
9
+ }
10
+
11
+ const date = asString(record.date);
12
+ const count = asNumber(record.count);
13
+ if (!date || count === null) {
14
+ return null;
15
+ }
16
+
17
+ return {
18
+ date,
19
+ count: Math.max(0, Math.round(count)),
20
+ };
21
+ };
22
+
23
+ const normalizeGitHubRecentCommit = (value: unknown): GitHubRecentCommit | null => {
24
+ const record = asRecord(value);
25
+ if (!record) {
26
+ return null;
27
+ }
28
+
29
+ const hash = asString(record.hash)?.trim();
30
+ const shortHash = asString(record.shortHash)?.trim();
31
+ const subject = asString(record.subject)?.trim();
32
+ const authorName = asString(record.authorName)?.trim();
33
+ const authorEmail = asString(record.authorEmail)?.trim() ?? "";
34
+ const authoredAt = asString(record.authoredAt)?.trim();
35
+ const body = asString(record.body)?.trim() ?? "";
36
+ const filesChanged = asNumber(record.filesChanged) ?? 0;
37
+ const insertions = asNumber(record.insertions) ?? 0;
38
+ const deletions = asNumber(record.deletions) ?? 0;
39
+ if (!hash || !shortHash || !subject || !authorName || !authoredAt) {
40
+ return null;
41
+ }
42
+
43
+ return {
44
+ hash,
45
+ shortHash,
46
+ subject,
47
+ authorName,
48
+ authorEmail,
49
+ authoredAt,
50
+ body,
51
+ filesChanged,
52
+ insertions,
53
+ deletions,
54
+ };
55
+ };
56
+
57
+ export const normalizeGitHubRepoSummarySnapshot = (
58
+ value: unknown,
59
+ ): GitHubRepoSummarySnapshot | null => {
60
+ const record = asRecord(value);
61
+ if (!record) {
62
+ return null;
63
+ }
64
+
65
+ const status = record.status;
66
+ if (status !== "ok" && status !== "unavailable" && status !== "error") {
67
+ return null;
68
+ }
69
+
70
+ const rawCommitsPerDay = Array.isArray(record.commitsPerDay) ? record.commitsPerDay : [];
71
+ const commitsPerDay = rawCommitsPerDay
72
+ .map((point) => normalizeGitHubCommitPoint(point))
73
+ .filter((point): point is GitHubCommitPoint => point !== null);
74
+ const rawRecentCommits = Array.isArray(record.recentCommits) ? record.recentCommits : [];
75
+ const recentCommits = rawRecentCommits
76
+ .map((commit) => normalizeGitHubRecentCommit(commit))
77
+ .filter((commit): commit is GitHubRecentCommit => commit !== null);
78
+
79
+ return {
80
+ status,
81
+ source: record.source === "gh-cli" ? "gh-cli" : "none",
82
+ fetchedAt: asString(record.fetchedAt) ?? new Date().toISOString(),
83
+ message: asString(record.message),
84
+ repo: asString(record.repo),
85
+ stargazerCount: asNumber(record.stargazerCount),
86
+ openIssueCount: asNumber(record.openIssueCount),
87
+ openPullRequestCount: asNumber(record.openPullRequestCount),
88
+ commitsPerDay,
89
+ recentCommits,
90
+ };
91
+ };
@@ -0,0 +1,18 @@
1
+ import { useMemo } from "react";
2
+
3
+ import {
4
+ type TerminalRuntimeStateInfo,
5
+ type TerminalRuntimeStateStore,
6
+ useTerminalRuntimeStates,
7
+ } from "../terminalRuntimeStateStore";
8
+ import type { TerminalView } from "../types";
9
+
10
+ export type AgentRuntimeStateInfo = TerminalRuntimeStateInfo;
11
+
12
+ export const useAgentRuntimeStates = (
13
+ runtimeStateStore: TerminalRuntimeStateStore,
14
+ columns: TerminalView,
15
+ ): Map<string, AgentRuntimeStateInfo> => {
16
+ const terminalIds = useMemo(() => columns.map((column) => column.terminalId), [columns]);
17
+ return useTerminalRuntimeStates(runtimeStateStore, terminalIds);
18
+ };
@@ -0,0 +1,53 @@
1
+ import { useEffect, useState } from "react";
2
+
3
+ import { buildTerminalSnapshotsUrl } from "../../runtime/runtimeEndpoints";
4
+ import { BACKEND_LIVENESS_SCAN_INTERVAL_MS } from "../constants";
5
+
6
+ type BackendLivenessStatus = "live" | "offline";
7
+
8
+ export const useBackendLivenessPolling = (): BackendLivenessStatus => {
9
+ const [status, setStatus] = useState<BackendLivenessStatus>("offline");
10
+
11
+ useEffect(() => {
12
+ let isDisposed = false;
13
+ let isInFlight = false;
14
+
15
+ const refreshLiveness = async () => {
16
+ if (isDisposed || isInFlight) {
17
+ return;
18
+ }
19
+
20
+ isInFlight = true;
21
+ try {
22
+ const response = await fetch(buildTerminalSnapshotsUrl(), {
23
+ method: "GET",
24
+ headers: {
25
+ Accept: "application/json",
26
+ },
27
+ });
28
+
29
+ if (!isDisposed) {
30
+ setStatus(response.ok ? "live" : "offline");
31
+ }
32
+ } catch {
33
+ if (!isDisposed) {
34
+ setStatus("offline");
35
+ }
36
+ } finally {
37
+ isInFlight = false;
38
+ }
39
+ };
40
+
41
+ void refreshLiveness();
42
+ const timerId = window.setInterval(() => {
43
+ void refreshLiveness();
44
+ }, BACKEND_LIVENESS_SCAN_INTERVAL_MS);
45
+
46
+ return () => {
47
+ isDisposed = true;
48
+ window.clearInterval(timerId);
49
+ };
50
+ }, []);
51
+
52
+ return status;
53
+ };