@hienlh/ppm 0.23.8 → 0.23.10

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 (448) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/CLAUDE.md +4 -0
  3. package/assets/skills/ppm/SKILL.md +1 -1
  4. package/assets/skills/ppm/references/cli-reference.md +4 -4
  5. package/assets/skills/ppm/references/http-api.md +1 -1
  6. package/dist/web/assets/accounts-settings-section-BOBJbG7i.js +1 -0
  7. package/dist/web/assets/accounts-settings-section-BOBJbG7i.js.br +0 -0
  8. package/dist/web/assets/accounts-settings-section-BOBJbG7i.js.gz +0 -0
  9. package/dist/web/assets/agent-session-sheet-CjD4hBZV.js +2 -0
  10. package/dist/web/assets/agent-session-sheet-CjD4hBZV.js.br +0 -0
  11. package/dist/web/assets/agent-session-sheet-CjD4hBZV.js.gz +0 -0
  12. package/dist/web/assets/agent-session-sheet-store-BcQbnaV2.js +1 -0
  13. package/dist/web/assets/agent-session-window-content-Drm1HsSn.js +1 -0
  14. package/dist/web/assets/agent-session-window-content-Drm1HsSn.js.br +0 -0
  15. package/dist/web/assets/agent-session-window-content-Drm1HsSn.js.gz +0 -0
  16. package/dist/web/assets/{ai-resource-editor-DfVRMUHS.js → ai-resource-editor-M1Eax2Hc.js} +1 -1
  17. package/dist/web/assets/ai-resource-editor-M1Eax2Hc.js.br +0 -0
  18. package/dist/web/assets/ai-resource-editor-M1Eax2Hc.js.gz +0 -0
  19. package/dist/web/assets/ai-settings-section-B73t9SXm.js +1 -0
  20. package/dist/web/assets/{ai-settings-section-Ezhw4Fef.js → ai-settings-section-H173dm1k.js} +1 -1
  21. package/dist/web/assets/ai-settings-section-H173dm1k.js.br +0 -0
  22. package/dist/web/assets/ai-settings-section-H173dm1k.js.gz +0 -0
  23. package/dist/web/assets/{api-settings-tYKJ5Fqk.js → api-settings-BA_n8cPt.js} +1 -1
  24. package/dist/web/assets/{appearance-settings-section-CPOAp0fY.js → appearance-settings-section-Dqu20pt8.js} +1 -1
  25. package/dist/web/assets/appearance-settings-section-Dqu20pt8.js.br +0 -0
  26. package/dist/web/assets/appearance-settings-section-Dqu20pt8.js.gz +0 -0
  27. package/dist/web/assets/{apps-panel-Cz7_o-M4.js → apps-panel-mkrzY7K0.js} +1 -1
  28. package/dist/web/assets/apps-panel-mkrzY7K0.js.br +0 -0
  29. package/dist/web/assets/apps-panel-mkrzY7K0.js.gz +0 -0
  30. package/dist/web/assets/branch-review-tab-Dw3bNixU.js +1 -0
  31. package/dist/web/assets/branch-review-tab-Dw3bNixU.js.br +0 -0
  32. package/dist/web/assets/branch-review-tab-Dw3bNixU.js.gz +0 -0
  33. package/dist/web/assets/chat-tab-BAbcVFzD.js +15 -0
  34. package/dist/web/assets/chat-tab-BAbcVFzD.js.br +0 -0
  35. package/dist/web/assets/chat-tab-BAbcVFzD.js.gz +0 -0
  36. package/dist/web/assets/chat-tab-BaXGaBEC.js +1 -0
  37. package/dist/web/assets/chat-tab-BaXGaBEC.js.br +4 -0
  38. package/dist/web/assets/chat-tab-BaXGaBEC.js.gz +0 -0
  39. package/dist/web/assets/code-editor-CXLANXnI.js +7 -0
  40. package/dist/web/assets/code-editor-CXLANXnI.js.br +0 -0
  41. package/dist/web/assets/code-editor-CXLANXnI.js.gz +0 -0
  42. package/dist/web/assets/{codex-reset-credit-button-D2hal4bG.js → codex-reset-credit-button-CB8Dfhki.js} +1 -1
  43. package/dist/web/assets/codex-reset-credit-button-CB8Dfhki.js.br +0 -0
  44. package/dist/web/assets/codex-reset-credit-button-CB8Dfhki.js.gz +0 -0
  45. package/dist/web/assets/{conflict-editor-Cj_gUncw.js → conflict-editor-CKsxIw3v.js} +2 -2
  46. package/dist/web/assets/conflict-editor-CKsxIw3v.js.br +0 -0
  47. package/dist/web/assets/conflict-editor-CKsxIw3v.js.gz +0 -0
  48. package/dist/web/assets/{csv-preview-Be3ODNj0.js → csv-preview-U0v_r_-7.js} +1 -1
  49. package/dist/web/assets/csv-preview-U0v_r_-7.js.br +0 -0
  50. package/dist/web/assets/csv-preview-U0v_r_-7.js.gz +0 -0
  51. package/dist/web/assets/{database-viewer-B1b--4y8.js → database-viewer-xQyUcBoL.js} +1 -1
  52. package/dist/web/assets/database-viewer-xQyUcBoL.js.br +0 -0
  53. package/dist/web/assets/database-viewer-xQyUcBoL.js.gz +0 -0
  54. package/dist/web/assets/{design-tab-8wi0IHss.js → design-tab-aaAt2_QE.js} +4 -4
  55. package/dist/web/assets/design-tab-aaAt2_QE.js.br +0 -0
  56. package/dist/web/assets/design-tab-aaAt2_QE.js.gz +0 -0
  57. package/dist/web/assets/{designs-sidebar-panel-D5-1Q8zQ.js → designs-sidebar-panel-CTzO4Thu.js} +1 -1
  58. package/dist/web/assets/designs-sidebar-panel-CTzO4Thu.js.br +0 -0
  59. package/dist/web/assets/designs-sidebar-panel-CTzO4Thu.js.gz +0 -0
  60. package/dist/web/assets/{diff-viewer-Dcxk6UTG.js → diff-viewer-CSE2-OE7.js} +1 -1
  61. package/dist/web/assets/diff-viewer-CSE2-OE7.js.br +0 -0
  62. package/dist/web/assets/diff-viewer-CSE2-OE7.js.gz +0 -0
  63. package/dist/web/assets/{edit-diff-preview-DxDAtYhi.js → edit-diff-preview-gH6pRk4S.js} +1 -1
  64. package/dist/web/assets/edit-diff-preview-gH6pRk4S.js.br +0 -0
  65. package/dist/web/assets/{edit-diff-preview-DxDAtYhi.js.gz → edit-diff-preview-gH6pRk4S.js.gz} +0 -0
  66. package/dist/web/assets/{editor-language-service-BVJQnTSV.js → editor-language-service-CYl04pUy.js} +1 -1
  67. package/dist/web/assets/editor-language-service-CYl04pUy.js.br +0 -0
  68. package/dist/web/assets/editor-language-service-CYl04pUy.js.gz +0 -0
  69. package/dist/web/assets/explorer-body-BBOcvwmV.js +2 -0
  70. package/dist/web/assets/explorer-body-BBOcvwmV.js.br +0 -0
  71. package/dist/web/assets/explorer-body-BBOcvwmV.js.gz +0 -0
  72. package/dist/web/assets/{explorer-window-content-DH5AD3Ng.js → explorer-window-content-juCGcrkc.js} +1 -1
  73. package/dist/web/assets/explorer-window-content-juCGcrkc.js.br +0 -0
  74. package/dist/web/assets/explorer-window-content-juCGcrkc.js.gz +0 -0
  75. package/dist/web/assets/{extension-manager-section-BJr6ccWw.js → extension-manager-section-poMRmwvg.js} +1 -1
  76. package/dist/web/assets/extension-manager-section-poMRmwvg.js.br +0 -0
  77. package/dist/web/assets/extension-manager-section-poMRmwvg.js.gz +0 -0
  78. package/dist/web/assets/{extension-webview-CYNXHd8D.js → extension-webview-0QfzTI6s.js} +1 -1
  79. package/dist/web/assets/extension-webview-0QfzTI6s.js.br +0 -0
  80. package/dist/web/assets/extension-webview-0QfzTI6s.js.gz +0 -0
  81. package/dist/web/assets/{files-settings-section-JX7o4lex.js → files-settings-section-DJptxRgH.js} +1 -1
  82. package/dist/web/assets/files-settings-section-DJptxRgH.js.br +0 -0
  83. package/dist/web/assets/files-settings-section-DJptxRgH.js.gz +0 -0
  84. package/dist/web/assets/{general-settings-section-kF94Mz5q.js → general-settings-section-BFNbdDNv.js} +1 -1
  85. package/dist/web/assets/general-settings-section-BFNbdDNv.js.br +0 -0
  86. package/dist/web/assets/general-settings-section-BFNbdDNv.js.gz +0 -0
  87. package/dist/web/assets/{git-log-panel-Crlp9ofM.js → git-log-panel-SMMIbjSe.js} +1 -1
  88. package/dist/web/assets/git-log-panel-SMMIbjSe.js.br +0 -0
  89. package/dist/web/assets/git-log-panel-SMMIbjSe.js.gz +0 -0
  90. package/dist/web/assets/{glide-data-grid-O6MO5dKU.js → glide-data-grid-DZVmgu0T.js} +3 -3
  91. package/dist/web/assets/glide-data-grid-DZVmgu0T.js.br +0 -0
  92. package/dist/web/assets/glide-data-grid-DZVmgu0T.js.gz +0 -0
  93. package/dist/web/assets/group-chat-tab-d_aaYuK_.js +1 -0
  94. package/dist/web/assets/group-chat-tab-d_aaYuK_.js.br +0 -0
  95. package/dist/web/assets/group-chat-tab-d_aaYuK_.js.gz +0 -0
  96. package/dist/web/assets/{index-DQCw6d-2.js → index-CIscPw4g.js} +6 -6
  97. package/dist/web/assets/index-CIscPw4g.js.br +0 -0
  98. package/dist/web/assets/index-CIscPw4g.js.gz +0 -0
  99. package/dist/web/assets/{jira-watcher-section-DwM7gqHT.js → jira-watcher-section-CsbhnLLn.js} +1 -1
  100. package/dist/web/assets/keybindings-store-BPs0pM0r.js +1 -0
  101. package/dist/web/assets/{keyboard-shortcuts-section-CewWGwiF.js → keyboard-shortcuts-section-CXqc2OtM.js} +1 -1
  102. package/dist/web/assets/keyboard-shortcuts-section-CXqc2OtM.js.br +0 -0
  103. package/dist/web/assets/keyboard-shortcuts-section-CXqc2OtM.js.gz +0 -0
  104. package/dist/web/assets/{language-servers-section-nricdxke.js → language-servers-section-B4FsBVvZ.js} +2 -2
  105. package/dist/web/assets/language-servers-section-B4FsBVvZ.js.br +0 -0
  106. package/dist/web/assets/language-servers-section-B4FsBVvZ.js.gz +0 -0
  107. package/dist/web/assets/{lsp-client-DRMEWb-z.js → lsp-client-MQ4mjDfT.js} +1 -1
  108. package/dist/web/assets/lsp-client-MQ4mjDfT.js.br +0 -0
  109. package/dist/web/assets/lsp-client-MQ4mjDfT.js.gz +0 -0
  110. package/dist/web/assets/markdown-renderer-BvCZZlue.js +1 -0
  111. package/dist/web/assets/{markdown-renderer-Dab9Us6u.js → markdown-renderer-CjPb5pns.js} +3 -3
  112. package/dist/web/assets/markdown-renderer-CjPb5pns.js.br +0 -0
  113. package/dist/web/assets/markdown-renderer-CjPb5pns.js.gz +0 -0
  114. package/dist/web/assets/{mobile-explorer-sheet-DtgdpvJ9.js → mobile-explorer-sheet-0aGhVQtI.js} +1 -1
  115. package/dist/web/assets/mobile-explorer-sheet-0aGhVQtI.js.br +0 -0
  116. package/dist/web/assets/mobile-explorer-sheet-0aGhVQtI.js.gz +0 -0
  117. package/dist/web/assets/{new-design-dialog-Dd5KDB_p.js → new-design-dialog-2oT2oc_F.js} +1 -1
  118. package/dist/web/assets/new-design-dialog-2oT2oc_F.js.br +0 -0
  119. package/dist/web/assets/new-design-dialog-2oT2oc_F.js.gz +0 -0
  120. package/dist/web/assets/notification-store-Dd25PMPQ.js +1 -0
  121. package/dist/web/assets/{panel-store-DrSH3Nhp.js → panel-store-1X6yM3XZ.js} +1 -1
  122. package/dist/web/assets/panel-store-1X6yM3XZ.js.br +0 -0
  123. package/dist/web/assets/panel-store-1X6yM3XZ.js.gz +0 -0
  124. package/dist/web/assets/performance-panel-Ub96qrEH.js +3 -0
  125. package/dist/web/assets/performance-panel-Ub96qrEH.js.br +0 -0
  126. package/dist/web/assets/performance-panel-Ub96qrEH.js.gz +0 -0
  127. package/dist/web/assets/{ppmbot-settings-section-D67u6gPi.js → ppmbot-settings-section-CPMGP66g.js} +1 -1
  128. package/dist/web/assets/ppmbot-settings-section-CPMGP66g.js.br +0 -0
  129. package/dist/web/assets/ppmbot-settings-section-CPMGP66g.js.gz +0 -0
  130. package/dist/web/assets/{problems-panel-DKgIw4uZ.js → problems-panel-CWwzZIP3.js} +1 -1
  131. package/dist/web/assets/problems-panel-CWwzZIP3.js.br +0 -0
  132. package/dist/web/assets/problems-panel-CWwzZIP3.js.gz +0 -0
  133. package/dist/web/assets/process-table-BwPws3zS.js +1 -0
  134. package/dist/web/assets/process-table-BwPws3zS.js.br +0 -0
  135. package/dist/web/assets/process-table-BwPws3zS.js.gz +0 -0
  136. package/dist/web/assets/{proxy-settings-section-CFiYcTBA.js → proxy-settings-section-nS8VbxI_.js} +1 -1
  137. package/dist/web/assets/proxy-settings-section-nS8VbxI_.js.br +0 -0
  138. package/dist/web/assets/proxy-settings-section-nS8VbxI_.js.gz +0 -0
  139. package/dist/web/assets/{query-audit-section-BybgiPsc.js → query-audit-section-Dv5Pxn-w.js} +1 -1
  140. package/dist/web/assets/query-audit-section-Dv5Pxn-w.js.br +0 -0
  141. package/dist/web/assets/query-audit-section-Dv5Pxn-w.js.gz +0 -0
  142. package/dist/web/assets/{remote-desktop-clipboard-notice-Bs3fZSRw.js → remote-desktop-clipboard-notice-DEbdu0hC.js} +1 -1
  143. package/dist/web/assets/remote-desktop-clipboard-notice-DEbdu0hC.js.br +0 -0
  144. package/dist/web/assets/remote-desktop-clipboard-notice-DEbdu0hC.js.gz +0 -0
  145. package/dist/web/assets/remote-desktop-mobile-sheet-BJm-akVY.js +2 -0
  146. package/dist/web/assets/remote-desktop-mobile-sheet-BJm-akVY.js.br +0 -0
  147. package/dist/web/assets/remote-desktop-mobile-sheet-BJm-akVY.js.gz +0 -0
  148. package/dist/web/assets/{remote-desktop-mobile-view-DgGOfPVY.js → remote-desktop-mobile-view-DPdGFD6C.js} +1 -1
  149. package/dist/web/assets/remote-desktop-mobile-view-DPdGFD6C.js.br +0 -0
  150. package/dist/web/assets/remote-desktop-mobile-view-DPdGFD6C.js.gz +0 -0
  151. package/dist/web/assets/{remote-desktop-readiness-gate-DOWryrQx.js → remote-desktop-readiness-gate-DV7YgSpP.js} +1 -1
  152. package/dist/web/assets/remote-desktop-readiness-gate-DV7YgSpP.js.br +0 -0
  153. package/dist/web/assets/remote-desktop-readiness-gate-DV7YgSpP.js.gz +0 -0
  154. package/dist/web/assets/remote-desktop-window-content-BktcWMjD.js +1 -0
  155. package/dist/web/assets/remote-desktop-window-content-BktcWMjD.js.br +0 -0
  156. package/dist/web/assets/remote-desktop-window-content-BktcWMjD.js.gz +0 -0
  157. package/dist/web/assets/{run-in-terminal-Cdn9PaAl.js → run-in-terminal-B_PCeBqG.js} +1 -1
  158. package/dist/web/assets/{schedules-settings-section-DGhG-b5O.js → schedules-settings-section-B3HYKSVh.js} +1 -1
  159. package/dist/web/assets/schedules-settings-section-B3HYKSVh.js.br +0 -0
  160. package/dist/web/assets/schedules-settings-section-B3HYKSVh.js.gz +0 -0
  161. package/dist/web/assets/{send-to-chat-B09VjsB5.js → send-to-chat-Bjg6c8Ld.js} +1 -1
  162. package/dist/web/assets/send-to-chat-Bjg6c8Ld.js.br +0 -0
  163. package/dist/web/assets/send-to-chat-Bjg6c8Ld.js.gz +0 -0
  164. package/dist/web/assets/{services-panel-0klE3O4g.js → services-panel-D6k6AVS4.js} +1 -1
  165. package/dist/web/assets/services-panel-D6k6AVS4.js.br +0 -0
  166. package/dist/web/assets/services-panel-D6k6AVS4.js.gz +0 -0
  167. package/dist/web/assets/{settings-body-CfqpqFfq.js → settings-body-Cu0mNA5x.js} +2 -2
  168. package/dist/web/assets/settings-body-Cu0mNA5x.js.br +0 -0
  169. package/dist/web/assets/settings-body-Cu0mNA5x.js.gz +0 -0
  170. package/dist/web/assets/{settings-store-3jIBB8gx.js → settings-store-DIV25PM_.js} +2 -2
  171. package/dist/web/assets/settings-store-DIV25PM_.js.br +0 -0
  172. package/dist/web/assets/settings-store-DIV25PM_.js.gz +0 -0
  173. package/dist/web/assets/{settings-tab-CHweH5Nh.js → settings-tab-C-A6RNQ3.js} +1 -1
  174. package/dist/web/assets/{settings-window-content-C6Od3qV7.js → settings-window-content-48e5FJAq.js} +1 -1
  175. package/dist/web/assets/{sql-query-editor-ALQ5_X0j.js → sql-query-editor-CgGWYu9K.js} +1 -1
  176. package/dist/web/assets/sql-query-editor-CgGWYu9K.js.br +0 -0
  177. package/dist/web/assets/sql-query-editor-CgGWYu9K.js.gz +0 -0
  178. package/dist/web/assets/{sqlite-viewer-DKTty_jR.js → sqlite-viewer-Co1Oi3dv.js} +1 -1
  179. package/dist/web/assets/sqlite-viewer-Co1Oi3dv.js.br +0 -0
  180. package/dist/web/assets/sqlite-viewer-Co1Oi3dv.js.gz +0 -0
  181. package/dist/web/assets/{system-monitor-body-D6J6lq7-.js → system-monitor-body-DxPKVa-W.js} +2 -2
  182. package/dist/web/assets/system-monitor-body-DxPKVa-W.js.br +0 -0
  183. package/dist/web/assets/system-monitor-body-DxPKVa-W.js.gz +0 -0
  184. package/dist/web/assets/system-monitor-tab-4miOZuQQ.js +1 -0
  185. package/dist/web/assets/{system-monitor-window-content-D1t30f32.js → system-monitor-window-content-B-A7DdML.js} +1 -1
  186. package/dist/web/assets/{tab-host-window-content-DTcx2D0O.js → tab-host-window-content-CChcpg1v.js} +1 -1
  187. package/dist/web/assets/tab-host-window-content-CChcpg1v.js.br +0 -0
  188. package/dist/web/assets/tab-host-window-content-CChcpg1v.js.gz +0 -0
  189. package/dist/web/assets/{tab-store-CUPgQIQz.js → tab-store-D33icgdA.js} +1 -1
  190. package/dist/web/assets/{terminal-tab-DgTfQKBI.js → terminal-tab-BDqlyBLK.js} +2 -2
  191. package/dist/web/assets/terminal-tab-BDqlyBLK.js.br +0 -0
  192. package/dist/web/assets/terminal-tab-BDqlyBLK.js.gz +0 -0
  193. package/dist/web/assets/tool-cards-iJ8tyrLq.js +9 -0
  194. package/dist/web/assets/tool-cards-iJ8tyrLq.js.br +0 -0
  195. package/dist/web/assets/tool-cards-iJ8tyrLq.js.gz +0 -0
  196. package/dist/web/assets/{use-inline-blame-Dv111BKY.js → use-inline-blame-D9js570a.js} +4 -4
  197. package/dist/web/assets/use-inline-blame-D9js570a.js.br +0 -0
  198. package/dist/web/assets/use-inline-blame-D9js570a.js.gz +0 -0
  199. package/dist/web/assets/{use-lsp-CZTFJIXX.js → use-lsp-BYtylZId.js} +1 -1
  200. package/dist/web/assets/use-lsp-BYtylZId.js.br +0 -0
  201. package/dist/web/assets/use-lsp-BYtylZId.js.gz +0 -0
  202. package/dist/web/assets/{use-monaco-theme-DL2dIG5L.js → use-monaco-theme-DJ8y_7IZ.js} +1 -1
  203. package/dist/web/assets/{use-process-kill-D7D9QvJG.js → use-process-kill-BS84S-kD.js} +1 -1
  204. package/dist/web/assets/use-process-kill-BS84S-kD.js.br +0 -0
  205. package/dist/web/assets/use-process-kill-BS84S-kD.js.gz +0 -0
  206. package/dist/web/assets/{use-websocket-ziY-Gz_B.js → use-websocket-Mqj27umG.js} +1 -1
  207. package/dist/web/assets/{video-preview-DzwY3q9W.js → video-preview-BuzUAIma.js} +1 -1
  208. package/dist/web/assets/video-preview-BuzUAIma.js.br +0 -0
  209. package/dist/web/assets/video-preview-BuzUAIma.js.gz +0 -0
  210. package/dist/web/assets/{voice-settings-section-B7zMHsuz.js → voice-settings-section-DnwQjBbw.js} +1 -1
  211. package/dist/web/assets/voice-settings-section-DnwQjBbw.js.br +0 -0
  212. package/dist/web/assets/voice-settings-section-DnwQjBbw.js.gz +0 -0
  213. package/dist/web/assets/{window-pip-registry-dxrBFfkP.js → window-pip-registry-Bl--fK8-.js} +1 -1
  214. package/dist/web/assets/window-store-vo1N70nK.js +1 -0
  215. package/dist/web/assets/window-store-vo1N70nK.js.br +0 -0
  216. package/dist/web/assets/window-store-vo1N70nK.js.gz +0 -0
  217. package/dist/web/index.html +6 -6
  218. package/dist/web/index.html.br +0 -0
  219. package/dist/web/index.html.gz +0 -0
  220. package/dist/web/sw.js +1 -1
  221. package/dist/web/sw.js.br +0 -0
  222. package/dist/web/sw.js.gz +0 -0
  223. package/docs/architecture/ai-chat-and-providers.md +66 -0
  224. package/docs/architecture/workspace-and-ui.md +15 -3
  225. package/docs/design-guidelines.md +6 -1
  226. package/package.json +1 -1
  227. package/src/providers/codex-app-server/codex-history.ts +4 -4
  228. package/src/providers/codex-app-server/codex-provider.ts +1 -1
  229. package/src/providers/codex-app-server/codex-rollout-tail-parser.ts +203 -0
  230. package/src/providers/codex-app-server/codex-subagent-thread.ts +7 -1
  231. package/src/server/index.ts +7 -2
  232. package/src/server/routes/chat.ts +12 -4
  233. package/src/server/ws/global.ts +19 -3
  234. package/src/services/agent-transcript/agent-transcript-activity.ts +72 -0
  235. package/src/services/agent-transcript/agent-transcript-codex-activity.ts +161 -0
  236. package/src/services/agent-transcript/agent-transcript-codex-tail-cache.ts +85 -0
  237. package/src/services/agent-transcript/agent-transcript-envelope.ts +86 -0
  238. package/src/services/agent-transcript/agent-transcript-error-map.ts +33 -0
  239. package/src/services/agent-transcript/agent-transcript-file-tail-feed.ts +180 -0
  240. package/src/services/agent-transcript/agent-transcript-file-tail-state.ts +100 -0
  241. package/src/services/agent-transcript/agent-transcript-fs-io.ts +79 -0
  242. package/src/services/agent-transcript/agent-transcript-hub-clock.ts +37 -0
  243. package/src/services/agent-transcript/agent-transcript-hub-registry.ts +153 -0
  244. package/src/services/agent-transcript/agent-transcript-hub.ts +103 -0
  245. package/src/services/agent-transcript/agent-transcript-index-cache.ts +85 -0
  246. package/src/services/agent-transcript/agent-transcript-session-hub-activity.ts +103 -0
  247. package/src/services/agent-transcript/agent-transcript-session-hub-read.ts +194 -0
  248. package/src/services/agent-transcript/agent-transcript-session-hub-types.ts +81 -0
  249. package/src/services/agent-transcript/agent-transcript-session-hub.ts +196 -0
  250. package/src/services/agent-transcript/agent-transcript-sources.ts +199 -0
  251. package/src/services/agent-transcript/agent-transcript-ws-like.ts +19 -0
  252. package/src/services/agent-transcript/claude-projects-root.ts +21 -0
  253. package/src/services/agent-transcript/session-ownership.ts +152 -0
  254. package/src/services/nested-subagent-spy.ts +3 -3
  255. package/src/services/subagent-transcript-merger.ts +21 -7
  256. package/src/shared/agent-transcript-protocol.ts +158 -0
  257. package/src/types/chat.ts +27 -3
  258. package/src/web/app.tsx +5 -5
  259. package/src/web/components/chat/agent-card-summary.tsx +73 -0
  260. package/src/web/components/chat/agent-session-context.ts +34 -0
  261. package/src/web/components/chat/agent-session-fallback-store.ts +60 -0
  262. package/src/web/components/chat/agent-session-sheet-store.ts +23 -0
  263. package/src/web/components/chat/{team-member-sheet.tsx → agent-session-sheet.tsx} +12 -12
  264. package/src/web/components/chat/agent-session-window-content.tsx +133 -0
  265. package/src/web/components/chat/chat-history-bar.tsx +1 -0
  266. package/src/web/components/chat/chat-tab.tsx +21 -4
  267. package/src/web/components/chat/codex-usage-panel.tsx +26 -8
  268. package/src/web/components/chat/running-agents-bar.tsx +93 -0
  269. package/src/web/components/chat/team-activity-panel.tsx +19 -5
  270. package/src/web/components/chat/tool-cards.tsx +73 -4
  271. package/src/web/components/chat/use-open-agent-session.ts +83 -0
  272. package/src/web/components/floating-window/window-content-registry.ts +4 -5
  273. package/src/web/components/floating-window/window-geometry.ts +18 -0
  274. package/src/web/components/floating-window/window-persistence.ts +5 -4
  275. package/src/web/components/floating-window/window-store-types.ts +1 -1
  276. package/src/web/components/settings/resolve-open-settings-action.ts +1 -1
  277. package/src/web/components/system/resolve-open-system-monitor-action.ts +1 -1
  278. package/src/web/hooks/use-agent-activity.ts +65 -0
  279. package/src/web/hooks/use-agent-session-stream.ts +134 -0
  280. package/src/web/hooks/use-chat.ts +12 -18
  281. package/src/web/hooks/use-global-events.ts +35 -2
  282. package/src/web/lib/agent-session-stream-merge.ts +48 -0
  283. package/src/web/lib/agent-step-summary.ts +331 -0
  284. package/src/web/lib/global-ws-channel.ts +41 -0
  285. package/src/web/lib/running-agent-rows.ts +115 -0
  286. package/dist/web/assets/accounts-settings-section-BNcPvEq1.js +0 -1
  287. package/dist/web/assets/accounts-settings-section-BNcPvEq1.js.br +0 -0
  288. package/dist/web/assets/accounts-settings-section-BNcPvEq1.js.gz +0 -0
  289. package/dist/web/assets/ai-resource-editor-DfVRMUHS.js.br +0 -0
  290. package/dist/web/assets/ai-resource-editor-DfVRMUHS.js.gz +0 -0
  291. package/dist/web/assets/ai-settings-section-B_0-P8jx.js +0 -1
  292. package/dist/web/assets/ai-settings-section-Ezhw4Fef.js.br +0 -0
  293. package/dist/web/assets/ai-settings-section-Ezhw4Fef.js.gz +0 -0
  294. package/dist/web/assets/appearance-settings-section-CPOAp0fY.js.br +0 -0
  295. package/dist/web/assets/appearance-settings-section-CPOAp0fY.js.gz +0 -0
  296. package/dist/web/assets/apps-panel-Cz7_o-M4.js.br +0 -0
  297. package/dist/web/assets/apps-panel-Cz7_o-M4.js.gz +0 -0
  298. package/dist/web/assets/branch-review-tab-BPx3kHrM.js +0 -1
  299. package/dist/web/assets/branch-review-tab-BPx3kHrM.js.br +0 -0
  300. package/dist/web/assets/branch-review-tab-BPx3kHrM.js.gz +0 -0
  301. package/dist/web/assets/chat-tab-BadDM_j8.js +0 -1
  302. package/dist/web/assets/chat-tab-BadDM_j8.js.br +0 -0
  303. package/dist/web/assets/chat-tab-BadDM_j8.js.gz +0 -0
  304. package/dist/web/assets/chat-tab-IqAY4c7p.js +0 -15
  305. package/dist/web/assets/chat-tab-IqAY4c7p.js.br +0 -0
  306. package/dist/web/assets/chat-tab-IqAY4c7p.js.gz +0 -0
  307. package/dist/web/assets/code-editor-Dmitw9QS.js +0 -7
  308. package/dist/web/assets/code-editor-Dmitw9QS.js.br +0 -0
  309. package/dist/web/assets/code-editor-Dmitw9QS.js.gz +0 -0
  310. package/dist/web/assets/codex-reset-credit-button-D2hal4bG.js.br +0 -0
  311. package/dist/web/assets/codex-reset-credit-button-D2hal4bG.js.gz +0 -0
  312. package/dist/web/assets/conflict-editor-Cj_gUncw.js.br +0 -0
  313. package/dist/web/assets/conflict-editor-Cj_gUncw.js.gz +0 -0
  314. package/dist/web/assets/csv-preview-Be3ODNj0.js.br +0 -0
  315. package/dist/web/assets/csv-preview-Be3ODNj0.js.gz +0 -0
  316. package/dist/web/assets/database-viewer-B1b--4y8.js.br +0 -0
  317. package/dist/web/assets/database-viewer-B1b--4y8.js.gz +0 -0
  318. package/dist/web/assets/design-tab-8wi0IHss.js.br +0 -0
  319. package/dist/web/assets/design-tab-8wi0IHss.js.gz +0 -0
  320. package/dist/web/assets/designs-sidebar-panel-D5-1Q8zQ.js.br +0 -0
  321. package/dist/web/assets/designs-sidebar-panel-D5-1Q8zQ.js.gz +0 -0
  322. package/dist/web/assets/diff-viewer-Dcxk6UTG.js.br +0 -0
  323. package/dist/web/assets/diff-viewer-Dcxk6UTG.js.gz +0 -0
  324. package/dist/web/assets/edit-diff-preview-DxDAtYhi.js.br +0 -0
  325. package/dist/web/assets/editor-language-service-BVJQnTSV.js.br +0 -0
  326. package/dist/web/assets/editor-language-service-BVJQnTSV.js.gz +0 -0
  327. package/dist/web/assets/explorer-body-CTkrlrI3.js +0 -2
  328. package/dist/web/assets/explorer-body-CTkrlrI3.js.br +0 -0
  329. package/dist/web/assets/explorer-body-CTkrlrI3.js.gz +0 -0
  330. package/dist/web/assets/explorer-window-content-DH5AD3Ng.js.br +0 -0
  331. package/dist/web/assets/explorer-window-content-DH5AD3Ng.js.gz +0 -0
  332. package/dist/web/assets/extension-manager-section-BJr6ccWw.js.br +0 -0
  333. package/dist/web/assets/extension-manager-section-BJr6ccWw.js.gz +0 -0
  334. package/dist/web/assets/extension-webview-CYNXHd8D.js.br +0 -0
  335. package/dist/web/assets/extension-webview-CYNXHd8D.js.gz +0 -0
  336. package/dist/web/assets/files-settings-section-JX7o4lex.js.br +0 -0
  337. package/dist/web/assets/files-settings-section-JX7o4lex.js.gz +0 -0
  338. package/dist/web/assets/general-settings-section-kF94Mz5q.js.br +0 -0
  339. package/dist/web/assets/general-settings-section-kF94Mz5q.js.gz +0 -0
  340. package/dist/web/assets/git-log-panel-Crlp9ofM.js.br +0 -0
  341. package/dist/web/assets/git-log-panel-Crlp9ofM.js.gz +0 -0
  342. package/dist/web/assets/glide-data-grid-O6MO5dKU.js.br +0 -0
  343. package/dist/web/assets/glide-data-grid-O6MO5dKU.js.gz +0 -0
  344. package/dist/web/assets/group-chat-tab-DGLSGf37.js +0 -1
  345. package/dist/web/assets/group-chat-tab-DGLSGf37.js.br +0 -0
  346. package/dist/web/assets/group-chat-tab-DGLSGf37.js.gz +0 -0
  347. package/dist/web/assets/index-DQCw6d-2.js.br +0 -0
  348. package/dist/web/assets/index-DQCw6d-2.js.gz +0 -0
  349. package/dist/web/assets/keybindings-store-CJtEHFIQ.js +0 -1
  350. package/dist/web/assets/keyboard-shortcuts-section-CewWGwiF.js.br +0 -0
  351. package/dist/web/assets/keyboard-shortcuts-section-CewWGwiF.js.gz +0 -0
  352. package/dist/web/assets/language-servers-section-nricdxke.js.br +0 -0
  353. package/dist/web/assets/language-servers-section-nricdxke.js.gz +0 -0
  354. package/dist/web/assets/lsp-client-DRMEWb-z.js.br +0 -0
  355. package/dist/web/assets/lsp-client-DRMEWb-z.js.gz +0 -0
  356. package/dist/web/assets/markdown-renderer-D5-xliE9.js +0 -1
  357. package/dist/web/assets/markdown-renderer-Dab9Us6u.js.br +0 -0
  358. package/dist/web/assets/markdown-renderer-Dab9Us6u.js.gz +0 -0
  359. package/dist/web/assets/mobile-explorer-sheet-DtgdpvJ9.js.br +0 -0
  360. package/dist/web/assets/mobile-explorer-sheet-DtgdpvJ9.js.gz +0 -0
  361. package/dist/web/assets/new-design-dialog-Dd5KDB_p.js.br +0 -0
  362. package/dist/web/assets/new-design-dialog-Dd5KDB_p.js.gz +0 -0
  363. package/dist/web/assets/notification-store-D2vTyp_Q.js +0 -1
  364. package/dist/web/assets/panel-store-DrSH3Nhp.js.br +0 -0
  365. package/dist/web/assets/panel-store-DrSH3Nhp.js.gz +0 -0
  366. package/dist/web/assets/performance-panel-LMGefOQ2.js +0 -3
  367. package/dist/web/assets/performance-panel-LMGefOQ2.js.br +0 -0
  368. package/dist/web/assets/performance-panel-LMGefOQ2.js.gz +0 -0
  369. package/dist/web/assets/ppmbot-settings-section-D67u6gPi.js.br +0 -0
  370. package/dist/web/assets/ppmbot-settings-section-D67u6gPi.js.gz +0 -0
  371. package/dist/web/assets/problems-panel-DKgIw4uZ.js.br +0 -0
  372. package/dist/web/assets/problems-panel-DKgIw4uZ.js.gz +0 -0
  373. package/dist/web/assets/process-table-BIAgC_jG.js +0 -1
  374. package/dist/web/assets/process-table-BIAgC_jG.js.br +0 -0
  375. package/dist/web/assets/process-table-BIAgC_jG.js.gz +0 -0
  376. package/dist/web/assets/proxy-settings-section-CFiYcTBA.js.br +0 -0
  377. package/dist/web/assets/proxy-settings-section-CFiYcTBA.js.gz +0 -0
  378. package/dist/web/assets/query-audit-section-BybgiPsc.js.br +0 -0
  379. package/dist/web/assets/query-audit-section-BybgiPsc.js.gz +0 -0
  380. package/dist/web/assets/remote-desktop-clipboard-notice-Bs3fZSRw.js.br +0 -0
  381. package/dist/web/assets/remote-desktop-clipboard-notice-Bs3fZSRw.js.gz +0 -0
  382. package/dist/web/assets/remote-desktop-mobile-sheet-DgxTdWMh.js +0 -2
  383. package/dist/web/assets/remote-desktop-mobile-sheet-DgxTdWMh.js.br +0 -0
  384. package/dist/web/assets/remote-desktop-mobile-sheet-DgxTdWMh.js.gz +0 -0
  385. package/dist/web/assets/remote-desktop-mobile-view-DgGOfPVY.js.br +0 -0
  386. package/dist/web/assets/remote-desktop-mobile-view-DgGOfPVY.js.gz +0 -0
  387. package/dist/web/assets/remote-desktop-readiness-gate-DOWryrQx.js.br +0 -0
  388. package/dist/web/assets/remote-desktop-readiness-gate-DOWryrQx.js.gz +0 -0
  389. package/dist/web/assets/remote-desktop-window-content-7t0wQsHx.js +0 -1
  390. package/dist/web/assets/remote-desktop-window-content-7t0wQsHx.js.br +0 -0
  391. package/dist/web/assets/remote-desktop-window-content-7t0wQsHx.js.gz +0 -0
  392. package/dist/web/assets/schedules-settings-section-DGhG-b5O.js.br +0 -0
  393. package/dist/web/assets/schedules-settings-section-DGhG-b5O.js.gz +0 -0
  394. package/dist/web/assets/send-to-chat-B09VjsB5.js.br +0 -0
  395. package/dist/web/assets/send-to-chat-B09VjsB5.js.gz +0 -0
  396. package/dist/web/assets/services-panel-0klE3O4g.js.br +0 -0
  397. package/dist/web/assets/services-panel-0klE3O4g.js.gz +0 -0
  398. package/dist/web/assets/settings-body-CfqpqFfq.js.br +0 -0
  399. package/dist/web/assets/settings-body-CfqpqFfq.js.gz +0 -0
  400. package/dist/web/assets/settings-store-3jIBB8gx.js.br +0 -0
  401. package/dist/web/assets/settings-store-3jIBB8gx.js.gz +0 -0
  402. package/dist/web/assets/sql-query-editor-ALQ5_X0j.js.br +0 -0
  403. package/dist/web/assets/sql-query-editor-ALQ5_X0j.js.gz +0 -0
  404. package/dist/web/assets/sqlite-viewer-DKTty_jR.js.br +0 -0
  405. package/dist/web/assets/sqlite-viewer-DKTty_jR.js.gz +0 -0
  406. package/dist/web/assets/system-monitor-body-D6J6lq7-.js.br +0 -0
  407. package/dist/web/assets/system-monitor-body-D6J6lq7-.js.gz +0 -0
  408. package/dist/web/assets/system-monitor-tab-Di18zNNS.js +0 -1
  409. package/dist/web/assets/tab-host-window-content-DTcx2D0O.js.br +0 -0
  410. package/dist/web/assets/tab-host-window-content-DTcx2D0O.js.gz +0 -0
  411. package/dist/web/assets/team-member-sheet-CFSbPG6n.js +0 -2
  412. package/dist/web/assets/team-member-sheet-CFSbPG6n.js.br +0 -0
  413. package/dist/web/assets/team-member-sheet-CFSbPG6n.js.gz +0 -0
  414. package/dist/web/assets/team-member-window-content-VeI6DGeM.js +0 -1
  415. package/dist/web/assets/team-member-window-content-VeI6DGeM.js.br +0 -0
  416. package/dist/web/assets/team-member-window-content-VeI6DGeM.js.gz +0 -0
  417. package/dist/web/assets/terminal-tab-DgTfQKBI.js.br +0 -0
  418. package/dist/web/assets/terminal-tab-DgTfQKBI.js.gz +0 -0
  419. package/dist/web/assets/tool-cards-_fpYI1hB.js +0 -9
  420. package/dist/web/assets/tool-cards-_fpYI1hB.js.br +0 -0
  421. package/dist/web/assets/tool-cards-_fpYI1hB.js.gz +0 -0
  422. package/dist/web/assets/use-inline-blame-Dv111BKY.js.br +0 -0
  423. package/dist/web/assets/use-inline-blame-Dv111BKY.js.gz +0 -0
  424. package/dist/web/assets/use-lsp-CZTFJIXX.js.br +0 -0
  425. package/dist/web/assets/use-lsp-CZTFJIXX.js.gz +0 -0
  426. package/dist/web/assets/use-open-team-member-ElN3KmCI.js +0 -1
  427. package/dist/web/assets/use-process-kill-D7D9QvJG.js.br +0 -0
  428. package/dist/web/assets/use-process-kill-D7D9QvJG.js.gz +0 -0
  429. package/dist/web/assets/video-preview-DzwY3q9W.js.br +0 -0
  430. package/dist/web/assets/video-preview-DzwY3q9W.js.gz +0 -0
  431. package/dist/web/assets/voice-settings-section-B7zMHsuz.js.br +0 -0
  432. package/dist/web/assets/voice-settings-section-B7zMHsuz.js.gz +0 -0
  433. package/dist/web/assets/window-store-B4cbZ5rr.js +0 -1
  434. package/dist/web/assets/window-store-B4cbZ5rr.js.br +0 -0
  435. package/dist/web/assets/window-store-B4cbZ5rr.js.gz +0 -0
  436. package/src/web/components/chat/team-member-window-content.tsx +0 -66
  437. package/src/web/components/chat/team-working-bar.tsx +0 -62
  438. package/src/web/components/chat/use-open-team-member.ts +0 -41
  439. package/src/web/hooks/use-member-transcript-tail.ts +0 -95
  440. /package/dist/web/assets/{api-settings-BeINN5Dy.js → api-settings-BWgfXl7o.js} +0 -0
  441. /package/dist/web/assets/{api-settings-BeINN5Dy.js.br → api-settings-BWgfXl7o.js.br} +0 -0
  442. /package/dist/web/assets/{api-settings-BeINN5Dy.js.gz → api-settings-BWgfXl7o.js.gz} +0 -0
  443. /package/dist/web/assets/{libesm-CxeQSM3t.js → libesm-DAF5_F99.js} +0 -0
  444. /package/dist/web/assets/{libesm-CxeQSM3t.js.br → libesm-DAF5_F99.js.br} +0 -0
  445. /package/dist/web/assets/{libesm-CxeQSM3t.js.gz → libesm-DAF5_F99.js.gz} +0 -0
  446. /package/dist/web/assets/{onboarding-entry-DD3QSDiz.js → onboarding-entry-Bh-WE3vq.js} +0 -0
  447. /package/dist/web/assets/{onboarding-entry-DD3QSDiz.js.br → onboarding-entry-Bh-WE3vq.js.br} +0 -0
  448. /package/dist/web/assets/{onboarding-entry-DD3QSDiz.js.gz → onboarding-entry-Bh-WE3vq.js.gz} +0 -0
