aicodeman 1.35.0 → 1.41.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (316) hide show
  1. package/README.md +33 -7
  2. package/dist/attachment-registry.d.ts +6 -0
  3. package/dist/attachment-registry.d.ts.map +1 -1
  4. package/dist/attachment-registry.js +9 -3
  5. package/dist/attachment-registry.js.map +1 -1
  6. package/dist/cli-agent.d.ts +225 -0
  7. package/dist/cli-agent.d.ts.map +1 -0
  8. package/dist/cli-agent.js +767 -0
  9. package/dist/cli-agent.js.map +1 -0
  10. package/dist/cli.d.ts.map +1 -1
  11. package/dist/cli.js +8 -32
  12. package/dist/cli.js.map +1 -1
  13. package/dist/codeman-credentials.d.ts +27 -0
  14. package/dist/codeman-credentials.d.ts.map +1 -0
  15. package/dist/codeman-credentials.js +66 -0
  16. package/dist/codeman-credentials.js.map +1 -0
  17. package/dist/config/cli-registry/patterns.d.ts +6 -0
  18. package/dist/config/cli-registry/patterns.d.ts.map +1 -1
  19. package/dist/config/cli-registry/patterns.js +13 -0
  20. package/dist/config/cli-registry/patterns.js.map +1 -1
  21. package/dist/config/cli-registry/schema.d.ts +15 -0
  22. package/dist/config/cli-registry/schema.d.ts.map +1 -1
  23. package/dist/config/cli-registry/schema.js +63 -2
  24. package/dist/config/cli-registry/schema.js.map +1 -1
  25. package/dist/config/cli-registry/stock.d.ts.map +1 -1
  26. package/dist/config/cli-registry/stock.js +180 -7
  27. package/dist/config/cli-registry/stock.js.map +1 -1
  28. package/dist/config/cli-registry/types.d.ts +105 -7
  29. package/dist/config/cli-registry/types.d.ts.map +1 -1
  30. package/dist/deepseek-route-config.d.ts +110 -0
  31. package/dist/deepseek-route-config.d.ts.map +1 -0
  32. package/dist/deepseek-route-config.js +477 -0
  33. package/dist/deepseek-route-config.js.map +1 -0
  34. package/dist/git-clone.d.ts.map +1 -1
  35. package/dist/git-clone.js +4 -0
  36. package/dist/git-clone.js.map +1 -1
  37. package/dist/git-workspace-status.d.ts +31 -5
  38. package/dist/git-workspace-status.d.ts.map +1 -1
  39. package/dist/git-workspace-status.js +49 -29
  40. package/dist/git-workspace-status.js.map +1 -1
  41. package/dist/image-watcher.d.ts.map +1 -1
  42. package/dist/image-watcher.js +6 -2
  43. package/dist/image-watcher.js.map +1 -1
  44. package/dist/mcp-sync-targets.d.ts +34 -0
  45. package/dist/mcp-sync-targets.d.ts.map +1 -0
  46. package/dist/mcp-sync-targets.js +55 -0
  47. package/dist/mcp-sync-targets.js.map +1 -0
  48. package/dist/mcp-sync.d.ts +2 -1
  49. package/dist/mcp-sync.d.ts.map +1 -1
  50. package/dist/mcp-sync.js +62 -1
  51. package/dist/mcp-sync.js.map +1 -1
  52. package/dist/model-config-resolvers.d.ts +29 -0
  53. package/dist/model-config-resolvers.d.ts.map +1 -0
  54. package/dist/model-config-resolvers.js +34 -0
  55. package/dist/model-config-resolvers.js.map +1 -0
  56. package/dist/session-display-model.d.ts +110 -0
  57. package/dist/session-display-model.d.ts.map +1 -0
  58. package/dist/session-display-model.js +154 -0
  59. package/dist/session-display-model.js.map +1 -0
  60. package/dist/session.d.ts +145 -8
  61. package/dist/session.d.ts.map +1 -1
  62. package/dist/session.js +290 -39
  63. package/dist/session.js.map +1 -1
  64. package/dist/subagent-watcher.d.ts.map +1 -1
  65. package/dist/subagent-watcher.js +29 -6
  66. package/dist/subagent-watcher.js.map +1 -1
  67. package/dist/templates/case-template.md +2 -0
  68. package/dist/tmux-manager.d.ts +5 -0
  69. package/dist/tmux-manager.d.ts.map +1 -1
  70. package/dist/tmux-manager.js +47 -22
  71. package/dist/tmux-manager.js.map +1 -1
  72. package/dist/tui/tui-client.d.ts +3 -17
  73. package/dist/tui/tui-client.d.ts.map +1 -1
  74. package/dist/tui/tui-client.js +3 -44
  75. package/dist/tui/tui-client.js.map +1 -1
  76. package/dist/types/session.d.ts +28 -1
  77. package/dist/types/session.d.ts.map +1 -1
  78. package/dist/types/session.js.map +1 -1
  79. package/dist/types/tools.d.ts +1 -1
  80. package/dist/types/tools.d.ts.map +1 -1
  81. package/dist/utils/deepseek-cli-resolver.d.ts +8 -0
  82. package/dist/utils/deepseek-cli-resolver.d.ts.map +1 -1
  83. package/dist/utils/deepseek-cli-resolver.js +18 -2
  84. package/dist/utils/deepseek-cli-resolver.js.map +1 -1
  85. package/dist/utils/index.d.ts +1 -0
  86. package/dist/utils/index.d.ts.map +1 -1
  87. package/dist/utils/index.js +1 -0
  88. package/dist/utils/index.js.map +1 -1
  89. package/dist/utils/process-exit-wait.d.ts +40 -0
  90. package/dist/utils/process-exit-wait.d.ts.map +1 -0
  91. package/dist/utils/process-exit-wait.js +68 -0
  92. package/dist/utils/process-exit-wait.js.map +1 -0
  93. package/dist/web/launch-defaults.d.ts +22 -0
  94. package/dist/web/launch-defaults.d.ts.map +1 -0
  95. package/dist/web/launch-defaults.js +46 -0
  96. package/dist/web/launch-defaults.js.map +1 -0
  97. package/dist/web/paste-image-gc.d.ts +46 -6
  98. package/dist/web/paste-image-gc.d.ts.map +1 -1
  99. package/dist/web/paste-image-gc.js +106 -32
  100. package/dist/web/paste-image-gc.js.map +1 -1
  101. package/dist/web/public/admin-ui.js.gz +0 -0
  102. package/dist/web/public/api-client.c9b1cddc.js.gz +0 -0
  103. package/dist/web/public/app.6f8552e1.js +43 -0
  104. package/dist/web/public/app.6f8552e1.js.br +0 -0
  105. package/dist/web/public/app.6f8552e1.js.gz +0 -0
  106. package/dist/web/public/approvals-ui.js.gz +0 -0
  107. package/dist/web/public/{constants.d95fade9.js → constants.bc9c1f63.js} +1276 -127
  108. package/dist/web/public/constants.bc9c1f63.js.br +0 -0
  109. package/dist/web/public/constants.bc9c1f63.js.gz +0 -0
  110. package/dist/web/public/cron-ui.js.gz +0 -0
  111. package/dist/web/public/entrance-animations.js +491 -43
  112. package/dist/web/public/entrance-animations.js.br +0 -0
  113. package/dist/web/public/entrance-animations.js.gz +0 -0
  114. package/dist/web/public/git-status-ui.js +55 -9
  115. package/dist/web/public/git-status-ui.js.br +0 -0
  116. package/dist/web/public/git-status-ui.js.gz +0 -0
  117. package/dist/web/public/home-sessions.js +13 -9
  118. package/dist/web/public/home-sessions.js.br +0 -0
  119. package/dist/web/public/home-sessions.js.gz +0 -0
  120. package/dist/web/public/host-wake-ui.js.gz +0 -0
  121. package/dist/web/public/i18n.a9840ea2.js +2 -0
  122. package/dist/web/public/i18n.a9840ea2.js.br +0 -0
  123. package/dist/web/public/i18n.a9840ea2.js.gz +0 -0
  124. package/dist/web/public/{image-input.a216cf9e.js → image-input.e00471c6.js} +21 -9
  125. package/dist/web/public/image-input.e00471c6.js.br +0 -0
  126. package/dist/web/public/image-input.e00471c6.js.gz +0 -0
  127. package/dist/web/public/index.html +250 -59
  128. package/dist/web/public/index.html.br +0 -0
  129. package/dist/web/public/index.html.gz +0 -0
  130. package/dist/web/public/input-cjk.8bc46081.js.gz +0 -0
  131. package/dist/web/public/keyboard-accessory.c1e7c7b7.js.gz +0 -0
  132. package/dist/web/public/{mobile-handlers.23a85db1.js → mobile-handlers.0ef9c311.js} +20 -0
  133. package/dist/web/public/mobile-handlers.0ef9c311.js.br +0 -0
  134. package/dist/web/public/mobile-handlers.0ef9c311.js.gz +0 -0
  135. package/dist/web/public/mobile-ime-preview.2dbaf262.js.gz +0 -0
  136. package/dist/web/public/mobile-overview.js.gz +0 -0
  137. package/dist/web/public/mobile.111b187d.css +1 -0
  138. package/dist/web/public/mobile.111b187d.css.br +0 -0
  139. package/dist/web/public/mobile.111b187d.css.gz +0 -0
  140. package/dist/web/public/{notification-manager.50bb87d8.js → notification-manager.feb82c04.js} +19 -2
  141. package/dist/web/public/notification-manager.feb82c04.js.br +0 -0
  142. package/dist/web/public/notification-manager.feb82c04.js.gz +0 -0
  143. package/dist/web/public/orchestrator-panel.js.gz +0 -0
  144. package/dist/web/public/{panels-ui.6a5898d9.js → panels-ui.71846fcb.js} +4 -4
  145. package/dist/web/public/panels-ui.71846fcb.js.br +0 -0
  146. package/dist/web/public/panels-ui.71846fcb.js.gz +0 -0
  147. package/dist/web/public/ralph-panel.b350e2ef.js.gz +0 -0
  148. package/dist/web/public/ralph-wizard.b6c0dd19.js.gz +0 -0
  149. package/dist/web/public/readmymind-ui.js.gz +0 -0
  150. package/dist/web/public/reboot-restore-ui.js.gz +0 -0
  151. package/dist/web/public/respawn-ui.ff0dae4c.js.gz +0 -0
  152. package/dist/web/public/sanitize-html.e7147e76.js.gz +0 -0
  153. package/dist/web/public/session-lineage.js +229 -86
  154. package/dist/web/public/session-lineage.js.br +0 -0
  155. package/dist/web/public/session-lineage.js.gz +0 -0
  156. package/dist/web/public/session-ui.f731602c.js +100 -0
  157. package/dist/web/public/session-ui.f731602c.js.br +0 -0
  158. package/dist/web/public/session-ui.f731602c.js.gz +0 -0
  159. package/dist/web/public/settings-ui.4e33390f.js +97 -0
  160. package/dist/web/public/settings-ui.4e33390f.js.br +0 -0
  161. package/dist/web/public/settings-ui.4e33390f.js.gz +0 -0
  162. package/dist/web/public/spreadsheet-preview-worker.js +290 -0
  163. package/dist/web/public/spreadsheet-preview-worker.js.br +0 -0
  164. package/dist/web/public/spreadsheet-preview-worker.js.gz +0 -0
  165. package/dist/web/public/spreadsheet-preview.af82fa2f.js +555 -0
  166. package/dist/web/public/spreadsheet-preview.af82fa2f.js.br +0 -0
  167. package/dist/web/public/spreadsheet-preview.af82fa2f.js.gz +0 -0
  168. package/dist/web/public/spreadsheet-xlsx-core.js +1006 -0
  169. package/dist/web/public/spreadsheet-xlsx-core.js.br +0 -0
  170. package/dist/web/public/spreadsheet-xlsx-core.js.gz +0 -0
  171. package/dist/web/public/styles.81a72906.css +1 -0
  172. package/dist/web/public/styles.81a72906.css.br +0 -0
  173. package/dist/web/public/styles.81a72906.css.gz +0 -0
  174. package/dist/web/public/{subagent-windows.e6ca799f.js → subagent-windows.07277a58.js} +7 -5
  175. package/dist/web/public/subagent-windows.07277a58.js.br +0 -0
  176. package/dist/web/public/subagent-windows.07277a58.js.gz +0 -0
  177. package/dist/web/public/sw.js +2 -2
  178. package/dist/web/public/sw.js.br +0 -0
  179. package/dist/web/public/sw.js.gz +0 -0
  180. package/dist/web/public/tab-layout-browser.065fcba4.js.gz +0 -0
  181. package/dist/web/public/{tab-rail-resize.94cd59f0.js → tab-rail-resize.a5867793.js} +1 -1
  182. package/dist/web/public/tab-rail-resize.a5867793.js.br +0 -0
  183. package/dist/web/public/{tab-rail-resize.94cd59f0.js.gz → tab-rail-resize.a5867793.js.gz} +0 -0
  184. package/dist/web/public/terminal-keycode229-recovery.af09f99a.js +1 -0
  185. package/dist/web/public/terminal-keycode229-recovery.af09f99a.js.br +0 -0
  186. package/dist/web/public/terminal-keycode229-recovery.af09f99a.js.gz +0 -0
  187. package/dist/web/public/terminal-split.js +146 -649
  188. package/dist/web/public/terminal-split.js.br +0 -0
  189. package/dist/web/public/terminal-split.js.gz +0 -0
  190. package/dist/web/public/terminal-tile.js +1688 -0
  191. package/dist/web/public/terminal-tile.js.br +0 -0
  192. package/dist/web/public/terminal-tile.js.gz +0 -0
  193. package/dist/web/public/terminal-ui.b7267005.js +2 -0
  194. package/dist/web/public/terminal-ui.b7267005.js.br +0 -0
  195. package/dist/web/public/terminal-ui.b7267005.js.gz +0 -0
  196. package/dist/web/public/tile-grid.js +2895 -0
  197. package/dist/web/public/tile-grid.js.br +0 -0
  198. package/dist/web/public/tile-grid.js.gz +0 -0
  199. package/dist/web/public/ultracode-panel.js.gz +0 -0
  200. package/dist/web/public/ultracode-windows.js +9 -6
  201. package/dist/web/public/ultracode-windows.js.br +0 -0
  202. package/dist/web/public/ultracode-windows.js.gz +0 -0
  203. package/dist/web/public/vendor/dompurify.min.js.gz +0 -0
  204. package/dist/web/public/vendor/exceljs.min.js +44 -0
  205. package/dist/web/public/vendor/exceljs.min.js.br +0 -0
  206. package/dist/web/public/vendor/exceljs.min.js.gz +0 -0
  207. package/dist/web/public/vendor/fflate.min.js +1 -0
  208. package/dist/web/public/vendor/fflate.min.js.br +0 -0
  209. package/dist/web/public/vendor/fflate.min.js.gz +0 -0
  210. package/dist/web/public/vendor/marked.min.js.gz +0 -0
  211. package/dist/web/public/vendor/xterm-addon-fit.min.js.gz +0 -0
  212. package/dist/web/public/vendor/xterm-addon-serialize.min.js.gz +0 -0
  213. package/dist/web/public/vendor/xterm-addon-unicode11.min.js.gz +0 -0
  214. package/dist/web/public/vendor/xterm-addon-webgl.min.js.gz +0 -0
  215. package/dist/web/public/vendor/xterm-predictive-echo.ae5e3d5d.js.gz +0 -0
  216. package/dist/web/public/vendor/xterm-zerolag-input.0b69623c.js +4 -0
  217. package/dist/web/public/vendor/xterm-zerolag-input.0b69623c.js.br +0 -0
  218. package/dist/web/public/vendor/xterm-zerolag-input.0b69623c.js.gz +0 -0
  219. package/dist/web/public/vendor/xterm.css.gz +0 -0
  220. package/dist/web/public/vendor/xterm.min.js.gz +0 -0
  221. package/dist/web/public/{voice-input.c4b51eb6.js → voice-input.2342c37b.js} +64 -10
  222. package/dist/web/public/voice-input.2342c37b.js.br +0 -0
  223. package/dist/web/public/voice-input.2342c37b.js.gz +0 -0
  224. package/dist/web/public/voice-pcm-worklet.js.gz +0 -0
  225. package/dist/web/public/webview-tabs.js +9 -1
  226. package/dist/web/public/webview-tabs.js.br +0 -0
  227. package/dist/web/public/webview-tabs.js.gz +0 -0
  228. package/dist/web/routes/cli-registry-routes.d.ts +23 -4
  229. package/dist/web/routes/cli-registry-routes.d.ts.map +1 -1
  230. package/dist/web/routes/cli-registry-routes.js +95 -8
  231. package/dist/web/routes/cli-registry-routes.js.map +1 -1
  232. package/dist/web/routes/file-routes.d.ts +7 -0
  233. package/dist/web/routes/file-routes.d.ts.map +1 -1
  234. package/dist/web/routes/file-routes.js +34 -4
  235. package/dist/web/routes/file-routes.js.map +1 -1
  236. package/dist/web/routes/git-status-routes.d.ts.map +1 -1
  237. package/dist/web/routes/git-status-routes.js +33 -6
  238. package/dist/web/routes/git-status-routes.js.map +1 -1
  239. package/dist/web/routes/mcp-sync-routes.d.ts +7 -2
  240. package/dist/web/routes/mcp-sync-routes.d.ts.map +1 -1
  241. package/dist/web/routes/mcp-sync-routes.js +10 -3
  242. package/dist/web/routes/mcp-sync-routes.js.map +1 -1
  243. package/dist/web/routes/reboot-restore-routes.js +1 -1
  244. package/dist/web/routes/reboot-restore-routes.js.map +1 -1
  245. package/dist/web/routes/session-routes.d.ts +18 -0
  246. package/dist/web/routes/session-routes.d.ts.map +1 -1
  247. package/dist/web/routes/session-routes.js +154 -69
  248. package/dist/web/routes/session-routes.js.map +1 -1
  249. package/dist/web/routes/status-telemetry-routes.d.ts +3 -0
  250. package/dist/web/routes/status-telemetry-routes.d.ts.map +1 -1
  251. package/dist/web/routes/status-telemetry-routes.js +12 -0
  252. package/dist/web/routes/status-telemetry-routes.js.map +1 -1
  253. package/dist/web/routes/system-routes.d.ts.map +1 -1
  254. package/dist/web/routes/system-routes.js +13 -0
  255. package/dist/web/routes/system-routes.js.map +1 -1
  256. package/dist/web/schemas.d.ts +30 -0
  257. package/dist/web/schemas.d.ts.map +1 -1
  258. package/dist/web/schemas.js +43 -0
  259. package/dist/web/schemas.js.map +1 -1
  260. package/dist/web/server.d.ts +6 -1
  261. package/dist/web/server.d.ts.map +1 -1
  262. package/dist/web/server.js +47 -21
  263. package/dist/web/server.js.map +1 -1
  264. package/dist/web/session-listener-wiring.d.ts +1 -0
  265. package/dist/web/session-listener-wiring.d.ts.map +1 -1
  266. package/dist/web/session-listener-wiring.js +11 -0
  267. package/dist/web/session-listener-wiring.js.map +1 -1
  268. package/package.json +6 -4
  269. package/scripts/postinstall.js +16 -0
  270. package/skills/codeman/SKILL.md +6 -0
  271. package/README.zh-CN.md +0 -1139
  272. package/dist/web/public/app.b9516c7b.js +0 -43
  273. package/dist/web/public/app.b9516c7b.js.br +0 -0
  274. package/dist/web/public/app.b9516c7b.js.gz +0 -0
  275. package/dist/web/public/constants.d95fade9.js.br +0 -0
  276. package/dist/web/public/constants.d95fade9.js.gz +0 -0
  277. package/dist/web/public/i18n.52f5b7cc.js +0 -1
  278. package/dist/web/public/i18n.52f5b7cc.js.br +0 -0
  279. package/dist/web/public/i18n.52f5b7cc.js.gz +0 -0
  280. package/dist/web/public/image-input.a216cf9e.js.br +0 -0
  281. package/dist/web/public/image-input.a216cf9e.js.gz +0 -0
  282. package/dist/web/public/mobile-handlers.23a85db1.js.br +0 -0
  283. package/dist/web/public/mobile-handlers.23a85db1.js.gz +0 -0
  284. package/dist/web/public/mobile.1aa4952d.css +0 -1
  285. package/dist/web/public/mobile.1aa4952d.css.br +0 -0
  286. package/dist/web/public/mobile.1aa4952d.css.gz +0 -0
  287. package/dist/web/public/notification-manager.50bb87d8.js.br +0 -0
  288. package/dist/web/public/notification-manager.50bb87d8.js.gz +0 -0
  289. package/dist/web/public/panels-ui.6a5898d9.js.br +0 -0
  290. package/dist/web/public/panels-ui.6a5898d9.js.gz +0 -0
  291. package/dist/web/public/session-ui.6bddf916.js +0 -89
  292. package/dist/web/public/session-ui.6bddf916.js.br +0 -0
  293. package/dist/web/public/session-ui.6bddf916.js.gz +0 -0
  294. package/dist/web/public/settings-ui.6e798b7b.js +0 -97
  295. package/dist/web/public/settings-ui.6e798b7b.js.br +0 -0
  296. package/dist/web/public/settings-ui.6e798b7b.js.gz +0 -0
  297. package/dist/web/public/styles.2d175c60.css +0 -1
  298. package/dist/web/public/styles.2d175c60.css.br +0 -0
  299. package/dist/web/public/styles.2d175c60.css.gz +0 -0
  300. package/dist/web/public/subagent-windows.e6ca799f.js.br +0 -0
  301. package/dist/web/public/subagent-windows.e6ca799f.js.gz +0 -0
  302. package/dist/web/public/tab-rail-resize.94cd59f0.js.br +0 -0
  303. package/dist/web/public/terminal-keycode229-recovery.fb91b25b.js +0 -1
  304. package/dist/web/public/terminal-keycode229-recovery.fb91b25b.js.br +0 -0
  305. package/dist/web/public/terminal-keycode229-recovery.fb91b25b.js.gz +0 -0
  306. package/dist/web/public/terminal-ui.a4535311.js +0 -2
  307. package/dist/web/public/terminal-ui.a4535311.js.br +0 -0
  308. package/dist/web/public/terminal-ui.a4535311.js.gz +0 -0
  309. package/dist/web/public/upload.html +0 -155
  310. package/dist/web/public/upload.html.br +0 -0
  311. package/dist/web/public/upload.html.gz +0 -0
  312. package/dist/web/public/vendor/xterm-zerolag-input.bbb5f84c.js +0 -4
  313. package/dist/web/public/vendor/xterm-zerolag-input.bbb5f84c.js.br +0 -0
  314. package/dist/web/public/vendor/xterm-zerolag-input.bbb5f84c.js.gz +0 -0
  315. package/dist/web/public/voice-input.c4b51eb6.js.br +0 -0
  316. package/dist/web/public/voice-input.c4b51eb6.js.gz +0 -0
