@deepseek-harness-tui/dsh-tui 0.9.0 → 0.9.2

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 (307) hide show
  1. package/README.md +69 -256
  2. package/bin/dsh-tui.js +172 -5
  3. package/cordis.patch.yml +16 -4
  4. package/dsh-ecosystem-spec/protocols/profile-definitions.d.ts +13 -13
  5. package/dsh-ecosystem-spec/protocols/profile-definitions.js +59 -59
  6. package/dsh-ecosystem-spec/protocols/tui-channel-http.d.ts +15 -15
  7. package/dsh-ecosystem-spec/protocols/tui-channel-http.js +32 -32
  8. package/dsh-ecosystem-spec/protocols/tui-channel.d.ts +59 -59
  9. package/dsh-ecosystem-spec/protocols/tui-channel.js +166 -166
  10. package/dsh-ecosystem-spec/protocols/tui-contributions.d.ts +42 -42
  11. package/dsh-ecosystem-spec/protocols/tui-contributions.js +143 -143
  12. package/dsh-ecosystem-spec/registry/README.md +14 -14
  13. package/dsh-ecosystem-spec/registry/host-descriptor.tui.example.json +56 -56
  14. package/dsh-ecosystem-spec/registry/permissions-0.1.json +13 -13
  15. package/dsh-ecosystem-spec/registry/registry-0.15.json +149 -149
  16. package/lib/types/cc/hyperlink.d.ts +7 -1
  17. package/lib/types/cc/hyperlink.d.ts.map +1 -1
  18. package/lib/types/cc/hyperlink.js +3 -2
  19. package/lib/types/cc/markdown.d.ts.map +1 -1
  20. package/lib/types/cc/markdown.js +33 -3
  21. package/lib/types/cc/sessionColors.d.ts +27 -0
  22. package/lib/types/cc/sessionColors.d.ts.map +1 -0
  23. package/lib/types/cc/sessionColors.js +39 -0
  24. package/lib/types/commands.d.ts.map +1 -1
  25. package/lib/types/commands.js +6 -0
  26. package/lib/types/components/AutoRecapRow.d.ts +23 -0
  27. package/lib/types/components/AutoRecapRow.d.ts.map +1 -0
  28. package/lib/types/components/AutoRecapRow.js +23 -0
  29. package/lib/types/components/ColorPicker.d.ts +17 -0
  30. package/lib/types/components/ColorPicker.d.ts.map +1 -0
  31. package/lib/types/components/ColorPicker.js +20 -0
  32. package/lib/types/components/CommandSuggestions.d.ts +3 -1
  33. package/lib/types/components/CommandSuggestions.d.ts.map +1 -1
  34. package/lib/types/components/ContextBarView.d.ts +33 -0
  35. package/lib/types/components/ContextBarView.d.ts.map +1 -0
  36. package/lib/types/components/ContextBarView.js +46 -0
  37. package/lib/types/components/EffortInputBorder.d.ts +12 -1
  38. package/lib/types/components/EffortInputBorder.d.ts.map +1 -1
  39. package/lib/types/components/EffortInputBorder.js +22 -5
  40. package/lib/types/components/FileActionsPanel.d.ts +22 -0
  41. package/lib/types/components/FileActionsPanel.d.ts.map +1 -0
  42. package/lib/types/components/FileActionsPanel.js +28 -0
  43. package/lib/types/components/FileSuggestions.d.ts +3 -1
  44. package/lib/types/components/FileSuggestions.d.ts.map +1 -1
  45. package/lib/types/components/MessageList.d.ts +3 -1
  46. package/lib/types/components/MessageList.d.ts.map +1 -1
  47. package/lib/types/components/MessageList.js +13 -6
  48. package/lib/types/components/ModelPicker.d.ts +26 -4
  49. package/lib/types/components/ModelPicker.d.ts.map +1 -1
  50. package/lib/types/components/ModelPicker.js +26 -6
  51. package/lib/types/components/PromptInput.d.ts.map +1 -1
  52. package/lib/types/components/PromptInput.js +365 -57
  53. package/lib/types/components/RecapPanel.d.ts +25 -0
  54. package/lib/types/components/RecapPanel.d.ts.map +1 -0
  55. package/lib/types/components/RecapPanel.js +55 -0
  56. package/lib/types/components/ScrollbarGutter.d.ts.map +1 -1
  57. package/lib/types/components/ScrollbarGutter.js +55 -4
  58. package/lib/types/components/SubagentCard.d.ts.map +1 -1
  59. package/lib/types/components/SubagentCard.js +6 -4
  60. package/lib/types/components/SuggestionCard.d.ts +4 -2
  61. package/lib/types/components/SuggestionCard.d.ts.map +1 -1
  62. package/lib/types/components/TimelineRail.d.ts.map +1 -1
  63. package/lib/types/components/TimelineRail.js +9 -2
  64. package/lib/types/components/design-system/ThemeProvider.d.ts +7 -0
  65. package/lib/types/components/design-system/ThemeProvider.d.ts.map +1 -1
  66. package/lib/types/components/design-system/ThemeProvider.js +2 -1
  67. package/lib/types/components/messages/AssistantToolUseMessage.d.ts +8 -1
  68. package/lib/types/components/messages/AssistantToolUseMessage.d.ts.map +1 -1
  69. package/lib/types/components/messages/AssistantToolUseMessage.js +36 -8
  70. package/lib/types/components/questions/AskUserQuestionPanel.d.ts +7 -3
  71. package/lib/types/components/questions/AskUserQuestionPanel.d.ts.map +1 -1
  72. package/lib/types/components/questions/AskUserQuestionPanel.js +41 -9
  73. package/lib/types/dsh-adapter/channel.d.ts +101 -2
  74. package/lib/types/dsh-adapter/channel.d.ts.map +1 -1
  75. package/lib/types/dsh-adapter/channel.js +969 -93
  76. package/lib/types/dsh-adapter/compat/index.d.ts +8 -6
  77. package/lib/types/dsh-adapter/compat/index.d.ts.map +1 -1
  78. package/lib/types/dsh-adapter/compat/index.js +8 -6
  79. package/lib/types/dsh-adapter/compat/sessionLog.d.ts +150 -0
  80. package/lib/types/dsh-adapter/compat/sessionLog.d.ts.map +1 -1
  81. package/lib/types/dsh-adapter/compat/sessionLog.js +518 -41
  82. package/lib/types/dsh-adapter/contract.d.ts +1 -1
  83. package/lib/types/dsh-adapter/contract.d.ts.map +1 -1
  84. package/lib/types/dsh-adapter/contract.js +1 -0
  85. package/lib/types/dsh-adapter/extension-events.d.ts +1 -1
  86. package/lib/types/dsh-adapter/extension-events.d.ts.map +1 -1
  87. package/lib/types/dsh-adapter/index.d.ts +10 -0
  88. package/lib/types/dsh-adapter/index.d.ts.map +1 -1
  89. package/lib/types/dsh-adapter/index.js +5 -0
  90. package/lib/types/dsh-adapter/plugin.d.ts.map +1 -1
  91. package/lib/types/dsh-adapter/plugin.js +317 -8
  92. package/lib/types/dsh-adapter/presets.d.ts +18 -0
  93. package/lib/types/dsh-adapter/presets.d.ts.map +1 -1
  94. package/lib/types/dsh-adapter/presets.js +31 -0
  95. package/lib/types/dsh-adapter/providerWizard.d.ts +29 -1
  96. package/lib/types/dsh-adapter/providerWizard.d.ts.map +1 -1
  97. package/lib/types/dsh-adapter/providerWizard.js +89 -5
  98. package/lib/types/dsh-adapter/questions.d.ts +19 -2
  99. package/lib/types/dsh-adapter/questions.d.ts.map +1 -1
  100. package/lib/types/dsh-adapter/questions.js +53 -10
  101. package/lib/types/dsh-adapter/recap.d.ts +49 -0
  102. package/lib/types/dsh-adapter/recap.d.ts.map +1 -0
  103. package/lib/types/dsh-adapter/recap.js +121 -0
  104. package/lib/types/dsh-adapter/sessionTree.d.ts +314 -0
  105. package/lib/types/dsh-adapter/sessionTree.d.ts.map +1 -0
  106. package/lib/types/dsh-adapter/sessionTree.js +954 -0
  107. package/lib/types/dsh-adapter/shortcuts.d.ts +15 -44
  108. package/lib/types/dsh-adapter/shortcuts.d.ts.map +1 -1
  109. package/lib/types/dsh-adapter/shortcuts.js +28 -165
  110. package/lib/types/dsh-adapter/types.d.ts +1 -1
  111. package/lib/types/dsh-adapter/types.d.ts.map +1 -1
  112. package/lib/types/herdr.js +1 -1
  113. package/lib/types/i18n.d.ts +604 -176
  114. package/lib/types/i18n.d.ts.map +1 -1
  115. package/lib/types/i18n.js +206 -72
  116. package/lib/types/ink/log-update.d.ts.map +1 -1
  117. package/lib/types/ink/log-update.js +21 -0
  118. package/lib/types/migrationPrefs.d.ts +60 -0
  119. package/lib/types/migrationPrefs.d.ts.map +1 -0
  120. package/lib/types/migrationPrefs.js +97 -0
  121. package/lib/types/modelGroups.d.ts +72 -0
  122. package/lib/types/modelGroups.d.ts.map +1 -0
  123. package/lib/types/modelGroups.js +97 -0
  124. package/lib/types/modelRecents.d.ts +39 -0
  125. package/lib/types/modelRecents.d.ts.map +1 -0
  126. package/lib/types/modelRecents.js +97 -0
  127. package/lib/types/native-ts/yoga-layout/index.d.ts +11 -0
  128. package/lib/types/native-ts/yoga-layout/index.d.ts.map +1 -1
  129. package/lib/types/native-ts/yoga-layout/index.js +31 -1
  130. package/lib/types/oauth.d.ts +15 -0
  131. package/lib/types/oauth.d.ts.map +1 -0
  132. package/lib/types/oauth.js +14 -0
  133. package/lib/types/plugin-spec/registry.d.ts +1 -1
  134. package/lib/types/plugin-spec/registry.js +1 -1
  135. package/lib/types/reload.d.ts +109 -0
  136. package/lib/types/reload.d.ts.map +1 -0
  137. package/lib/types/reload.js +118 -0
  138. package/lib/types/screens/Chat.d.ts +3 -1
  139. package/lib/types/screens/Chat.d.ts.map +1 -1
  140. package/lib/types/screens/Chat.js +588 -64
  141. package/lib/types/screens/SessionTree.d.ts +27 -0
  142. package/lib/types/screens/SessionTree.d.ts.map +1 -0
  143. package/lib/types/screens/SessionTree.js +555 -0
  144. package/lib/types/screens/StatusLine.d.ts +0 -9
  145. package/lib/types/screens/StatusLine.d.ts.map +1 -1
  146. package/lib/types/screens/StatusLine.js +215 -52
  147. package/lib/types/screens/StatusMetrics.d.ts +35 -3
  148. package/lib/types/screens/StatusMetrics.d.ts.map +1 -1
  149. package/lib/types/screens/StatusMetrics.js +63 -8
  150. package/lib/types/screens/chatOverlay.d.ts +17 -1
  151. package/lib/types/screens/chatOverlay.d.ts.map +1 -1
  152. package/lib/types/screens/chatOverlay.js +3 -1
  153. package/lib/types/tips.d.ts.map +1 -1
  154. package/lib/types/tips.js +22 -4
  155. package/lib/types/update.d.ts +159 -0
  156. package/lib/types/update.d.ts.map +1 -1
  157. package/lib/types/update.js +496 -30
  158. package/lib/types/utils/debug.d.ts +1 -1
  159. package/lib/types/utils/debug.js +3 -3
  160. package/lib/types/utils/fileTarget.d.ts +46 -0
  161. package/lib/types/utils/fileTarget.d.ts.map +1 -0
  162. package/lib/types/utils/fileTarget.js +178 -0
  163. package/lib/types/utils/keymap.d.ts +134 -0
  164. package/lib/types/utils/keymap.d.ts.map +1 -0
  165. package/lib/types/utils/keymap.js +311 -0
  166. package/lib/types/utils/mentions.d.ts +25 -2
  167. package/lib/types/utils/mentions.d.ts.map +1 -1
  168. package/lib/types/utils/mentions.js +69 -10
  169. package/lib/types/utils/openExternal.d.ts +50 -0
  170. package/lib/types/utils/openExternal.d.ts.map +1 -0
  171. package/lib/types/utils/openExternal.js +170 -0
  172. package/node_modules/@deepseek-harness-tui/dsh-auth/LICENSE +21 -0
  173. package/node_modules/@deepseek-harness-tui/dsh-auth/README.md +140 -0
  174. package/node_modules/@deepseek-harness-tui/dsh-auth/cordis.patch.yml +11 -0
  175. package/node_modules/@deepseek-harness-tui/dsh-auth/dsh-plugin.json +44 -0
  176. package/node_modules/@deepseek-harness-tui/dsh-auth/lib/command.d.ts +14 -0
  177. package/node_modules/@deepseek-harness-tui/dsh-auth/lib/command.js +67 -0
  178. package/node_modules/@deepseek-harness-tui/dsh-auth/lib/credentials.d.ts +64 -0
  179. package/node_modules/@deepseek-harness-tui/dsh-auth/lib/credentials.js +170 -0
  180. package/node_modules/@deepseek-harness-tui/dsh-auth/lib/index.d.ts +79 -0
  181. package/node_modules/@deepseek-harness-tui/dsh-auth/lib/index.js +196 -0
  182. package/node_modules/@deepseek-harness-tui/dsh-auth/lib/interaction.d.ts +68 -0
  183. package/node_modules/@deepseek-harness-tui/dsh-auth/lib/interaction.js +193 -0
  184. package/node_modules/@deepseek-harness-tui/dsh-auth/lib/opener.d.ts +32 -0
  185. package/node_modules/@deepseek-harness-tui/dsh-auth/lib/opener.js +101 -0
  186. package/node_modules/@deepseek-harness-tui/dsh-auth/lib/profiles.d.ts +31 -0
  187. package/node_modules/@deepseek-harness-tui/dsh-auth/lib/profiles.js +68 -0
  188. package/node_modules/@deepseek-harness-tui/dsh-auth/lib/service.d.ts +69 -0
  189. package/node_modules/@deepseek-harness-tui/dsh-auth/lib/service.js +129 -0
  190. package/node_modules/@deepseek-harness-tui/dsh-auth/package.json +70 -0
  191. package/node_modules/@dsh-std/command/CHANGELOG.md +8 -8
  192. package/node_modules/@dsh-std/command/LICENSE +21 -21
  193. package/node_modules/@dsh-std/command/README.md +26 -26
  194. package/node_modules/@dsh-std/command/README.zh.md +28 -28
  195. package/node_modules/@dsh-std/command/lib/index.js.map +1 -1
  196. package/node_modules/@dsh-std/command/package.json +22 -22
  197. package/node_modules/@dsh-std/connection/CHANGELOG.md +8 -8
  198. package/node_modules/@dsh-std/connection/LICENSE +21 -21
  199. package/node_modules/@dsh-std/connection/README.md +42 -42
  200. package/node_modules/@dsh-std/connection/README.zh.md +42 -42
  201. package/node_modules/@dsh-std/connection/lib/index.js.map +1 -1
  202. package/node_modules/@dsh-std/connection/lib/memory.js.map +1 -1
  203. package/node_modules/@dsh-std/connection/lib/resolve-3X6ausgz.js.map +1 -1
  204. package/node_modules/@dsh-std/connection/package.json +20 -20
  205. package/node_modules/@dsh-std/core/CHANGELOG.md +8 -8
  206. package/node_modules/@dsh-std/core/LICENSE +21 -21
  207. package/node_modules/@dsh-std/core/README.md +13 -13
  208. package/node_modules/@dsh-std/core/README.zh.md +13 -13
  209. package/node_modules/@dsh-std/core/lib/index.js.map +1 -1
  210. package/node_modules/@dsh-std/core/package.json +21 -21
  211. package/node_modules/@dsh-std/manifest/CHANGELOG.md +9 -9
  212. package/node_modules/@dsh-std/manifest/LICENSE +21 -21
  213. package/node_modules/@dsh-std/manifest/README.md +5 -5
  214. package/node_modules/@dsh-std/manifest/README.zh.md +5 -5
  215. package/node_modules/@dsh-std/manifest/lib/index.js.map +1 -1
  216. package/node_modules/@dsh-std/manifest/package.json +20 -20
  217. package/node_modules/@dsh-std/manifest/schema/dsh-plugin-0.15.schema.json +144 -144
  218. package/node_modules/@dsh-std/messages/CHANGELOG.md +8 -8
  219. package/node_modules/@dsh-std/messages/LICENSE +21 -21
  220. package/node_modules/@dsh-std/messages/README.md +5 -5
  221. package/node_modules/@dsh-std/messages/README.zh.md +7 -7
  222. package/node_modules/@dsh-std/messages/lib/index.js.map +1 -1
  223. package/node_modules/@dsh-std/messages/package.json +20 -20
  224. package/node_modules/@dsh-std/messages/schema/message-observer-envelope-0.15.schema.json +54 -54
  225. package/node_modules/@dsh-std/presentation/CHANGELOG.md +9 -9
  226. package/node_modules/@dsh-std/presentation/LICENSE +21 -21
  227. package/node_modules/@dsh-std/presentation/README.md +27 -27
  228. package/node_modules/@dsh-std/presentation/README.zh.md +29 -29
  229. package/node_modules/@dsh-std/presentation/lib/callback.js.map +1 -1
  230. package/node_modules/@dsh-std/presentation/lib/index.js.map +1 -1
  231. package/node_modules/@dsh-std/presentation/package.json +25 -25
  232. package/node_modules/@dsh-std/storage/CHANGELOG.md +8 -8
  233. package/node_modules/@dsh-std/storage/LICENSE +21 -21
  234. package/node_modules/@dsh-std/storage/README.md +5 -5
  235. package/node_modules/@dsh-std/storage/README.zh.md +7 -7
  236. package/node_modules/@dsh-std/storage/lib/index.js.map +1 -1
  237. package/node_modules/@dsh-std/storage/package.json +20 -20
  238. package/node_modules/@dsh-std/storage/schema/local-storage-0.15.schema.json +56 -56
  239. package/package.json +17 -5
  240. package/presets/liangshen/.dsh-tui-managed.json +1 -1
  241. package/presets/liangshen/instruction-hint.mjs +8 -0
  242. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/connection/CHANGELOG.md +8 -8
  243. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/connection/LICENSE +21 -21
  244. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/connection/README.md +42 -42
  245. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/connection/README.zh.md +42 -42
  246. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/connection/lib/index.js.map +1 -1
  247. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/connection/lib/memory.js.map +1 -1
  248. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/connection/lib/resolve-3X6ausgz.js.map +1 -1
  249. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/connection/package.json +20 -20
  250. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/core/CHANGELOG.md +8 -8
  251. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/core/LICENSE +21 -21
  252. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/core/README.md +13 -13
  253. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/core/README.zh.md +13 -13
  254. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/core/lib/index.js.map +1 -1
  255. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/core/package.json +21 -21
  256. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/manifest/CHANGELOG.md +9 -9
  257. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/manifest/LICENSE +21 -21
  258. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/manifest/README.md +5 -5
  259. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/manifest/README.zh.md +5 -5
  260. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/manifest/lib/index.js.map +1 -1
  261. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/manifest/package.json +20 -20
  262. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/manifest/schema/dsh-plugin-0.15.schema.json +144 -144
  263. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/presentation/CHANGELOG.md +9 -9
  264. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/presentation/LICENSE +21 -21
  265. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/presentation/README.md +27 -27
  266. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/presentation/README.zh.md +29 -29
  267. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/presentation/lib/callback.js.map +1 -1
  268. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/presentation/lib/index.js.map +1 -1
  269. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/presentation/package.json +25 -25
  270. package/vendor/dsh-std/packages/connection/node_modules/@dsh-std/core/CHANGELOG.md +8 -8
  271. package/vendor/dsh-std/packages/connection/node_modules/@dsh-std/core/LICENSE +21 -21
  272. package/vendor/dsh-std/packages/connection/node_modules/@dsh-std/core/README.md +13 -13
  273. package/vendor/dsh-std/packages/connection/node_modules/@dsh-std/core/README.zh.md +13 -13
  274. package/vendor/dsh-std/packages/connection/node_modules/@dsh-std/core/lib/index.js.map +1 -1
  275. package/vendor/dsh-std/packages/connection/node_modules/@dsh-std/core/package.json +21 -21
  276. package/vendor/dsh-std/packages/manifest/node_modules/@dsh-std/core/CHANGELOG.md +8 -8
  277. package/vendor/dsh-std/packages/manifest/node_modules/@dsh-std/core/LICENSE +21 -21
  278. package/vendor/dsh-std/packages/manifest/node_modules/@dsh-std/core/README.md +13 -13
  279. package/vendor/dsh-std/packages/manifest/node_modules/@dsh-std/core/README.zh.md +13 -13
  280. package/vendor/dsh-std/packages/manifest/node_modules/@dsh-std/core/lib/index.js.map +1 -1
  281. package/vendor/dsh-std/packages/manifest/node_modules/@dsh-std/core/package.json +21 -21
  282. package/vendor/dsh-std/packages/messages/node_modules/@dsh-std/core/CHANGELOG.md +8 -8
  283. package/vendor/dsh-std/packages/messages/node_modules/@dsh-std/core/LICENSE +21 -21
  284. package/vendor/dsh-std/packages/messages/node_modules/@dsh-std/core/README.md +13 -13
  285. package/vendor/dsh-std/packages/messages/node_modules/@dsh-std/core/README.zh.md +13 -13
  286. package/vendor/dsh-std/packages/messages/node_modules/@dsh-std/core/lib/index.js.map +1 -1
  287. package/vendor/dsh-std/packages/messages/node_modules/@dsh-std/core/package.json +21 -21
  288. package/vendor/dsh-std/packages/presentation/node_modules/@dsh-std/connection/CHANGELOG.md +8 -8
  289. package/vendor/dsh-std/packages/presentation/node_modules/@dsh-std/connection/LICENSE +21 -21
  290. package/vendor/dsh-std/packages/presentation/node_modules/@dsh-std/connection/README.md +42 -42
  291. package/vendor/dsh-std/packages/presentation/node_modules/@dsh-std/connection/README.zh.md +42 -42
  292. package/vendor/dsh-std/packages/presentation/node_modules/@dsh-std/connection/lib/index.js.map +1 -1
  293. package/vendor/dsh-std/packages/presentation/node_modules/@dsh-std/connection/lib/memory.js.map +1 -1
  294. package/vendor/dsh-std/packages/presentation/node_modules/@dsh-std/connection/lib/resolve-3X6ausgz.js.map +1 -1
  295. package/vendor/dsh-std/packages/presentation/node_modules/@dsh-std/connection/package.json +20 -20
  296. package/vendor/dsh-std/packages/presentation/node_modules/@dsh-std/core/CHANGELOG.md +8 -8
  297. package/vendor/dsh-std/packages/presentation/node_modules/@dsh-std/core/LICENSE +21 -21
  298. package/vendor/dsh-std/packages/presentation/node_modules/@dsh-std/core/README.md +13 -13
  299. package/vendor/dsh-std/packages/presentation/node_modules/@dsh-std/core/README.zh.md +13 -13
  300. package/vendor/dsh-std/packages/presentation/node_modules/@dsh-std/core/lib/index.js.map +1 -1
  301. package/vendor/dsh-std/packages/presentation/node_modules/@dsh-std/core/package.json +21 -21
  302. package/vendor/dsh-std/packages/storage/node_modules/@dsh-std/core/CHANGELOG.md +8 -8
  303. package/vendor/dsh-std/packages/storage/node_modules/@dsh-std/core/LICENSE +21 -21
  304. package/vendor/dsh-std/packages/storage/node_modules/@dsh-std/core/README.md +13 -13
  305. package/vendor/dsh-std/packages/storage/node_modules/@dsh-std/core/README.zh.md +13 -13
  306. package/vendor/dsh-std/packages/storage/node_modules/@dsh-std/core/lib/index.js.map +1 -1
  307. package/vendor/dsh-std/packages/storage/node_modules/@dsh-std/core/package.json +21 -21