@@ -15,11 +15,11 @@
15
15
  */
16
16
 
17
17
  import { existsSync, readdirSync, readFileSync } from "node:fs";
18
- import { homedir } from "node:os";
19
18
  import { join } from "node:path";
20
19
  import type { ChatEvent } from "../types/chat.ts";
21
20
  import { parseSessionMessage } from "./jsonl-transcript-parser.ts";
22
21
  import { groupSubagentsByCard } from "./team-member-activity/subagent-transcript-index.ts";
22
+ import { claudeProjectsRoot } from "./agent-transcript/claude-projects-root.ts";
23
23
 
24
24
  /**
25
25
  * Locate the per-session directory (…/projects/<slug>/<sessionId>) that holds
@@ -29,8 +29,7 @@ import { groupSubagentsByCard } from "./team-member-activity/subagent-transcript
29
29
  * the session's main JSONL.
30
30
  */
31
31
  export function resolveSessionDir(sessionId: string, projectPath: string | null | undefined): string | null {
32
- const home = homedir();
33
- const projectsRoot = join(home, ".claude", "projects");
32
+ const projectsRoot = claudeProjectsRoot();
34
33
  if (projectPath) {
35
34
  const encoded = projectPath.replace(/[/\\:.]/g, "-");
36
35
  const dir = join(projectsRoot, encoded);
@@ -67,16 +66,28 @@ export interface TimedChatEvent {
67
66
  * Incremental parser for one agent transcript. Feed it JSONL lines in file
68
67
  * order (whole file at once, or a live tail's new lines) and it returns the
69
68
  * child events those lines carry, keeping the skip/limit state across calls.
69
+ *
70
+ * `midFile: true` disables the first-user-record skip: a subscriber resuming
71
+ * a session hub mid-file (e.g. a SendMessage resume prompt lands well after the
72
+ * spawn prompt) must see every user record it is fed, not just the ones after
73
+ * the file's first — there is no "first" from where it joined.
74
+ *
75
+ * `maxEvents` overrides the default history-payload guard (`MAX_CHILDREN_PER_AGENT`).
76
+ * A live session-window subscription reads one file per subscriber rather than
77
+ * one payload for a whole reload, and the client already bounds its own render
78
+ * buffer — so the hub passes `Infinity` here rather than silently freezing a
79
+ * long-running teammate's stream once the reload guard's count is reached.
70
80
  */
71
- export function createAgentTranscriptLineParser() {
72
- let isFirstUser = true;
81
+ export function createAgentTranscriptLineParser(opts?: { midFile?: boolean; maxEvents?: number }) {
82
+ let isFirstUser = !opts?.midFile;
83
+ const maxEvents = opts?.maxEvents ?? MAX_CHILDREN_PER_AGENT;
73
84
  let emitted = 0;
74
85
  let lastTs = 0;
75
86
  return {
76
87
  feed(line: string): TimedChatEvent[] {
77
88
  const out: TimedChatEvent[] = [];
78
89
  const trimmed = line.trim();
79
- if (!trimmed || emitted >= MAX_CHILDREN_PER_AGENT) return out;
90
+ if (!trimmed || emitted >= maxEvents) return out;
80
91
  let entry: any;
81
92
  try {
82
93
  entry = JSON.parse(trimmed);
@@ -98,7 +109,7 @@ export function createAgentTranscriptLineParser() {
98
109
  if (!Number.isNaN(parsedTs)) lastTs = parsedTs;
99
110
  const parsed = parseSessionMessage(entry);
100
111
  for (const ev of parsed.events ?? []) {
101
- if (emitted >= MAX_CHILDREN_PER_AGENT) break;
112
+ if (emitted >= maxEvents) break;
102
113
  // Keep single events from ballooning the history payload (agent files
103
114
  // can carry multi-MB tool outputs the live stream also showed in full,
104
115
  // but 24 agents × full outputs breaks mobile reloads).
@@ -151,6 +162,9 @@ export function mergeSubagentChildren(sessionDir: string, messages: MessageLike[
151
162
  if (ev.type !== "tool_use" || (ev.tool !== "Agent" && ev.tool !== "Task") || !ev.toolUseId) continue;
152
163
  const group = groups.get(ev.toolUseId);
153
164
  if (!group) continue;
165
+ // A group was found on disk regardless of what it parsed to — the client can slim this
166
+ // card's children because a session window can always stream the rest from here.
167
+ (ev as { transcriptAvailable?: boolean }).transcriptAvailable = true;
154
168
  const timed: TimedChatEvent[] = [];
155
169
  for (const agent of group) timed.push(...parseAgentTranscriptTimed(agent.transcriptPath));
156
170
  // Stable sort: same-timestamp events keep transcript order.
@@ -0,0 +1,158 @@
1
+ /**
2
+ * Wire protocol for the Agent card session window, carried entirely over
3
+ * `/ws/global`. One hub per `(providerId, sessionId)` on the server streams a
4
+ * card's (or teammate's) transcript from disk, from whatever byte offset the
5
+ * client already has, and keeps pushing new steps live as they are written.
6
+ *
7
+ * Contract the server holds to (see `agent-transcript-hub.ts`):
8
+ * - The client never names a file or a byte range directly — only a `source`
9
+ * (a card id or a teammate handle) the server resolves itself, and a
10
+ * `cursor` of offsets keyed by file keys the server handed out earlier.
11
+ * Offsets for keys the server never derived are ignored; a stale offset
12
+ * past the current file size is clamped down rather than rejected.
13
+ * - Every error the client can see is one of the four codes below — never an
14
+ * exception message, a path, or a code from an internal module.
15
+ */
16
+ import type { ChatEvent } from "../types/chat.ts";
17
+
18
+ export type AgentTranscriptProviderId = "claude" | "codex";
19
+
20
+ export type AgentTranscriptSourceKind =
21
+ | { kind: "card"; cardId: string }
22
+ | { kind: "member"; teamName: string; memberName: string };
23
+
24
+ /** Byte offset already consumed, per file key the server previously derived. */
25
+ export interface AgentTranscriptCursor {
26
+ [fileKey: string]: number;
27
+ }
28
+
29
+ /**
30
+ * One step on the wire. `k` is a stable de-dupe key, normally
31
+ * `"<fileKey>:<byteOffset>:<i>"` where `byteOffset` is a file position before
32
+ * the read that produced it, so a page re-sent after a dropped connection
33
+ * carries the same keys as before. `replace: true` (Codex only) means the
34
+ * client should update the step already shown for this `ev`'s `toolUseId` in
35
+ * place, not append a new one — the usual case is a tool_result answering an
36
+ * earlier tool_use. A `replace` envelope's `k` is the ORIGINAL key issued for
37
+ * that `toolUseId`, reused rather than freshly minted, precisely so a
38
+ * same-`k` upsert on the client has something to match.
39
+ */
40
+ export interface AgentTranscriptEnvelope {
41
+ ev: ChatEvent;
42
+ ts: number;
43
+ k: string;
44
+ replace?: true;
45
+ }
46
+
47
+ export type AgentTranscriptErrorCode = "not_found" | "forbidden" | "limit" | "bad_request";
48
+
49
+ /** One entry of the running-agents bar. Exactly one of `cardId`/`memberName` is set. */
50
+ export interface AgentTranscriptRunningEntry {
51
+ cardId?: string;
52
+ memberName?: string;
53
+ /** Epoch ms the underlying transcript file was last written. */
54
+ lastWriteAt: number;
55
+ /** Short label for the agent's most recent step, when one could be read cheaply. */
56
+ lastStep?: string;
57
+ }
58
+
59
+ // ---- Client → server ----
60
+
61
+ export interface AgentTranscriptSubscribeMsg {
62
+ type: "agent-transcript:subscribe";
63
+ subId: string;
64
+ projectName: string;
65
+ providerId: AgentTranscriptProviderId;
66
+ sessionId: string;
67
+ source: AgentTranscriptSourceKind;
68
+ cursor?: AgentTranscriptCursor;
69
+ }
70
+
71
+ export interface AgentTranscriptUnsubscribeMsg {
72
+ type: "agent-transcript:unsubscribe";
73
+ subId: string;
74
+ }
75
+
76
+ export interface AgentActivitySubscribeMsg {
77
+ type: "agent-activity:subscribe";
78
+ subId: string;
79
+ projectName: string;
80
+ providerId: AgentTranscriptProviderId;
81
+ sessionId: string;
82
+ }
83
+
84
+ export interface AgentActivityUnsubscribeMsg {
85
+ type: "agent-activity:unsubscribe";
86
+ subId: string;
87
+ }
88
+
89
+ export interface PingMsg {
90
+ type: "ping";
91
+ }
92
+
93
+ export type AgentTranscriptClientMsg =
94
+ | AgentTranscriptSubscribeMsg
95
+ | AgentTranscriptUnsubscribeMsg
96
+ | AgentActivitySubscribeMsg
97
+ | AgentActivityUnsubscribeMsg
98
+ | PingMsg;
99
+
100
+ // ---- Server → client ----
101
+
102
+ export interface AgentTranscriptEventsMsg {
103
+ type: "agent-transcript:events";
104
+ subId: string;
105
+ events: AgentTranscriptEnvelope[];
106
+ /** Current per-file consumed offsets for this subscription — the next `cursor` to resume from. */
107
+ cursor: AgentTranscriptCursor;
108
+ /** More pages are coming right behind this one (a backlog being drained on subscribe). */
109
+ more?: true;
110
+ /**
111
+ * The client must discard everything it has for this subscription and
112
+ * replace it with `events` — a truncated/rotated Claude transcript file, or
113
+ * a Codex compaction/rollback record, rewrote history rather than appending
114
+ * to it. Set only on the first page of the replacement; later pages of the
115
+ * same catch-up carry `more` instead.
116
+ */
117
+ reset?: true;
118
+ /** The source resolved to at least one readable file (as opposed to the error path). */
119
+ available: boolean;
120
+ /** Whether the underlying agent still looks active (see hub's idle heuristic). */
121
+ running: boolean;
122
+ }
123
+
124
+ export interface AgentTranscriptErrorMsg {
125
+ type: "agent-transcript:error";
126
+ subId: string;
127
+ code: AgentTranscriptErrorCode;
128
+ }
129
+
130
+ export interface AgentActivityMsg {
131
+ type: "agent-activity";
132
+ subId: string;
133
+ running: AgentTranscriptRunningEntry[];
134
+ }
135
+
136
+ export interface PongMsg {
137
+ type: "pong";
138
+ }
139
+
140
+ export type AgentTranscriptServerMsg =
141
+ | AgentTranscriptEventsMsg
142
+ | AgentTranscriptErrorMsg
143
+ | AgentActivityMsg
144
+ | PongMsg;
145
+
146
+ // ---- Tuning constants, shared so client and server agree ----
147
+
148
+ export const MAX_TRANSCRIPT_SUBS_PER_CLIENT = 8;
149
+ export const MAX_TRANSCRIPT_SUBS_PER_SERVER = 64;
150
+ export const CATCH_UP_MAX_BYTES = 512 * 1024;
151
+ export const CATCH_UP_MAX_EVENTS = 500;
152
+ export const TOOL_OUTPUT_MAX_CHARS = 20_000;
153
+ export const LIVE_TICK_MS = 250;
154
+ export const IDLE_TICK_MS = 2_000;
155
+ export const IDLE_AFTER_MS = 30_000;
156
+ export const ACTIVITY_TICK_MS = 3_000;
157
+ export const ACTIVITY_WINDOW_MS = 90_000;
158
+ export const INDEX_REFRESH_MS = 2_000;
package/src/types/chat.ts CHANGED
@@ -243,15 +243,39 @@ export type ResultSubtype =
243
243
  | "error_auth";
244
244
 
245
245
  export type ChatEvent =
246
- | { type: "text"; content: string; parentToolUseId?: string }
247
- | { type: "thinking"; content: string; parentToolUseId?: string }
246
+ | { type: "text"; content: string; parentToolUseId?: string; arrivalSeq?: number }
247
+ | { type: "thinking"; content: string; parentToolUseId?: string; arrivalSeq?: number }
248
248
  | {
249
249
  type: "tool_use"; tool: string; input: unknown; toolUseId?: string; parentToolUseId?: string; children?: ChatEvent[];
250
250
  /** Terminal state of a backgrounded Agent/Task, once its `<task-notification>` arrives.
251
251
  * Absent on a launched-but-unfinished agent — the card renders that as still running. */
252
252
  bgStatus?: import("../shared/background-agent-status").BackgroundAgentStatus;
253
+ /** Agent/Task only: distinct child tool_use ids counted as "steps" so far — a Set-like
254
+ * array kept stable under a WS replay redelivering the same id. */
255
+ stepIds?: string[];
256
+ /** stepIds.length, cached alongside it so the one-line card need not measure the array. */
257
+ stepCount?: number;
258
+ /** Plain-text description of the most recent step, for the one-line card. */
259
+ lastStep?: string;
260
+ /** Set by a provider when an on-disk transcript was found for this card — its `children`
261
+ * are safe to reduce to the slimmed set because a session window can stream the rest
262
+ * from disk instead. Never set by the live stream itself. */
263
+ transcriptAvailable?: boolean;
264
+ /** Bounded ring buffer (last 200, or ~256KB serialized) of child events slimming would
265
+ * otherwise drop — the fallback shown in a window when no on-disk transcript exists at
266
+ * all, merged back with `children` in original arrival order via each entry's
267
+ * `arrivalSeq`. */
268
+ recentChildren?: ChatEvent[];
269
+ /** Monotonic counter this Agent/Task card's own `applyChildToParent` routing bumps once
270
+ * per incoming child — the source of `arrivalSeq` stamped onto each routed child. */
271
+ childSeq?: number;
272
+ /** Position among this child's siblings in the order they actually arrived — set when a
273
+ * child is routed by `applyChildToParent`/`pushRecentChild` with a parent's `childSeq`,
274
+ * so `children` and `recentChildren` (split apart by kept-vs-ring-buffer routing) can be
275
+ * merged back into arrival order instead of concatenated. */
276
+ arrivalSeq?: number;
253
277
  }
254
- | { type: "tool_result"; output: string; isError?: boolean; exitCode?: number; toolUseId?: string; parentToolUseId?: string }
278
+ | { type: "tool_result"; output: string; isError?: boolean; exitCode?: number; toolUseId?: string; parentToolUseId?: string; arrivalSeq?: number }
255
279
  | { type: "approval_request"; requestId: string; tool: string; input: unknown }
256
280
  | { type: "error"; message: string }
257
281
  | { type: "done"; sessionId: string; resultSubtype?: ResultSubtype; numTurns?: number; contextWindowPct?: number; costUsd?: number; lastMessageUuid?: string; usage?: import("../shared/turn-usage").TurnUsage }
package/src/web/app.tsx CHANGED
@@ -54,8 +54,8 @@ import { OnboardingRoot } from "@/components/onboarding/onboarding-root";
54
54
  const MobileExplorerSheet = lazy(() =>
55
55
  import("@/components/os-explorer/mobile/mobile-explorer-sheet").then((m) => ({ default: m.MobileExplorerSheet })),
56
56
  );
57
- const TeamMemberSheet = lazy(() =>
58
- import("@/components/chat/team-member-sheet").then((m) => ({ default: m.TeamMemberSheet })),
57
+ const AgentSessionSheet = lazy(() =>
58
+ import("@/components/chat/agent-session-sheet").then((m) => ({ default: m.AgentSessionSheet })),
59
59
  );
60
60
  const RemoteDesktopMobileSheet = lazy(() =>
61
61
  import("@/components/remote-desktop/remote-desktop-mobile-sheet").then((m) => ({ default: m.RemoteDesktopMobileSheet })),
@@ -390,12 +390,12 @@ export function App() {
390
390
  <MobileExplorerSheet />
391
391
  </Suspense>
392
392
 
393
- {/* Mobile stand-in for the team-member window, which WindowLayer never renders below md */}
393
+ {/* Mobile stand-in for the agent-session window, which WindowLayer never renders below md */}
394
394
  <Suspense fallback={null}>
395
- <TeamMemberSheet />
395
+ <AgentSessionSheet />
396
396
  </Suspense>
397
397
 
398
- {/* Mobile full-screen remote-desktop viewer — same reason as TeamMemberSheet above */}
398
+ {/* Mobile full-screen remote-desktop viewer — same reason as AgentSessionSheet above */}
399
399
  <Suspense fallback={null}>
400
400
  <RemoteDesktopMobileSheet />
401
401
  </Suspense>
@@ -0,0 +1,73 @@
1
+ /**
2
+ * The Agent/Task card's one-line row in chat: description (or teammate handle),
3
+ * step count, current step, status icon, and a tap that opens the live session
4
+ * window/sheet — no inline expansion, no long-press menu, tap only.
5
+ *
6
+ * `usePrefersCoarsePointer` (not `useIsMobile`) is what gates the 44 px minimum:
7
+ * a touch-capable desktop/tablet needs the bigger target just as much as a
8
+ * phone, and a mouse on a narrow desktop pane does not.
9
+ */
10
+ import { Bot, ChevronRight, Loader2, CheckCircle2, XCircle, Users } from "@/lib/icons";
11
+ import { cn } from "@/lib/utils";
12
+ import { usePrefersCoarsePointer } from "@/components/os-explorer/use-coarse-long-press";
13
+ import { formatStepCount } from "@/lib/agent-step-summary";
14
+
15
+ export type AgentCardStatus = "running" | "done" | "error";
16
+
17
+ export interface AgentCardSummaryProps {
18
+ /** Teammate handle, when the card is addressable via SendMessage — leads the row. */
19
+ handle: string | null;
20
+ /** input.description/prompt, shown when there is no handle. */
21
+ description: string;
22
+ stepCount: number;
23
+ lastStep?: string;
24
+ status: AgentCardStatus;
25
+ /** Mirrors the pre-existing "running…" chip for a launched-but-unconfirmed background agent —
26
+ * distinct from `status === "running"`, which also covers an ordinary still-executing call. */
27
+ bgRunning?: boolean;
28
+ onOpen: () => void;
29
+ }
30
+
31
+ export function AgentCardSummary({
32
+ handle, description, stepCount, lastStep, status, bgRunning, onOpen,
33
+ }: AgentCardSummaryProps) {
34
+ const coarse = usePrefersCoarsePointer();
35
+ const StatusIcon = status === "error" ? XCircle : status === "done" ? CheckCircle2 : Loader2;
36
+ const statusCls = status === "error" ? "text-error" : status === "done" ? "text-success" : "text-primary animate-spin";
37
+
38
+ return (
39
+ <button
40
+ type="button"
41
+ onClick={onOpen}
42
+ className={cn(
43
+ "flex w-full items-center gap-2.5 rounded-[11px] border border-border bg-panel px-2.5 text-left text-xs",
44
+ "transition-colors hover:bg-panel-2/40",
45
+ coarse ? "min-h-[44px] py-2" : "py-2",
46
+ )}
47
+ title={handle ? `Open ${handle}'s session` : "Open agent session"}
48
+ >
49
+ <span className={cn(
50
+ "inline-flex items-center justify-center size-6 rounded-[7px] shrink-0",
51
+ handle ? "bg-accent-2/15 text-accent-2" : "bg-accent-wash text-primary",
52
+ )}>
53
+ {handle ? <Users className="size-3.5" /> : <Bot className="size-3.5" />}
54
+ </span>
55
+
56
+ <span className="min-w-0 flex-1 truncate">
57
+ {handle
58
+ ? <span className="font-medium text-text-primary">{handle}</span>
59
+ : <span className="font-medium text-text">{description || "Agent"}</span>}
60
+ {!!lastStep && (
61
+ <span className="text-text-subtle"> · {lastStep}</span>
62
+ )}
63
+ </span>
64
+
65
+ <span className="ml-auto flex items-center gap-2 shrink-0 text-text-3">
66
+ {bgRunning && <span className="text-[10px] text-primary">running…</span>}
67
+ {stepCount > 0 && <span className="font-mono text-[10px]">{formatStepCount(stepCount)}</span>}
68
+ <StatusIcon className={cn("size-3.5", statusCls)} />
69
+ <ChevronRight className="size-3" />
70
+ </span>
71
+ </button>
72
+ );
73
+ }
@@ -0,0 +1,34 @@
1
+ /**
2
+ * Which session an Agent/Task card's one-line summary belongs to.
3
+ *
4
+ * `ToolCard`/`AgentCardSummary` need `{projectName, providerId, sessionId}` to
5
+ * build the `openAgentSession` payload, but a card has no such identity of its
6
+ * own — it only ever knows its `toolUseId`. `chat-tab.tsx` provides this once
7
+ * per conversation; a read-only surface with no real session (the group-chat
8
+ * transcript viewer) provides none, and the consumer falls back to a synthetic,
9
+ * never-resolvable identity so the card still opens in memory-only mode instead
10
+ * of doing nothing on tap.
11
+ */
12
+ import { createContext, createElement, useContext, type ReactNode } from "react";
13
+ import type { AgentTranscriptProviderId } from "../../../shared/agent-transcript-protocol";
14
+
15
+ export interface AgentSessionIdentity {
16
+ projectName: string;
17
+ providerId: AgentTranscriptProviderId;
18
+ sessionId: string;
19
+ }
20
+
21
+ /** Claude and Codex are the only hubs that exist — anything else falls back to Claude. */
22
+ export function normalizeProviderId(id: string | undefined | null): AgentTranscriptProviderId {
23
+ return id === "codex" ? "codex" : "claude";
24
+ }
25
+
26
+ const AgentSessionContext = createContext<AgentSessionIdentity | null>(null);
27
+
28
+ export function useAgentSessionContext(): AgentSessionIdentity | null {
29
+ return useContext(AgentSessionContext);
30
+ }
31
+
32
+ export function AgentSessionProvider({ value, children }: { value: AgentSessionIdentity; children: ReactNode }) {
33
+ return createElement(AgentSessionContext.Provider, { value }, children);
34
+ }
@@ -0,0 +1,60 @@
1
+ /**
2
+ * Steps already held in chat memory, shown until (or unless) a window/sheet's live hub
3
+ * subscription takes over — seeded by the opener, since the window itself never had the
4
+ * chat's in-memory `children` array to fall back to.
5
+ *
6
+ * Pruned reactively rather than left to grow: every close/focus/open on the window store or
7
+ * the mobile sheet re-derives which keys are still on screen and drops the rest, so a
8
+ * session opened once and closed does not keep its whole event buffer alive for the rest of
9
+ * the page's life.
10
+ */
11
+ import { create } from "zustand";
12
+ import { useWindowStore } from "@/components/floating-window/window-store";
13
+ import { useAgentSessionSheetStore } from "./agent-session-sheet-store";
14
+ import type { AgentTranscriptSourceKind } from "../../../shared/agent-transcript-protocol";
15
+ import type { ChatEvent } from "../../../types/chat";
16
+
17
+ export function fallbackKey(sessionId: string, source: AgentTranscriptSourceKind): string {
18
+ return source.kind === "card"
19
+ ? `${sessionId}:${source.cardId}`
20
+ : `${sessionId}:${source.teamName}:${source.memberName}`;
21
+ }
22
+
23
+ interface FallbackStoreState {
24
+ entries: Record<string, ChatEvent[]>;
25
+ }
26
+
27
+ export const useAgentSessionFallbackStore = create<FallbackStoreState>(() => ({ entries: {} }));
28
+
29
+ /** Called by the opener right before it opens a window/sheet for this key. */
30
+ export function setFallbackEvents(key: string, events: ChatEvent[]): void {
31
+ useAgentSessionFallbackStore.setState((s) => ({ entries: { ...s.entries, [key]: events } }));
32
+ }
33
+
34
+ function activeKeys(): Set<string> {
35
+ const keys = new Set<string>();
36
+ for (const win of Object.values(useWindowStore.getState().windows)) {
37
+ if (win.kind !== "agent-session") continue;
38
+ const p = win.payload as { sessionId?: unknown; source?: AgentTranscriptSourceKind } | undefined;
39
+ if (typeof p?.sessionId === "string" && p.source) keys.add(fallbackKey(p.sessionId, p.source));
40
+ }
41
+ const sheetPayload = useAgentSessionSheetStore.getState().payload;
42
+ if (sheetPayload) keys.add(fallbackKey(sheetPayload.sessionId, sheetPayload.source));
43
+ return keys;
44
+ }
45
+
46
+ function prune(): void {
47
+ const keep = activeKeys();
48
+ useAgentSessionFallbackStore.setState((s) => {
49
+ const entries: Record<string, ChatEvent[]> = {};
50
+ let changed = false;
51
+ for (const [key, events] of Object.entries(s.entries)) {
52
+ if (keep.has(key)) entries[key] = events;
53
+ else changed = true;
54
+ }
55
+ return changed ? { entries } : s;
56
+ });
57
+ }
58
+
59
+ useWindowStore.subscribe(prune);
60
+ useAgentSessionSheetStore.subscribe(prune);
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Mobile-only: which agent/teammate session the full-screen sheet is showing, if any.
3
+ *
4
+ * Kept separate from `use-open-agent-session.ts` (unlike the old `team-member` stack, which
5
+ * had it inline) so `agent-session-fallback-store.ts` can read this store to decide what is
6
+ * still on screen without importing the opener — the opener already imports the fallback
7
+ * store to seed it on open, and a two-way import between them would make the eval order of
8
+ * two zustand `create()` calls load-bearing.
9
+ */
10
+ import { create } from "zustand";
11
+ import type { AgentSessionWindowPayload } from "./agent-session-window-content";
12
+
13
+ interface AgentSessionSheetState {
14
+ payload: AgentSessionWindowPayload | null;
15
+ open: (payload: AgentSessionWindowPayload) => void;
16
+ close: () => void;
17
+ }
18
+
19
+ export const useAgentSessionSheetStore = create<AgentSessionSheetState>((set) => ({
20
+ payload: null,
21
+ open: (payload) => set({ payload }),
22
+ close: () => set({ payload: null }),
23
+ }));
@@ -1,28 +1,28 @@
1
1
  /**
2
- * Full-screen mobile presentation of a teammate's work session.
2
+ * Full-screen mobile presentation of an agent's or teammate's work session.
3
3
  *
4
- * Hosts the same content component the desktop floating window uses, so the steps,
5
- * refresh and error states are shared code rather than a parallel mobile build.
6
- * Self-gated singleton, mounted once at the app root beside the other overlays.
4
+ * Hosts the same content component the desktop floating window uses, so steps, prompt and
5
+ * status are shared code rather than a parallel mobile build. Self-gated singleton, mounted
6
+ * once at the app root beside the other overlays.
7
7
  */
8
8
 
9
9
  import { lazy, Suspense } from "react";
10
10
  import { X } from "@/lib/icons";
11
11
  import { BottomSheet } from "@/components/ui/mobile-bottom-sheet";
12
- import { useTeamMemberSheet } from "./use-open-team-member";
12
+ import { useAgentSessionSheetStore } from "./agent-session-sheet-store";
13
13
 
14
- const TeamMemberWindowContent = lazy(() => import("./team-member-window-content"));
14
+ const AgentSessionWindowContent = lazy(() => import("./agent-session-window-content"));
15
15
 
16
- export function TeamMemberSheet() {
17
- const payload = useTeamMemberSheet((s) => s.payload);
18
- const close = useTeamMemberSheet((s) => s.close);
16
+ export function AgentSessionSheet() {
17
+ const payload = useAgentSessionSheetStore((s) => s.payload);
18
+ const close = useAgentSessionSheetStore((s) => s.close);
19
19
  if (!payload) return null;
20
20
 
21
21
  return (
22
22
  <BottomSheet open onClose={close} zIndex={45} className="flex h-[var(--sheet-vh)] flex-col p-0">
23
23
  <div className="flex items-center gap-2 border-b border-border px-3 py-2 shrink-0">
24
24
  <span className="truncate text-sm font-medium text-text-primary">
25
- Session — {payload.memberName}
25
+ {payload.title ?? "Agent session"}
26
26
  </span>
27
27
  <button
28
28
  type="button"
@@ -36,8 +36,8 @@ export function TeamMemberSheet() {
36
36
  <div className="min-h-0 flex-1 overflow-auto">
37
37
  <Suspense fallback={<div className="p-4 text-sm text-text-2">Loading…</div>}>
38
38
  {/* No floating window backs this sheet, so the id is only an identity for the content. */}
39
- <TeamMemberWindowContent
40
- id={`sheet-${payload.memberName}`}
39
+ <AgentSessionWindowContent
40
+ id={`sheet-${payload.sessionId}`}
41
41
  payload={payload as unknown as Record<string, unknown>}
42
42
  />
43
43
  </Suspense>