@@ -1,29 +1,41 @@
1
1
  /**
2
- * @fileoverview Session lineage lines — the arcs joining a tab to the tabs it spawned.
2
+ * @fileoverview Session lineage lines: the tree joining a tab to the tabs it spawned.
3
3
  *
4
4
  * A session that starts another session (the `codeman` agent skill spawning a worker,
5
5
  * which passes its own `$CODEMAN_SESSION_ID`) gets `parentSessionId` stamped on its
6
- * state server-side. This module turns that field into the same kind of glowing
7
- * connection line the subagent windows use, but tab → tab, so the strip shows at a
8
- * glance which tab spawned which.
6
+ * state server-side. This module turns that field into a quiet orthogonal tree, one per
7
+ * spawning tab, routed through the gaps between tab rows so it never crosses a label or
8
+ * the terminal (geometry: `CodemanLineage.computeTree` in constants.js).
9
+ *
10
+ * EVERY FAMILY IS ALWAYS DRAWN, AND THE SELECTED TAB'S IS EMPHASIZED: the family the
11
+ * active tab spawned, and the family it belongs to as a child, get the
12
+ * `lineage-family--focus` group (thicker, full opacity, drawn last so nothing covers
13
+ * it). Drawing ONLY the selected family was tried first and rejected by the owner
14
+ * (2026-10-07: "I wanna see all the connections always"). Selection still has to
15
+ * redraw to move the emphasis, which `_updateActiveTabImmediate()` does whenever any
16
+ * lineage exists (`_lineageTotalEdges`).
9
17
  *
10
18
  * It is an ADDITIONAL LAYER on the existing SVG pass, not a second pass: the core
11
19
  * `_updateConnectionLinesImmediate()` (subagent-windows.js) calls
12
20
  * `_appendLineageConnectionLines(svg, rects)` at its tail, exactly like ultracode does,
13
21
  * so every layer shares ONE batched read → write reflow and one tab-rect cache.
14
22
  *
15
- * Two constraints that are not obvious from the code:
16
- * - DESKTOP ONLY. The overlay is `z-index: 999`; the desktop header is 100 (arcs paint
23
+ * Constraints that are not obvious from the code:
24
+ * - DESKTOP ONLY. The overlay is `z-index: 999`; the desktop header is 100 (lines paint
17
25
  * over it, which is what lets them touch tab bottoms), but under 1024px mobile.css
18
26
  * makes the header `position: fixed; z-index: 1200` and would bury them. The phone
19
27
  * strip is also a scroller where both endpoints are rarely on screen at once.
28
+ * - The routing room is RESERVED in CSS (`.session-tabs.lineage-tree`), toggled by
29
+ * `_syncLineageGutter()` from `updateTabOverflowMode()`. It keys on whether ANY
30
+ * family exists, never on the selection, so switching tabs never resizes the header
31
+ * (and with it the terminal and the PTY).
20
32
  * - Paths carry `data-agent-id="lineage:<childId>"` because that is the attribute
21
33
  * `_applyLineEntrances()` queries, so the draw-in animation and its
22
34
  * negative-`animation-delay` resume across `svg.innerHTML = ''` come for free.
23
35
  *
24
36
  * @mixin Extends CodemanApp.prototype via Object.assign
25
37
  * @dependency subagent-windows.js (_updateConnectionLinesImmediate, #connectionLines)
26
- * @dependency constants.js (window.CodemanLineage.computePath + .COLORS)
38
+ * @dependency constants.js (window.CodemanLineage.computeTree + .COLORS)
27
39
  * @dependency settings-ui.js (loadAppSettingsFromStorage, getDefaultSettings)
28
40
  * @loadorder 15.6 (after ultracode-windows.js — appended to the same SVG pass)
29
41
  */