@@ -0,0 +1,954 @@
1
+ export const TREE_FILTERS = ['default', 'no-tools', 'user-only', 'all'];
2
+ /**
3
+ * Confirm-time preview for a USER-message pick: how much of its branch the
4
+ * drop removes. Undefined for non-user entries (they keep through their
5
+ * step) and whenever the turn cannot be located in the loaded events.
6
+ */
7
+ export function droppedTurnInfo(data, entry) {
8
+ if (entry.kind !== 'user')
9
+ return undefined;
10
+ const facts = data.rewindFacts.get(entry.sessionId);
11
+ if (facts === undefined)
12
+ return undefined;
13
+ const turn = facts.turns.find(range => entry.seq > range.start && entry.seq <= range.end);
14
+ if (turn === undefined)
15
+ return undefined;
16
+ return {
17
+ droppedEntries: turn.entries,
18
+ coversBranch: facts.tailComplete && facts.ownEntries > 0 && turn.entries === facts.ownEntries,
19
+ };
20
+ }
21
+ /** Preview cap for one entry line (the screen truncates further to width). */
22
+ const ENTRY_PREVIEW_LIMIT = 120;
23
+ /** Tool arguments preview cap inside a tool entry line. */
24
+ const TOOL_ARGS_PREVIEW_LIMIT = 60;
25
+ /** Whitespace-folded, capped one-line preview. */
26
+ function preview(text, limit = ENTRY_PREVIEW_LIMIT) {
27
+ const flat = text.replace(/\s+/g, ' ').trim();
28
+ return flat.length <= limit ? flat : `${flat.slice(0, limit)}…`;
29
+ }
30
+ /** All text blocks joined (assistant messages, compact summaries). */
31
+ function textOf(content) {
32
+ return (content ?? []).map(block => (block.type === 'text' ? (block.text ?? '') : '')).join('').trim();
33
+ }
34
+ /** FIRST text block only — `@`-mention attachments ride as later blocks
35
+ * (channel.ts firstTextOf, issue #15). */
36
+ function firstTextOf(content) {
37
+ return (content ?? []).find(block => block.type === 'text')?.text?.trim() ?? '';
38
+ }
39
+ /**
40
+ * Coalesce runs of same-type assistant/chunk deltas into single synthetic
41
+ * events for REPLAY only. A streamed turn logs one event per token (~100k
42
+ * events in long sessions); replaying them one at a time costs per-chunk
43
+ * string growth on every row (quadratic in the turn's length). Merging is
44
+ * outcome-identical: ensureStreaming/ensureReasoning only read chunk.type
45
+ * and the concatenated text, and the row's seq comes from the run's FIRST
46
+ * chunk (the fork boundary rewindToNode derives from it). Parts join once —
47
+ * no quadratic concat. Live events never go through this.
48
+ *
49
+ * (Moved from channel.ts: the transcript replay and the tree extraction
50
+ * share it.)
51
+ */
52
+ export function coalesceReplayEvents(events) {
53
+ const out = [];
54
+ let run = null;
55
+ const flush = () => {
56
+ if (run === null)
57
+ return;
58
+ const chunk = run.event.data.chunk;
59
+ out.push({
60
+ ...run.event,
61
+ data: { ...run.event.data, chunk: { ...chunk, text: run.parts.join('') } },
62
+ });
63
+ run = null;
64
+ };
65
+ for (const event of events) {
66
+ if (event.type === 'assistant/chunk' &&
67
+ (event.data.chunk.type === 'text-delta' || event.data.chunk.type === 'reasoning-delta')) {
68
+ if (run !== null && run.type === event.data.chunk.type) {
69
+ // oxlint-disable-next-line typescript/no-unnecessary-condition -- durable replay data may lack text
70
+ run.parts.push(event.data.chunk.text ?? '');
71
+ continue;
72
+ }
73
+ flush();
74
+ // oxlint-disable-next-line typescript/no-unnecessary-condition -- durable replay data may lack text
75
+ run = { event, type: event.data.chunk.type, parts: [event.data.chunk.text ?? ''] };
76
+ continue;
77
+ }
78
+ flush();
79
+ out.push(event);
80
+ }
81
+ flush();
82
+ return out;
83
+ }
84
+ /**
85
+ * Fork boundary for rewinding to the entry at `seq`: the seq just before its
86
+ * enclosing turn/start (DSH logs `turn/start → user/message → … → turn/end`,
87
+ * so a message's own seq sits inside an open turn and fork would reject it).
88
+ * This is the DROP-THE-TURN boundary — used for user messages (the turn is
89
+ * dropped, its prompt returns to the input) and as the fallback for entries
90
+ * of a still-open turn. Falls back to `seq` itself when no enclosing
91
+ * turn/start exists (bookkeeping between turns).
92
+ */
93
+ export function turnBoundary(events, seq) {
94
+ const start = events[seq]?.type === 'turn/end' ? seq - 1 : seq;
95
+ for (let i = start; i >= 0; i--) {
96
+ const event = events[i];
97
+ // oxlint-disable-next-line typescript/no-unnecessary-condition -- runtime guard: seq may exceed events
98
+ if (event === undefined)
99
+ break;
100
+ if (event.type === 'turn/start')
101
+ return event.seq - 1;
102
+ if (event.type === 'turn/end')
103
+ break;
104
+ }
105
+ return seq;
106
+ }
107
+ /**
108
+ * Rewind target for a tree entry — pi's navigateTree semantics mapped onto
109
+ * DSH's turn-closed fork constraint (the seed must not end inside an open
110
+ * turn). Callers must pass the FULL session log (seq === array index).
111
+ *
112
+ * - user message → DROP its turn: the seq just before the enclosing
113
+ * turn/start (turnBoundary); the turn's prompt returns to the input for
114
+ * re-editing. -1 when that turn is the log's own first turn.
115
+ * - any other entry inside a CLOSED turn (assistant/tool/notice/interrupt)
116
+ * → KEEP through the entry's enclosing STEP: the boundary is that step's
117
+ * step/end, with closeTurn set. DSH agentic turns are marathons (one
118
+ * prompt → N steps → thousands of events), so turn-granular keeping is
119
+ * useless mid-turn — picking a bash call of step 2 in a 6-step turn must
120
+ * not retain steps 3-6. The step is the finest SAFE cut: a step closes
121
+ * with every tool call answered, so the replayed context never dangles a
122
+ * tool_call the API would reject. When no step/end follows the entry
123
+ * inside its turn (no-step turns, or the entry sits past the last step),
124
+ * the boundary is the turn/end as before (no closeTurn).
125
+ * - entry between turns (a compact checkpoint) → its own seq.
126
+ * - entry inside the still-OPEN last turn → a closed step ahead of it is
127
+ * still a valid mid-turn cut (closeTurn set; the open tail drops); with no
128
+ * step/end ahead the turn cannot be kept at all, so it falls back to
129
+ * dropping the turn (turnBoundary) and restoring its prompt.
130
+ */
131
+ export function rewindTarget(events, seq) {
132
+ const selected = events[seq];
133
+ // oxlint-disable-next-line typescript/no-unnecessary-condition -- runtime guard: seq may exceed events
134
+ if (selected === undefined || selected.type === 'user/message') {
135
+ return { boundary: turnBoundary(events, seq) };
136
+ }
137
+ if (selected.type === 'turn/end')
138
+ return { boundary: seq }; // interrupt/notice entries keep through their turn's end
139
+ // The enclosing turn's number — needed to close a mid-turn cut.
140
+ let turn;
141
+ for (let i = seq; i >= 0; i--) {
142
+ const event = events[i];
143
+ if (event.type === 'turn/start') {
144
+ turn = event.data.turn;
145
+ break;
146
+ }
147
+ if (event.type === 'turn/end')
148
+ break; // between turns: no enclosing turn
149
+ }
150
+ for (let i = seq + 1; i < events.length; i++) {
151
+ const event = events[i];
152
+ if (event.type === 'step/end' && turn !== undefined) {
153
+ return { boundary: event.seq, closeTurn: turn };
154
+ }
155
+ if (event.type === 'turn/end')
156
+ return { boundary: event.seq };
157
+ if (event.type === 'turn/start')
158
+ break; // between turns: keep through the entry
159
+ }
160
+ return { boundary: turnBoundary(events, seq) };
161
+ }
162
+ /**
163
+ * Fork target for a tree entry — pi's `/fork` semantics: the picked entry is
164
+ * KEPT (a fork branches the conversation; it does not re-edit the past).
165
+ *
166
+ * - user message → keep THROUGH the message itself: the boundary is the
167
+ * entry's own seq and the turn's reply drops. The cut ends inside the
168
+ * still-open turn, so closeTurn carries the enclosing turn's number for
169
+ * the synthetic turn/end (the same shape a real interrupt writes). A
170
+ * between-turns plugin message (a compact checkpoint) is already
171
+ * turn-closed — no closer.
172
+ * - any other entry → identical to rewindTarget (keep through the
173
+ * entry's enclosing step / turn end).
174
+ *
175
+ * Unlike a rewind pick, a turn-0 user message forks fine (boundary = its own
176
+ * seq, never -1), and nothing returns to the input for re-editing.
177
+ */
178
+ export function forkTarget(events, seq) {
179
+ const selected = events[seq];
180
+ // oxlint-disable-next-line typescript/no-unnecessary-condition -- runtime guard: seq may exceed events
181
+ if (selected === undefined || selected.type !== 'user/message') {
182
+ return rewindTarget(events, seq);
183
+ }
184
+ // The enclosing turn's number — needed to close the mid-turn cut. A
185
+ // turn/end encountered first means the entry sits between turns.
186
+ let turn;
187
+ for (let i = seq; i >= 0; i--) {
188
+ const event = events[i];
189
+ if (event.type === 'turn/start') {
190
+ turn = event.data.turn;
191
+ break;
192
+ }
193
+ if (event.type === 'turn/end')
194
+ break; // between turns: already closed
195
+ }
196
+ return turn === undefined ? { boundary: seq } : { boundary: seq, closeTurn: turn };
197
+ }
198
+ /**
199
+ * Tail window of an over-budget LIVE session's events, aligned to whole
200
+ * turns. Recent turns are the likely rewind targets, so the budget keeps
201
+ * the tail — but a slice can open mid-turn, and entries of a partial turn
202
+ * make broken rows: when that partial turn is the log's OWN first turn its
203
+ * rewind boundary is -1 ("cannot rewind to the very first message"), yet
204
+ * the slice hides the turn/start@0 that firstTurn marking keys on — the
205
+ * row would confirm and then fail inside rewindToNode. So: align to the
206
+ * first whole turn inside the window. When the window holds no turn/start
207
+ * at all, the log's LAST turn alone overflows the budget — retry over the
208
+ * log before that turn: its earlier complete turns are perfectly good
209
+ * rewind targets and must not vanish behind one giant turn. Only when the
210
+ * oversized turn IS the log's own first turn does the window drop to empty
211
+ * — no complete turn in view means no safe rewind target to show. Entries
212
+ * dropped here stay rewindable through the real session; the slice only
213
+ * narrows what the tree displays.
214
+ */
215
+ export function liveTailWindow(events, remaining) {
216
+ if (events.length <= remaining)
217
+ return events;
218
+ if (remaining <= 0)
219
+ return [];
220
+ let scope = events;
221
+ for (;;) {
222
+ const sliced = scope.slice(scope.length - remaining);
223
+ const firstStart = sliced.findIndex(event => event.type === 'turn/start');
224
+ if (firstStart === 0)
225
+ return sliced;
226
+ if (firstStart > 0)
227
+ return sliced.slice(firstStart);
228
+ // No turn/start in the window: the oversized turn spans it whole. Cut
229
+ // the log just before that turn and retry — the window over the shorter
230
+ // scope either fits complete earlier turns or overflows again into an
231
+ // even earlier oversized turn (the loop converges: each cut removes at
232
+ // least one turn).
233
+ let lastStart = -1;
234
+ for (let i = scope.length - 1; i >= 0; i--) {
235
+ if (scope[i].type === 'turn/start') {
236
+ lastStart = i;
237
+ break;
238
+ }
239
+ }
240
+ if (lastStart <= 0)
241
+ return [];
242
+ scope = scope.slice(0, lastStart);
243
+ if (scope.length <= remaining)
244
+ return scope;
245
+ }
246
+ }
247
+ /** The turn's first human-typed text (the prompt rewind restores for editing). */
248
+ export function turnUserText(events, seq) {
249
+ const boundary = rewindTarget(events, seq).boundary;
250
+ // A boundary at/after the entry means its turn is KEPT (assistant/tool
251
+ // targets, between-turns checkpoints) — pi restores editor text only for
252
+ // dropped user messages, and scanning on from here would cross the NEXT
253
+ // turn/start and steal that turn's prompt.
254
+ if (boundary >= seq)
255
+ return '';
256
+ for (let i = boundary + 1; i < events.length; i++) {
257
+ const event = events[i];
258
+ if (event.type === 'turn/end')
259
+ break;
260
+ if (event.type !== 'user/message')
261
+ continue;
262
+ if (event.data.source.kind !== 'user')
263
+ continue;
264
+ const text = firstTextOf(event.data.content);
265
+ if (text)
266
+ return text;
267
+ }
268
+ return '';
269
+ }
270
+ /**
271
+ * One session's displayable entries, in seq order. Mirrors renderEvent's
272
+ * choices (channel.ts): human user messages only, compaction checkpoints as
273
+ * compact rows, assistant text (chunk-only text from aborted turns kept with
274
+ * an `aborted` label), tool cards minus ask_user_question, interrupts and
275
+ * failed turns. Bookkeeping events (turn/step markers, titles, requests,
276
+ * `session/end-seed`, activity frames…) never become entries.
277
+ */
278
+ export function extractEntries(sessionId, events) {
279
+ const entries = [];
280
+ // First-turn marking applies only when the log still starts at seq 0 — the
281
+ // complete history, whose turn 0 can never rewind. A budget-truncated tail
282
+ // opens mid-log; its first visible turn has a valid boundary in the full
283
+ // log (rewindToNode computes it there), so it must stay selectable.
284
+ const markFirstTurn = events.find(event => event.type === 'turn/start')?.seq === 0;
285
+ let turnsSeen = 0;
286
+ let inFirstTurn = false;
287
+ const push = (entry) => {
288
+ entries.push({ ...entry, sessionId, ...(inFirstTurn ? { firstTurn: true } : {}) });
289
+ return entries.length - 1;
290
+ };
291
+ /** Steps with a settled assistant/message (chunk-run tentatives drop). */
292
+ const seenSteps = new Set();
293
+ /** stepKey → entry indices of chunk-synthesized assistant texts. One step
294
+ * can yield SEVERAL runs when a non-chunk event interleaves the deltas
295
+ * (coalescing flushes at the boundary) — track them all, or earlier runs
296
+ * survive as ghost duplicates of the settled message. */
297
+ const tentatives = new Map();
298
+ /** callId → entry index of an unsettled tool card. */
299
+ const openTools = new Map();
300
+ /** Turns whose turn/end said aborted/interrupted. */
301
+ const abortedTurns = new Set();
302
+ for (const event of coalesceReplayEvents(events)) {
303
+ if (event.type === 'turn/start') {
304
+ turnsSeen += 1;
305
+ inFirstTurn = markFirstTurn && turnsSeen === 1;
306
+ continue;
307
+ }
308
+ switch (event.type) {
309
+ case 'user/message': {
310
+ const source = event.data.source;
311
+ if (source.kind === 'plugin' && source.plugin === 'compact') {
312
+ const summary = textOf(event.data.content);
313
+ push({
314
+ seq: event.seq,
315
+ kind: 'compact',
316
+ text: preview(summary || '(compaction)'),
317
+ searchText: `compact ${summary}`,
318
+ time: event.time,
319
+ });
320
+ break;
321
+ }
322
+ if (source.kind !== 'user')
323
+ break;
324
+ const text = firstTextOf(event.data.content);
325
+ if (text) {
326
+ push({
327
+ seq: event.seq,
328
+ kind: 'user',
329
+ text: preview(text),
330
+ searchText: `user ${text}`,
331
+ time: event.time,
332
+ });
333
+ }
334
+ break;
335
+ }
336
+ case 'assistant/message': {
337
+ const { turn, step } = event.data;
338
+ seenSteps.add(`${turn}:${step}`);
339
+ const text = textOf(event.data.message.content);
340
+ // pi hides assistant messages with only tool calls (no text).
341
+ if (text) {
342
+ push({
343
+ seq: event.seq,
344
+ kind: 'assistant',
345
+ text: preview(text),
346
+ searchText: `assistant ${text}`,
347
+ time: event.time,
348
+ });
349
+ }
350
+ break;
351
+ }
352
+ case 'assistant/chunk': {
353
+ const chunk = event.data.chunk;
354
+ if (chunk.type !== 'text-delta')
355
+ break;
356
+ const text = 'text' in chunk ? (chunk.text ?? '') : '';
357
+ if (!text.trim())
358
+ break;
359
+ const key = `${event.data.turn}:${event.data.step}`;
360
+ const index = push({
361
+ seq: event.seq,
362
+ kind: 'assistant',
363
+ text: preview(text),
364
+ searchText: `assistant ${text}`,
365
+ time: event.time,
366
+ });
367
+ const group = tentatives.get(key);
368
+ if (group === undefined)
369
+ tentatives.set(key, [index]);
370
+ else
371
+ group.push(index);
372
+ break;
373
+ }
374
+ case 'tool/call': {
375
+ if (event.data.name === 'ask_user_question')
376
+ break;
377
+ const args = preview(event.data.arguments, TOOL_ARGS_PREVIEW_LIMIT);
378
+ const index = push({
379
+ seq: event.seq,
380
+ kind: 'tool',
381
+ text: `[${event.data.name}] ${args}`,
382
+ searchText: `tool ${event.data.name} ${event.data.arguments}`,
383
+ time: event.time,
384
+ toolStatus: 'running',
385
+ });
386
+ openTools.set(event.data.callId, index);
387
+ break;
388
+ }
389
+ case 'tool/result': {
390
+ const index = openTools.get(event.data.message.source.callId);
391
+ if (index === undefined)
392
+ break;
393
+ const entry = entries[index];
394
+ entries[index] = {
395
+ ...entry,
396
+ toolStatus: event.data.error === undefined ? 'ok' : 'error',
397
+ };
398
+ openTools.delete(event.data.message.source.callId);
399
+ break;
400
+ }
401
+ case 'turn/end': {
402
+ const reason = event.data.reason;
403
+ if (reason.kind === 'aborted' || reason.kind === 'interrupted') {
404
+ abortedTurns.add(event.data.turn);
405
+ push({ seq: event.seq, kind: 'interrupt', text: 'interrupted', searchText: 'interrupt interrupted', time: event.time });
406
+ }
407
+ else if (reason.kind !== 'completed') {
408
+ const detail = reason.kind === 'error' ? reason.error.message : '';
409
+ push({
410
+ seq: event.seq,
411
+ kind: 'notice',
412
+ text: preview(`turn ${reason.kind}${detail ? ` · ${detail}` : ''}`),
413
+ searchText: `notice turn ${reason.kind} ${detail}`,
414
+ time: event.time,
415
+ });
416
+ }
417
+ // The turn CLOSES here — including turn 0. Entries logged BETWEEN
418
+ // turns (a compact checkpoint) are not turn-0 entries: their rewind
419
+ // boundary is the entry itself, perfectly valid, so the flag must
420
+ // not leak past this point and get them refused as "first message".
421
+ inFirstTurn = false;
422
+ break;
423
+ }
424
+ default:
425
+ break;
426
+ }
427
+ }
428
+ // Chunk-synthesized assistant entries lose to a settled assistant/message
429
+ // for the same step; survivors of an aborted turn get the marker.
430
+ const drop = new Set();
431
+ for (const [key, indices] of tentatives) {
432
+ if (seenSteps.has(key)) {
433
+ for (const index of indices)
434
+ drop.add(index);
435
+ continue;
436
+ }
437
+ const turn = Number(key.slice(0, key.indexOf(':')));
438
+ if (abortedTurns.has(turn)) {
439
+ for (const index of indices) {
440
+ entries[index] = { ...entries[index], label: 'aborted' };
441
+ }
442
+ }
443
+ }
444
+ return drop.size === 0 ? entries : entries.filter((_, index) => !drop.has(index));
445
+ }
446
+ /** A session's title event, if it ever got one (manual /rename or auto). */
447
+ function titleOf(events) {
448
+ let title;
449
+ for (const event of events) {
450
+ if (event.type === 'session/title') {
451
+ const data = event.data;
452
+ if (typeof data.title === 'string' && data.title !== '')
453
+ title = data.title;
454
+ }
455
+ }
456
+ return title;
457
+ }
458
+ /**
459
+ * Assemble the family tree. Roots are sessions with no parentSession (or a
460
+ * parent missing from the family — a deleted log; the orphan's self-contained
461
+ * prefix keeps its history whole). Fork chains attach at the last entry with
462
+ * seq <= seedLength-1, walking UP the ancestor chain when the direct parent
463
+ * no longer displays that entry (seed-prefix trimming — the fork point lives
464
+ * in the shared prefix an ancestor shows); the anchored session's own tail
465
+ * past the anchor stays the first (main) branch. Sessions with no own
466
+ * entries get one placeholder node so the fork structure stays visible.
467
+ */
468
+ export function buildSessionTree(sessions, liveSessionId, truncated = false) {
469
+ const byId = new Map(sessions.map(session => [session.id, session]));
470
+ // Corrupt headers can close a parent LOOP (a self-reference, or A↔B):
471
+ // attached under each other with no topmost member, every session in the
472
+ // loop is skipped by root selection and the whole loop — live chain
473
+ // included — vanishes from the panel. Cut the edge that closes each loop
474
+ // before anything hangs off parent links; walking in input order keeps the
475
+ // surviving root deterministic, and every structural read below goes
476
+ // through parentOf so the cut is observed consistently.
477
+ const cutEdges = new Set();
478
+ const parentOf = (session) => cutEdges.has(session.id) ? undefined : session.parentSession;
479
+ for (const session of sessions) {
480
+ const seen = new Set([session.id]);
481
+ let cursor = session;
482
+ for (;;) {
483
+ const parentId = parentOf(cursor);
484
+ if (parentId === undefined)
485
+ break;
486
+ const parent = byId.get(parentId);
487
+ if (parent === undefined)
488
+ break;
489
+ if (seen.has(parent.id)) {
490
+ cutEdges.add(cursor.id);
491
+ break;
492
+ }
493
+ seen.add(parent.id);
494
+ cursor = parent;
495
+ }
496
+ }
497
+ // Coverage bookkeeping: coveredThrough(S) = the highest K such that every
498
+ // seq in [0..K] is displayed by S's chain or an ancestor's. A fork's
499
+ // inherited seed prefix duplicates that range, so the fork's own chain
500
+ // trims it — but ONLY the covered part: an unreadable/unloaded or
501
+ // budget-truncated parent shows nothing (or less than the full prefix),
502
+ // and trimming the full seedLength would hide inherited history nobody
503
+ // displays (the child's log is self-contained). Transparent sessions
504
+ // (unreadable/unloaded/empty) forward their own ancestors' coverage, so a
505
+ // fork of a dead branch still dedups against the grandparent's chain.
506
+ const coveredThrough = new Map();
507
+ const covering = new Set();
508
+ const coverOf = (id) => {
509
+ const known = coveredThrough.get(id);
510
+ if (known !== undefined)
511
+ return known;
512
+ const session = byId.get(id);
513
+ if (session === undefined || covering.has(id))
514
+ return -1;
515
+ covering.add(id);
516
+ const parentId = parentOf(session);
517
+ const parentCover = parentId !== undefined && byId.has(parentId) ? coverOf(parentId) : -1;
518
+ covering.delete(id);
519
+ let cover;
520
+ if (session.unreadable === true || session.unloaded === true || session.events.length === 0) {
521
+ cover = parentCover;
522
+ }
523
+ else {
524
+ const firstSeq = session.events[0].seq;
525
+ const lastSeq = session.events[session.events.length - 1].seq;
526
+ // The events claim [0..lastSeq] only when they CONNECT to the parent's
527
+ // coverage (a full log starts at 0; a coverage-skipped read starts at
528
+ // the parent's cutoff). Otherwise the range is an island and coverage
529
+ // stays at the parent's — the events still display, they just cannot
530
+ // vouch for a contiguous prefix.
531
+ cover = firstSeq <= parentCover + 1 ? Math.max(parentCover, lastSeq) : parentCover;
532
+ }
533
+ coveredThrough.set(id, cover);
534
+ return cover;
535
+ };
536
+ // Per-session chains: extracted entries, with the inherited seed prefix
537
+ // trimmed by what the ancestor chain ACTUALLY covers (never past the seed
538
+ // prefix — events beyond it are this session's own).
539
+ const chains = new Map();
540
+ const metas = new Map();
541
+ const rewindFacts = new Map();
542
+ for (const session of sessions) {
543
+ metas.set(session.id, {
544
+ title: titleOf(session.events),
545
+ createdAt: session.createdAt,
546
+ live: session.live,
547
+ unreadable: session.unreadable === true,
548
+ unloaded: session.unloaded === true,
549
+ });
550
+ const parentId = parentOf(session);
551
+ const trimFrom = parentId !== undefined && byId.has(parentId)
552
+ ? Math.min(session.seedLength ?? 0, coverOf(parentId) + 1)
553
+ : 0;
554
+ const own = session.unreadable === true ? [] : extractEntries(session.id, session.events).filter(entry => entry.seq >= trimFrom);
555
+ // Rewind UX facts: own turn ranges (drop-turn confirm warning) and the
556
+ // branch-adopt target. A coverage/budget head cut can open the loaded
557
+ // events MID-turn — a turn/end then has no matching start; that turn
558
+ // gets no range and droppedTurnInfo stays silent for its entries rather
559
+ // than blaming the wrong span. Open turns end at the loaded tail.
560
+ {
561
+ const mutableTurns = [];
562
+ const lastSeq = session.events.length > 0 ? session.events[session.events.length - 1].seq : -1;
563
+ let lastTurnEnd = -1;
564
+ for (const event of session.events) {
565
+ if (event.type === 'turn/start') {
566
+ mutableTurns.push({ start: event.seq, end: lastSeq, entries: 0, closed: false });
567
+ }
568
+ else if (event.type === 'turn/end') {
569
+ lastTurnEnd = event.seq;
570
+ const open = mutableTurns[mutableTurns.length - 1];
571
+ if (open !== undefined && !open.closed) {
572
+ open.end = event.seq;
573
+ open.closed = true;
574
+ }
575
+ }
576
+ }
577
+ // Both lists are seq-ordered: two-pointer the own entries into turns.
578
+ let turnIndex = 0;
579
+ for (const entry of own) {
580
+ while (turnIndex < mutableTurns.length && entry.seq > mutableTurns[turnIndex].end)
581
+ turnIndex++;
582
+ const turn = mutableTurns[turnIndex];
583
+ if (turn !== undefined && entry.seq > turn.start)
584
+ turn.entries++;
585
+ }
586
+ rewindFacts.set(session.id, {
587
+ turns: mutableTurns,
588
+ ownEntries: own.length,
589
+ tailComplete: session.tailComplete === true,
590
+ ...(session.tailComplete === true && lastTurnEnd >= 0 ? { tipBoundary: lastTurnEnd } : {}),
591
+ });
592
+ }
593
+ if (own.length === 0) {
594
+ chains.set(session.id, [
595
+ { id: `${session.id}:head`, entry: null, sessionId: session.id, branchHead: true, children: [] },
596
+ ]);
597
+ continue;
598
+ }
599
+ chains.set(session.id, own.map((entry, index) => ({
600
+ id: `${session.id}:${entry.seq}`,
601
+ entry,
602
+ sessionId: session.id,
603
+ branchHead: index === 0,
604
+ children: [],
605
+ })));
606
+ }
607
+ // Linear links first: node[i].children = [node[i+1]].
608
+ for (const chain of chains.values()) {
609
+ for (let i = 0; i + 1 < chain.length; i++) {
610
+ chain[i].children.push(chain[i + 1]);
611
+ }
612
+ }
613
+ // Fork attachments, oldest fork first at each anchor. The parent's own
614
+ // continuation (linked above) stays child #0 — the main branch.
615
+ //
616
+ // Anchor search walks UP the ancestor chain when the direct parent does
617
+ // not display the fork point: seed-prefix trimming moved those entries to
618
+ // an ancestor's chain (seqs are shared across the self-contained logs), so
619
+ // a fork whose boundary predates the parent's own entries correctly becomes
620
+ // a sibling of the branch it diverged from. Only when NO ancestor displays
621
+ // an entry at or before the boundary does the fork hang off a synthesized
622
+ // session-start head. Heads are synthesized in a separate pass BEFORE any
623
+ // attachment — attaching through a chain head that is later unshifted
624
+ // would leave the new head (and its subtree) disconnected.
625
+ const childrenOf = new Map();
626
+ for (const session of sessions) {
627
+ const parentId = parentOf(session);
628
+ if (parentId === undefined || !byId.has(parentId))
629
+ continue;
630
+ const siblings = childrenOf.get(parentId);
631
+ if (siblings === undefined)
632
+ childrenOf.set(parentId, [session]);
633
+ else
634
+ siblings.push(session);
635
+ }
636
+ /** Last displayed entry with seq <= boundary, walking up from the parent. */
637
+ const findAnchor = (fork) => {
638
+ const boundary = (fork.seedLength ?? 0) - 1;
639
+ const visited = new Set();
640
+ let cursorId = fork.parentSession;
641
+ while (cursorId !== undefined && !visited.has(cursorId)) {
642
+ visited.add(cursorId);
643
+ const holder = byId.get(cursorId);
644
+ if (holder === undefined)
645
+ break;
646
+ const chain = chains.get(cursorId);
647
+ for (let i = chain.length - 1; i >= 0; i--) {
648
+ const entry = chain[i].entry;
649
+ if (entry !== null && entry.seq <= boundary)
650
+ return chain[i];
651
+ }
652
+ cursorId = parentOf(holder);
653
+ }
654
+ return undefined;
655
+ };
656
+ // Pass A: resolve anchors, marking parents whose fork predates everything
657
+ // displayable (those need a session-start head).
658
+ const headlessParents = new Set();
659
+ const resolved = new Map();
660
+ for (const [parentId, forks] of childrenOf) {
661
+ const list = forks.sort((a, b) => a.createdAt - b.createdAt)
662
+ .map(fork => {
663
+ const anchor = findAnchor(fork);
664
+ if (anchor === undefined)
665
+ headlessParents.add(parentId);
666
+ return { anchor, fork };
667
+ });
668
+ resolved.set(parentId, list);
669
+ }
670
+ // Pass B: synthesize the session-start heads (skipped when the parent's
671
+ // chain already opens with a placeholder).
672
+ for (const parentId of headlessParents) {
673
+ const parentChain = chains.get(parentId);
674
+ const first = parentChain[0];
675
+ if (first.entry === null)
676
+ continue;
677
+ const head = { id: `${parentId}:head`, entry: null, sessionId: parentId, branchHead: true, children: [] };
678
+ first.branchHead = false;
679
+ head.children.push(first);
680
+ parentChain.unshift(head);
681
+ }
682
+ // Pass C: attach — chain heads are final now, no capture can go stale.
683
+ for (const [parentId, list] of resolved) {
684
+ for (const { anchor, fork } of list) {
685
+ const forkHead = chains.get(fork.id)[0];
686
+ (anchor ?? chains.get(parentId)[0]).children.push(forkHead);
687
+ }
688
+ }
689
+ const roots = [];
690
+ for (const session of sessions) {
691
+ const parentId = parentOf(session);
692
+ if (parentId !== undefined && byId.has(parentId))
693
+ continue;
694
+ roots.push(chains.get(session.id)[0]);
695
+ }
696
+ // Active path: live session's whole chain, then each ancestor up to the
697
+ // fork boundary the next hop inherited. The boundary only NARROWS walking
698
+ // up: a descendant that forked inside its parent's INHERITED prefix caps
699
+ // the path at its own fork point — the parent's own entries past it (and
700
+ // the grandparent's entries past that) belong to dead branches, not the
701
+ // path to the live tip. Cycle-guarded (corrupt headers).
702
+ const activePath = new Set();
703
+ let activeLeafId = null;
704
+ const liveChain = chains.get(liveSessionId);
705
+ if (liveChain !== undefined) {
706
+ activeLeafId = liveChain.at(-1).id;
707
+ for (const node of liveChain)
708
+ activePath.add(node.id);
709
+ const visited = new Set([liveSessionId]);
710
+ let current = byId.get(liveSessionId);
711
+ let boundary = Number.POSITIVE_INFINITY;
712
+ while (current !== undefined) {
713
+ const parentId = parentOf(current);
714
+ if (parentId === undefined || !byId.has(parentId))
715
+ break;
716
+ if (visited.has(parentId))
717
+ break;
718
+ visited.add(parentId);
719
+ boundary = Math.min(boundary, (current.seedLength ?? 0) - 1);
720
+ for (const node of chains.get(parentId)) {
721
+ if (node.entry !== null && node.entry.seq <= boundary)
722
+ activePath.add(node.id);
723
+ }
724
+ current = byId.get(parentId);
725
+ }
726
+ }
727
+ return { roots, activePath, activeLeafId, sessions: metas, rewindFacts, truncated, sessionCount: sessions.length };
728
+ }
729
+ /**
730
+ * Flatten the tree into render rows, pi-style: the subtree containing the
731
+ * active leaf sorts first at every branch point; single-child chains keep
732
+ * their indent, branch points indent +1 (plus the first generation after a
733
+ * branch); gutters record where ancestor │ lines continue.
734
+ */
735
+ export function flattenTree(roots, activeLeafId) {
736
+ const multipleRoots = roots.length > 1;
737
+ // Post-order: which subtrees contain the active leaf (sorted first below).
738
+ const containsActive = new Map();
739
+ {
740
+ const all = [];
741
+ const stack = [...roots];
742
+ while (stack.length > 0) {
743
+ const node = stack.pop();
744
+ all.push(node);
745
+ for (let i = node.children.length - 1; i >= 0; i--)
746
+ stack.push(node.children[i]);
747
+ }
748
+ for (let i = all.length - 1; i >= 0; i--) {
749
+ const node = all[i];
750
+ let has = node.id === activeLeafId;
751
+ for (const child of node.children) {
752
+ if (containsActive.get(child) === true)
753
+ has = true;
754
+ }
755
+ containsActive.set(node, has);
756
+ }
757
+ }
758
+ const activeFirst = (nodes) => {
759
+ const prioritized = [];
760
+ const rest = [];
761
+ for (const node of nodes) {
762
+ if (containsActive.get(node) === true)
763
+ prioritized.push(node);
764
+ else
765
+ rest.push(node);
766
+ }
767
+ return [...prioritized, ...rest];
768
+ };
769
+ const stack = [];
770
+ const orderedRoots = activeFirst(roots);
771
+ for (let i = orderedRoots.length - 1; i >= 0; i--) {
772
+ stack.push([
773
+ orderedRoots[i],
774
+ multipleRoots ? 1 : 0,
775
+ multipleRoots,
776
+ multipleRoots,
777
+ i === orderedRoots.length - 1,
778
+ [],
779
+ multipleRoots,
780
+ null,
781
+ ]);
782
+ }
783
+ const flat = [];
784
+ while (stack.length > 0) {
785
+ const [node, indent, justBranched, showConnector, isLast, gutters, isVirtualRootChild, parentId] = stack.pop();
786
+ flat.push({
787
+ node,
788
+ parentId,
789
+ indent,
790
+ showConnector,
791
+ isLast,
792
+ gutters,
793
+ isVirtualRootChild,
794
+ multipleRoots,
795
+ });
796
+ const children = activeFirst(node.children);
797
+ const multipleChildren = children.length > 1;
798
+ let childIndent;
799
+ if (multipleChildren)
800
+ childIndent = indent + 1;
801
+ else if (justBranched && indent > 0)
802
+ childIndent = indent + 1;
803
+ else
804
+ childIndent = indent;
805
+ const connectorDisplayed = showConnector && !isVirtualRootChild;
806
+ const currentDisplayIndent = multipleRoots ? Math.max(0, indent - 1) : indent;
807
+ const connectorPosition = Math.max(0, currentDisplayIndent - 1);
808
+ const childGutters = connectorDisplayed
809
+ ? [...gutters, { position: connectorPosition, show: !isLast }]
810
+ : gutters;
811
+ for (let i = children.length - 1; i >= 0; i--) {
812
+ stack.push([
813
+ children[i],
814
+ childIndent,
815
+ multipleChildren,
816
+ multipleChildren,
817
+ i === children.length - 1,
818
+ childGutters,
819
+ false,
820
+ node.id,
821
+ ]);
822
+ }
823
+ }
824
+ return flat;
825
+ }
826
+ /**
827
+ * Filter + search the flattened tree, then recompute the tree drawing over
828
+ * the visible rows (each visible node re-hangs under its nearest visible
829
+ * ancestor, so hidden intermediates don't drift the indent). The active leaf
830
+ * always survives filtering (pi keeps the current position visible).
831
+ */
832
+ export function filterTree(flat, activeLeafId, filter, query) {
833
+ const tokens = query.toLowerCase().split(/\s+/).filter(Boolean);
834
+ const visible = flat.filter(flatNode => {
835
+ const { node } = flatNode;
836
+ if (node.id === activeLeafId)
837
+ return true;
838
+ if (node.entry === null) {
839
+ // Placeholders: structure-only rows. `all` shows them; any search
840
+ // query hides them (no text to match) so they don't become stray
841
+ // roots; other modes drop them unless a visible descendant re-hangs.
842
+ return filter === 'all' && tokens.length === 0;
843
+ }
844
+ switch (filter) {
845
+ case 'user-only':
846
+ if (node.entry.kind !== 'user')
847
+ return false;
848
+ break;
849
+ case 'no-tools':
850
+ if (node.entry.kind === 'tool' || node.entry.kind === 'notice')
851
+ return false;
852
+ break;
853
+ case 'all':
854
+ break;
855
+ default:
856
+ if (node.entry.kind === 'notice')
857
+ return false;
858
+ break;
859
+ }
860
+ if (tokens.length > 0) {
861
+ const haystack = node.entry.searchText.toLowerCase();
862
+ return tokens.every(token => haystack.includes(token));
863
+ }
864
+ return true;
865
+ });
866
+ if (visible.length === 0)
867
+ return visible;
868
+ // Re-hang visible nodes under their nearest visible ancestor.
869
+ const visibleIds = new Set(visible.map(flatNode => flatNode.node.id));
870
+ const byNodeId = new Map(flat.map(flatNode => [flatNode.node.id, flatNode]));
871
+ const visibleParent = new Map();
872
+ const visibleChildren = new Map();
873
+ visibleChildren.set(null, []);
874
+ const nearestVisible = (id) => {
875
+ let current = byNodeId.get(id)?.parentId ?? null;
876
+ while (current !== null) {
877
+ if (visibleIds.has(current))
878
+ return current;
879
+ current = byNodeId.get(current)?.parentId ?? null;
880
+ }
881
+ return null;
882
+ };
883
+ for (const flatNode of visible) {
884
+ const ancestor = nearestVisible(flatNode.node.id);
885
+ visibleParent.set(flatNode.node.id, ancestor);
886
+ const siblings = visibleChildren.get(ancestor);
887
+ if (siblings === undefined)
888
+ visibleChildren.set(ancestor, [flatNode.node.id]);
889
+ else
890
+ siblings.push(flatNode.node.id);
891
+ }
892
+ // Recompute indent/connectors/gutters over the visible tree, same rules as
893
+ // flattenTree (roots = children of the virtual null parent).
894
+ const visibleRoots = visibleChildren.get(null);
895
+ const multipleRoots = visibleRoots.length > 1;
896
+ const visibleById = new Map(visible.map(flatNode => [flatNode.node.id, flatNode]));
897
+ const stack = [];
898
+ for (let i = visibleRoots.length - 1; i >= 0; i--) {
899
+ stack.push([visibleRoots[i], multipleRoots ? 1 : 0, multipleRoots, multipleRoots, i === visibleRoots.length - 1, [], multipleRoots]);
900
+ }
901
+ const out = [];
902
+ while (stack.length > 0) {
903
+ const [id, indent, justBranched, showConnector, isLast, gutters, isVirtualRootChild] = stack.pop();
904
+ const flatNode = visibleById.get(id);
905
+ flatNode.indent = indent;
906
+ flatNode.showConnector = showConnector;
907
+ flatNode.isLast = isLast;
908
+ flatNode.gutters = gutters;
909
+ flatNode.isVirtualRootChild = isVirtualRootChild;
910
+ flatNode.multipleRoots = multipleRoots;
911
+ out.push(flatNode);
912
+ const children = visibleChildren.get(id) ?? [];
913
+ const multipleChildren = children.length > 1;
914
+ let childIndent;
915
+ if (multipleChildren)
916
+ childIndent = indent + 1;
917
+ else if (justBranched && indent > 0)
918
+ childIndent = indent + 1;
919
+ else
920
+ childIndent = indent;
921
+ const connectorDisplayed = showConnector && !isVirtualRootChild;
922
+ const currentDisplayIndent = multipleRoots ? Math.max(0, indent - 1) : indent;
923
+ const connectorPosition = Math.max(0, currentDisplayIndent - 1);
924
+ const childGutters = connectorDisplayed
925
+ ? [...gutters, { position: connectorPosition, show: !isLast }]
926
+ : gutters;
927
+ for (let i = children.length - 1; i >= 0; i--) {
928
+ stack.push([children[i], childIndent, multipleChildren, multipleChildren, i === children.length - 1, childGutters, false]);
929
+ }
930
+ }
931
+ return out;
932
+ }
933
+ /**
934
+ * Index of `targetId` in `visible`, walking the FULL list's parent chain when
935
+ * the exact node was filtered out; falls back to the last visible row
936
+ * (pi's findNearestVisibleIndex).
937
+ */
938
+ export function nearestVisibleIndex(visible, full, targetId) {
939
+ if (visible.length === 0)
940
+ return 0;
941
+ const visibleIndexById = new Map(visible.map((flatNode, index) => [flatNode.node.id, index]));
942
+ const fullById = new Map(full.map(flatNode => [flatNode.node.id, flatNode]));
943
+ let current = targetId;
944
+ while (current !== null) {
945
+ const index = visibleIndexById.get(current);
946
+ if (index !== undefined)
947
+ return index;
948
+ const parent = fullById.get(current)?.parentId ?? null;
949
+ if (parent === current)
950
+ break; // corrupt self-parent guard
951
+ current = parent;
952
+ }
953
+ return visible.length - 1;
954
+ }