@@ -62,49 +74,99 @@ Object.assign(CodemanApp.prototype, {
62
74
  applyLineageLineSettings() {
63
75
  const prev = this._lineageLinesOn;
64
76
  const next = this._syncLineageLinesEnabled();
65
- if (prev !== next) this.updateConnectionLines();
77
+ if (prev !== next) {
78
+ // The reserved routing room follows the setting; updateTabOverflowMode()
79
+ // re-syncs it and re-measures the wrap with the new padding.
80
+ this.updateTabOverflowMode?.();
81
+ this.updateConnectionLines();
82
+ }
66
83
  },
67
84
 
68
85
  /**
69
- * Every parent → child pair worth drawing, with the child's index among its siblings
70
- * (that index is what nests sibling arcs instead of overprinting them).
86
+ * Reserve (or release) the strip's routing room: `.lineage-tree` on #sessionTabs
87
+ * widens the row gap, pads the bottom for the last row's gap, and opens the spine
88
+ * channel on the left of a wrapped strip (styles.css). Called at the top of
89
+ * `updateTabOverflowMode()`, so it runs on every tab render, BEFORE the wrap is
90
+ * measured.
91
+ *
92
+ * Keyed on whether any family exists at all, never on which one is selected: a
93
+ * class that followed the selection would grow and shrink the header on every tab
94
+ * switch, and the header's height is the terminal's height.
71
95
  *
72
- * Walks `sessionOrder` rather than the sessions Map so sibling depth follows the
73
- * strip's own left-to-right order, which is what the user sees.
96
+ * Only the header strip routes through reserved gaps. The vertical rail keeps its
97
+ * own `--lineage-vertical-gutter`, and the sidebar draws no lineage.
98
+ */
99
+ _syncLineageGutter() {
100
+ const edges = this._lineageLinesEnabled() ? this._collectLineageEdges() : [];
101
+ this._lineageTotalEdges = edges.length;
102
+ const strip = document.getElementById('sessionTabs');
103
+ if (!strip) return;
104
+ const want = edges.length > 0 && !this._isVerticalTabList?.();
105
+ if (strip.classList.contains('lineage-tree') !== want) strip.classList.toggle('lineage-tree', want);
106
+ },
107
+
108
+ /**
109
+ * Every parent → child pair, in strip order. Walks `sessionOrder` rather than the
110
+ * sessions Map so sibling order follows the strip's own left-to-right order, which
111
+ * is what the user sees.
74
112
  */
75
113
  _collectLineageEdges() {
76
114
  const edges = [];
77
115
  if (!this.sessions || this.sessions.size < 2) return edges;
78
116
  const order = this.sessionOrder && this.sessionOrder.length ? this.sessionOrder : [...this.sessions.keys()];
79
- const seenPerParent = new Map();
80
117
  for (const id of order) {
81
118
  const session = this.sessions.get(id);
82
119
  const parentId = session && session.parentSessionId;
83
120
  // A parent that is gone (closed, or never came back after a restart) draws
84
121
  // nothing: the field is decoration, so a dangling one is simply not rendered.
85
122
  if (!parentId || parentId === id || !this.sessions.has(parentId)) continue;
86
- const depth = seenPerParent.get(parentId) || 0;
87
- seenPerParent.set(parentId, depth + 1);
88
- edges.push({ parentId, childId: id, depth, status: session.status || 'idle' });
123
+ edges.push({ parentId, childId: id, status: session.status || 'idle' });
89
124
  }
90
125
  return edges;
91
126
  },
92
127
 
128
+ /** Every family as `{ parentId, edges }`, in strip order of first appearance. */
129
+ _lineageFamilies(edges) {
130
+ const families = new Map();
131
+ for (const edge of edges) {
132
+ if (!families.has(edge.parentId)) families.set(edge.parentId, []);
133
+ families.get(edge.parentId).push(edge);
134
+ }
135
+ return [...families].map(([parentId, familyEdges]) => ({ parentId, edges: familyEdges }));
136
+ },
137
+
93
138
  /**
94
- * Colour for one arc, from CodemanLineage.COLORS, keyed on the SPAWNING tab.
139
+ * Parent ids of the families the selection emphasizes: the family the selected tab
140
+ * spawned, and the family it was spawned into (its parent plus its siblings). Empty
141
+ * when a web tab holds the stage, or the selected tab has no lineage at all.
142
+ */
143
+ _lineageFocusParents(edges) {
144
+ const parents = new Set();
145
+ const focus = this.activeWebviewId ? null : this.activeSessionId;
146
+ if (!focus) return parents;
147
+ for (const edge of edges) {
148
+ if (edge.parentId === focus || edge.childId === focus) parents.add(edge.parentId);
149
+ }
150
+ return parents;
151
+ },
152
+
153
+ /**
154
+ * Colour for one family, from CodemanLineage.COLORS, keyed on the SPAWNING tab.
95
155
  *
96
- * ⚠️ Per PARENT, not per child: every arc leaving one tab is the same colour, no
156
+ * ⚠ Per PARENT, not per child: every line leaving one tab is the same colour, no
97
157
  * matter how many workers it spawns, so the strip reads as "these five came from
98
158
  * w1, those two came from w2". Keying it per child instead gave one tab's own
99
159
  * children a different colour each, which is the thing the colours exist to tell
100
160
  * apart. A child that goes on to spawn its own workers is a parent in its turn and
101
- * gets its own colour for the arcs BELOW it, so a chain changes colour at each
161
+ * gets its own colour for the lines BELOW it, so a chain changes colour at each
102
162
  * generation while each generation's fan-out stays uniform.
103
163
  *
104
164
  * Assigned in FIRST-SEEN order and remembered per parent id. First-seen rather than
105
165
  * draw-index keeps a colour stable across re-renders, tab reorders and sibling
106
166
  * closes (the SVG is wiped and rebuilt constantly, so an index-based colour would
107
- * flicker). An empty string means "no override": the CSS falls back to
167
+ * flicker). The draw pass claims a colour for EVERY family in strip order before it
168
+ * draws anything, so neither the draw order (the selected family goes last) nor
169
+ * which family was selected first ever decides who gets which colour. An empty string means "no override": the CSS falls back to
108
170
  * --session-blue, so the first spawning tab keeps the skin-aware blue.
109
171
  */
110
172
  _lineageColorFor(parentId) {
@@ -140,19 +202,20 @@ Object.assign(CodemanApp.prototype, {
140
202
  _appendLineageConnectionLines(svg, rects) {
141
203
  this._lineageEdgeCount = 0;
142
204
  if (!svg || !this._lineageLinesEnabled()) return;
143
- // Sidebar layout: computeLineagePath()'s whole geometry — the U-bridge hung
144
- // from the STRIP's bottom edge, the 64px dip corridor — assumes a horizontal
145
- // tab row. Against a vertical list the "strip bottom" is the bottom of the
146
- // sidebar, so every arc would draw a giant loop to the foot of the list.
147
- // Parent/child adjacency reads fine in a vertical list without arcs; a
148
- // sideways lineage shape is a follow-up with its own visual tuning, not a
149
- // by-product of a layout port.
205
+ // Sidebar layout: the tree is routed for a horizontal strip or the vertical
206
+ // rail. The sidebar is a vertical list with its own scroller and no reserved
207
+ // channel; parent/child adjacency reads fine there without lines.
150
208
  if (this.isSessionSidebarActive?.()) return;
151
- const compute = window.CodemanLineage && window.CodemanLineage.computePath;
152
- if (!compute) return;
209
+ const computeTree = window.CodemanLineage && window.CodemanLineage.computeTree;
210
+ if (!computeTree) return;
153
211
 
154
212
  const edges = this._collectLineageEdges();
213
+ this._lineageTotalEdges = edges.length;
155
214
  if (edges.length === 0) return;
215
+ // Claim colours in strip order for every family before drawing (_lineageColorFor).
216
+ for (const edge of edges) this._lineageColorFor(edge.parentId);
217
+ const families = this._lineageFamilies(edges);
218
+ const focusParents = this._lineageFocusParents(edges);
156
219
  if (!rects) rects = new Map();
157
220
 
158
221
  // PHASE 1 — reads.
@@ -162,9 +225,9 @@ Object.assign(CodemanApp.prototype, {
162
225
  const orientation =
163
226
  document.documentElement.getAttribute('data-tab-orientation') === 'vertical' ? 'vertical' : 'horizontal';
164
227
  // A session hidden inside a collapsed group of the grouped rail has no row
165
- // to anchor to, so its end of the arc moves to that group's header (a
166
- // "proxied" endpoint, drawn quieter). Two endpoints proxied to the SAME
167
- // header would be an arc from a row to itself: skipped.
228
+ // to anchor to, so its end of the line moves to that group's header (a
229
+ // "proxied" endpoint, drawn quieter). A child proxied to the same header as
230
+ // its parent would be a line from a row to itself: skipped.
168
231
  const resolveEndpoint = (id) => {
169
232
  const tab = strip.querySelector(`.session-tab[data-id="${CSS.escape(id)}"]`);
170
233
  if (tab) return { key: 'tab:' + id, element: tab, proxied: false };
@@ -173,71 +236,138 @@ Object.assign(CodemanApp.prototype, {
173
236
  const header = strip.querySelector(`[data-tab-group-header="${CSS.escape(groupId)}"]`);
174
237
  return { key: 'group:' + groupId, element: header, proxied: !!header };
175
238
  };
176
- const resolvedEdges = [];
177
- for (const edge of edges) {
178
- const parentEndpoint = resolveEndpoint(edge.parentId);
179
- const childEndpoint = resolveEndpoint(edge.childId);
180
- if (parentEndpoint.key === childEndpoint.key) continue;
181
- resolvedEdges.push({ edge, parentEndpoint, childEndpoint });
182
- for (const endpoint of [parentEndpoint, childEndpoint]) {
183
- if (rects.has(endpoint.key)) continue;
239
+ const measure = (endpoint) => {
240
+ if (!rects.has(endpoint.key)) {
184
241
  rects.set(endpoint.key, endpoint.element ? endpoint.element.getBoundingClientRect() : null);
185
242
  }
243
+ return rects.get(endpoint.key);
244
+ };
245
+ const resolvedFamilies = [];
246
+ for (const family of families) {
247
+ const parentEndpoint = resolveEndpoint(family.parentId);
248
+ const parentRect = measure(parentEndpoint);
249
+ if (!parentRect) continue;
250
+ const children = [];
251
+ for (const edge of family.edges) {
252
+ const childEndpoint = resolveEndpoint(edge.childId);
253
+ if (childEndpoint.key === parentEndpoint.key) continue;
254
+ const rect = measure(childEndpoint);
255
+ if (rect) children.push({ edge, endpoint: childEndpoint, rect });
256
+ }
257
+ if (children.length > 0) resolvedFamilies.push({ family, parentEndpoint, parentRect, children });
258
+ }
259
+ if (resolvedFamilies.length === 0) return;
260
+ // The header strip's rows come from EVERY tab in it (computeTree hangs a row's
261
+ // gap under its tallest tab), web tabs included. The rail needs none.
262
+ const tabRects = [];
263
+ let spineLeft;
264
+ if (orientation === 'horizontal') {
265
+ // Where the spine channel is: the reserved --lineage-spine-channel just left
266
+ // of the strip's content edge. Usually that is the strip's own left edge,
267
+ // but grouped by state the label column comes first (styles.css), and a
268
+ // spine at the edge ran through every label. Read back from the padding
269
+ // the CSS laid out, so geometry and stylesheet cannot disagree.
270
+ const style = typeof getComputedStyle === 'function' ? getComputedStyle(strip) : null;
271
+ const channel = style ? parseFloat(style.getPropertyValue('--lineage-spine-channel')) : NaN;
272
+ if (Number.isFinite(channel)) {
273
+ const inset = (parseFloat(style.borderLeftWidth) || 0) + (parseFloat(style.paddingLeft) || 0);
274
+ spineLeft = stripRect.left + inset - channel;
275
+ }
276
+ for (const tab of strip.querySelectorAll('.session-tab')) {
277
+ const id = tab.getAttribute('data-id');
278
+ const key = id ? 'tab:' + id : null;
279
+ if (key && rects.has(key)) tabRects.push(rects.get(key));
280
+ else {
281
+ const rect = tab.getBoundingClientRect();
282
+ if (key) rects.set(key, rect);
283
+ tabRects.push(rect);
284
+ }
285
+ }
186
286
  }
187
- this._lineageEdgeCount = resolvedEdges.length;
188
287
 
189
288
  // PHASE 2 — writes, from the cache only.
190
- for (const { edge, parentEndpoint, childEndpoint } of resolvedEdges) {
191
- const parentRect = rects.get(parentEndpoint.key);
192
- const childRect = rects.get(childEndpoint.key);
193
- if (!parentRect || !childRect) continue;
194
-
195
- const geom = compute({
289
+ // Lanes follow STRIP order, so a family keeps its lane when the selection moves;
290
+ // only the DRAW order changes (the emphasized families last, on top). A row gap
291
+ // fits a few lanes, so they cycle: families that share one are told apart by colour.
292
+ const laneLimit = Math.max(1, (window.CodemanLineage && window.CodemanLineage.MAX_LANES) || 3);
293
+ const laneCount = Math.min(laneLimit, resolvedFamilies.length);
294
+ const drawOrder = resolvedFamilies
295
+ .map((resolved, index) => ({
296
+ ...resolved,
297
+ lane: index % laneCount,
298
+ focus: focusParents.has(resolved.family.parentId),
299
+ }))
300
+ .sort((a, b) => a.focus - b.focus);
301
+ for (const { family, parentEndpoint, parentRect, children, lane, focus } of drawOrder) {
302
+ const geom = computeTree({
196
303
  parent: parentRect,
197
- child: childRect,
304
+ children: children.map((c) => ({ id: c.edge.childId, rect: c.rect })),
198
305
  strip: stripRect,
199
- depth: edge.depth,
306
+ tabs: tabRects,
307
+ spineLeft,
200
308
  orientation,
309
+ lane,
310
+ laneCount,
201
311
  });
202
- if (!geom) continue; // scrolled out of the strip, or a degenerate rect
203
-
204
- const line = document.createElementNS('http://www.w3.org/2000/svg', 'path');
205
- line.setAttribute('d', geom.d);
206
- // The working class marches the dashes, so an active worker is visible along
207
- // the line itself. `status` is the CHILD's, which is the interesting end.
208
- const working = edge.status === 'working' ? ' lineage-line--working' : '';
209
- const proxied = parentEndpoint.proxied || childEndpoint.proxied;
210
- line.setAttribute('class', 'connection-line lineage-line' + working + (proxied ? ' lineage-line--proxied' : ''));
211
- // The PARENT's colour rides a CSS custom property so the stylesheet keeps owning
212
- // opacity, glow and dash; an empty colour leaves the --session-blue fallback.
213
- // Every arc out of one tab shares it — see _lineageColorFor().
214
- const color = this._lineageColorFor(edge.parentId);
215
- if (color) line.style.setProperty('--lineage-color', color);
216
- // `data-agent-id` is what _applyLineEntrances() queries — see the file header.
217
- line.setAttribute('data-agent-id', 'lineage:' + edge.childId);
218
- line.setAttribute('data-parent-tab', edge.parentId);
219
- line.setAttribute('data-child-tab', edge.childId);
220
- svg.appendChild(line);
221
-
222
- // Direction marker at the CHILD end. A circle rather than an SVG <marker>:
223
- // markers need a <defs> block and fight the dash pattern.
224
- const dot = document.createElementNS('http://www.w3.org/2000/svg', 'circle');
225
- dot.setAttribute('cx', String(geom.endX));
226
- dot.setAttribute('cy', String(geom.endY));
227
- // Resting radius; `lineage-dot-pulse` breathes it 3.5 → 4.5 while the child
228
- // works, so the two have to be changed together.
229
- dot.setAttribute('r', '3.5');
230
- dot.setAttribute('class', 'lineage-line-dot' + working + (proxied ? ' lineage-line-dot--proxied' : ''));
231
- dot.setAttribute('data-child-tab', edge.childId);
232
- if (color) dot.style.setProperty('--lineage-color', color);
233
- svg.appendChild(dot);
312
+ if (!geom || geom.routes.length === 0) continue;
313
+ const byId = new Map(children.map((c) => [c.edge.childId, c]));
314
+ const color = this._lineageColorFor(family.parentId);
315
+ // One group per family: it carries the translucency, so the stretches its
316
+ // routes share (the trunk) do not stack into a brighter line than the branches,
317
+ // and the emphasis for the selected tab's families (styles.css).
318
+ const group = document.createElementNS('http://www.w3.org/2000/svg', 'g');
319
+ group.setAttribute('class', 'lineage-family' + (focus ? ' lineage-family--focus' : ''));
320
+ group.setAttribute('data-parent-tab', family.parentId);
321
+ // Working routes go in FIRST, so an idle sibling's solid stroke covers the
322
+ // shared trunk and only the working child's own branch shows its dashes.
323
+ const routes = geom.routes
324
+ .map((route) => ({ route, child: byId.get(route.id) }))
325
+ .filter((r) => r.child)
326
+ .sort((a, b) => (b.child.edge.status === 'working') - (a.child.edge.status === 'working'));
327
+ for (const { route, child } of routes) {
328
+ const line = document.createElementNS('http://www.w3.org/2000/svg', 'path');
329
+ line.setAttribute('d', route.d);
330
+ // `status` is the CHILD's, which is the interesting end: a working child's
331
+ // route is dashed (and marches, motion permitting).
332
+ const working = child.edge.status === 'working' ? ' lineage-line--working' : '';
333
+ const proxied = parentEndpoint.proxied || child.endpoint.proxied;
334
+ line.setAttribute(
335
+ 'class',
336
+ 'connection-line lineage-line' + working + (proxied ? ' lineage-line--proxied' : '')
337
+ );
338
+ // The PARENT's colour rides a CSS custom property so the stylesheet keeps
339
+ // owning weight and dash; an empty colour leaves the --session-blue fallback.
340
+ if (color) line.style.setProperty('--lineage-color', color);
341
+ // `data-agent-id` is what _applyLineEntrances() queries — see the file header.
342
+ line.setAttribute('data-agent-id', 'lineage:' + child.edge.childId);
343
+ line.setAttribute('data-parent-tab', family.parentId);
344
+ line.setAttribute('data-child-tab', child.edge.childId);
345
+ group.appendChild(line);
346
+ }
347
+ // Direction marker at each CHILD end, after every path so no stroke covers it.
348
+ for (const { route, child } of routes) {
349
+ const working = child.edge.status === 'working' ? ' lineage-line--working' : '';
350
+ const proxied = parentEndpoint.proxied || child.endpoint.proxied;
351
+ const dot = document.createElementNS('http://www.w3.org/2000/svg', 'circle');
352
+ dot.setAttribute('cx', String(route.endX));
353
+ dot.setAttribute('cy', String(route.endY));
354
+ // Fallback radius only: styles.css sizes the dot through `--lineage-dot-r`
355
+ // (larger in an emphasized family), and `lineage-dot-pulse` breathes from it.
356
+ dot.setAttribute('r', '2.5');
357
+ dot.setAttribute('class', 'lineage-line-dot' + working + (proxied ? ' lineage-line-dot--proxied' : ''));
358
+ dot.setAttribute('data-child-tab', child.edge.childId);
359
+ if (color) dot.style.setProperty('--lineage-color', color);
360
+ group.appendChild(dot);
361
+ }
362
+ svg.appendChild(group);
363
+ this._lineageEdgeCount += routes.length;
234
364
  }
235
365
  },
236
366
 
237
367
  /**
238
368
  * The strip scrolls (desktop `overflow-x: auto` and every wrapped layout), and a
239
- * scroll moves both endpoints without firing any render, so the arcs would slide off
240
- * their tabs. Passive listener, and the redraw is the normal coalesced one.
369
+ * scroll moves both endpoints without firing any render, so the lines would slide
370
+ * off their tabs. Passive listener, and the redraw is the normal coalesced one.
241
371
  *
242
372
  * Installed once; the guard also keeps a re-init from stacking listeners.
243
373
  */
@@ -248,11 +378,24 @@ Object.assign(CodemanApp.prototype, {
248
378
  this._lineageScrollHandler = () => {
249
379
  // Sidebar layout and the vertical rail scroll the SAME element
250
380
  // vertically, and there the subagent/ultracode connectors anchor to tab
251
- // rects too (the sidebar skips lineage arcs entirely, and the rail can
252
- // show connectors with zero lineage edges, so _lineageEdgeCount alone
253
- // would never redraw them).
381
+ // rects too (the sidebar skips lineage entirely, and the rail can show
382
+ // connectors with zero lineage edges, so _lineageEdgeCount alone would
383
+ // never redraw them).
254
384
  if (this._lineageEdgeCount > 0 || this._isVerticalTabList?.()) this.updateConnectionLines();
255
385
  };
256
386
  strip.addEventListener('scroll', this._lineageScrollHandler, { passive: true });
387
+
388
+ // ⚠ A SELECTION RESIZES TABS AFTER THE REDRAW. The active tab reveals its gear
389
+ // and close icons by transitioning their padding (styles.css), so it keeps
390
+ // widening for ~150ms after `_updateActiveTabImmediate()` has already redrawn,
391
+ // and the tab it was selected from shrinks. That can move tabs or re-wrap a row,
392
+ // which left a family's trunk hanging under the tab's OLD position. Redraw once
393
+ // a size transition inside the strip ends (coalesced, like every other redraw).
394
+ this._lineageTransitionHandler = (event) => {
395
+ const prop = event.propertyName || '';
396
+ if (!(prop === 'width' || prop === 'max-width' || prop.startsWith('padding'))) return;
397
+ if (this._lineageTotalEdges > 0) this.updateConnectionLines();
398
+ };
399
+ strip.addEventListener('transitionend', this._lineageTransitionHandler);
257
400
  },
258
401
  });