@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.
- package/CHANGELOG.md +16 -0
- package/CLAUDE.md +4 -0
- package/assets/skills/ppm/SKILL.md +1 -1
- package/assets/skills/ppm/references/cli-reference.md +4 -4
- package/assets/skills/ppm/references/http-api.md +1 -1
- package/dist/web/assets/accounts-settings-section-BOBJbG7i.js +1 -0
- package/dist/web/assets/accounts-settings-section-BOBJbG7i.js.br +0 -0
- package/dist/web/assets/accounts-settings-section-BOBJbG7i.js.gz +0 -0
- package/dist/web/assets/agent-session-sheet-CjD4hBZV.js +2 -0
- package/dist/web/assets/agent-session-sheet-CjD4hBZV.js.br +0 -0
- package/dist/web/assets/agent-session-sheet-CjD4hBZV.js.gz +0 -0
- package/dist/web/assets/agent-session-sheet-store-BcQbnaV2.js +1 -0
- package/dist/web/assets/agent-session-window-content-Drm1HsSn.js +1 -0
- package/dist/web/assets/agent-session-window-content-Drm1HsSn.js.br +0 -0
- package/dist/web/assets/agent-session-window-content-Drm1HsSn.js.gz +0 -0
- package/dist/web/assets/{ai-resource-editor-DfVRMUHS.js → ai-resource-editor-M1Eax2Hc.js} +1 -1
- package/dist/web/assets/ai-resource-editor-M1Eax2Hc.js.br +0 -0
- package/dist/web/assets/ai-resource-editor-M1Eax2Hc.js.gz +0 -0
- package/dist/web/assets/ai-settings-section-B73t9SXm.js +1 -0
- package/dist/web/assets/{ai-settings-section-Ezhw4Fef.js → ai-settings-section-H173dm1k.js} +1 -1
- package/dist/web/assets/ai-settings-section-H173dm1k.js.br +0 -0
- package/dist/web/assets/ai-settings-section-H173dm1k.js.gz +0 -0
- package/dist/web/assets/{api-settings-tYKJ5Fqk.js → api-settings-BA_n8cPt.js} +1 -1
- package/dist/web/assets/{appearance-settings-section-CPOAp0fY.js → appearance-settings-section-Dqu20pt8.js} +1 -1
- package/dist/web/assets/appearance-settings-section-Dqu20pt8.js.br +0 -0
- package/dist/web/assets/appearance-settings-section-Dqu20pt8.js.gz +0 -0
- package/dist/web/assets/{apps-panel-Cz7_o-M4.js → apps-panel-mkrzY7K0.js} +1 -1
- package/dist/web/assets/apps-panel-mkrzY7K0.js.br +0 -0
- package/dist/web/assets/apps-panel-mkrzY7K0.js.gz +0 -0
- package/dist/web/assets/branch-review-tab-Dw3bNixU.js +1 -0
- package/dist/web/assets/branch-review-tab-Dw3bNixU.js.br +0 -0
- package/dist/web/assets/branch-review-tab-Dw3bNixU.js.gz +0 -0
- package/dist/web/assets/chat-tab-BAbcVFzD.js +15 -0
- package/dist/web/assets/chat-tab-BAbcVFzD.js.br +0 -0
- package/dist/web/assets/chat-tab-BAbcVFzD.js.gz +0 -0
- package/dist/web/assets/chat-tab-BaXGaBEC.js +1 -0
- package/dist/web/assets/chat-tab-BaXGaBEC.js.br +4 -0
- package/dist/web/assets/chat-tab-BaXGaBEC.js.gz +0 -0
- package/dist/web/assets/code-editor-CXLANXnI.js +7 -0
- package/dist/web/assets/code-editor-CXLANXnI.js.br +0 -0
- package/dist/web/assets/code-editor-CXLANXnI.js.gz +0 -0
- package/dist/web/assets/{codex-reset-credit-button-D2hal4bG.js → codex-reset-credit-button-CB8Dfhki.js} +1 -1
- package/dist/web/assets/codex-reset-credit-button-CB8Dfhki.js.br +0 -0
- package/dist/web/assets/codex-reset-credit-button-CB8Dfhki.js.gz +0 -0
- package/dist/web/assets/{conflict-editor-Cj_gUncw.js → conflict-editor-CKsxIw3v.js} +2 -2
- package/dist/web/assets/conflict-editor-CKsxIw3v.js.br +0 -0
- package/dist/web/assets/conflict-editor-CKsxIw3v.js.gz +0 -0
- package/dist/web/assets/{csv-preview-Be3ODNj0.js → csv-preview-U0v_r_-7.js} +1 -1
- package/dist/web/assets/csv-preview-U0v_r_-7.js.br +0 -0
- package/dist/web/assets/csv-preview-U0v_r_-7.js.gz +0 -0
- package/dist/web/assets/{database-viewer-B1b--4y8.js → database-viewer-xQyUcBoL.js} +1 -1
- package/dist/web/assets/database-viewer-xQyUcBoL.js.br +0 -0
- package/dist/web/assets/database-viewer-xQyUcBoL.js.gz +0 -0
- package/dist/web/assets/{design-tab-8wi0IHss.js → design-tab-aaAt2_QE.js} +4 -4
- package/dist/web/assets/design-tab-aaAt2_QE.js.br +0 -0
- package/dist/web/assets/design-tab-aaAt2_QE.js.gz +0 -0
- package/dist/web/assets/{designs-sidebar-panel-D5-1Q8zQ.js → designs-sidebar-panel-CTzO4Thu.js} +1 -1
- package/dist/web/assets/designs-sidebar-panel-CTzO4Thu.js.br +0 -0
- package/dist/web/assets/designs-sidebar-panel-CTzO4Thu.js.gz +0 -0
- package/dist/web/assets/{diff-viewer-Dcxk6UTG.js → diff-viewer-CSE2-OE7.js} +1 -1
- package/dist/web/assets/diff-viewer-CSE2-OE7.js.br +0 -0
- package/dist/web/assets/diff-viewer-CSE2-OE7.js.gz +0 -0
- package/dist/web/assets/{edit-diff-preview-DxDAtYhi.js → edit-diff-preview-gH6pRk4S.js} +1 -1
- package/dist/web/assets/edit-diff-preview-gH6pRk4S.js.br +0 -0
- package/dist/web/assets/{edit-diff-preview-DxDAtYhi.js.gz → edit-diff-preview-gH6pRk4S.js.gz} +0 -0
- package/dist/web/assets/{editor-language-service-BVJQnTSV.js → editor-language-service-CYl04pUy.js} +1 -1
- package/dist/web/assets/editor-language-service-CYl04pUy.js.br +0 -0
- package/dist/web/assets/editor-language-service-CYl04pUy.js.gz +0 -0
- package/dist/web/assets/explorer-body-BBOcvwmV.js +2 -0
- package/dist/web/assets/explorer-body-BBOcvwmV.js.br +0 -0
- package/dist/web/assets/explorer-body-BBOcvwmV.js.gz +0 -0
- package/dist/web/assets/{explorer-window-content-DH5AD3Ng.js → explorer-window-content-juCGcrkc.js} +1 -1
- package/dist/web/assets/explorer-window-content-juCGcrkc.js.br +0 -0
- package/dist/web/assets/explorer-window-content-juCGcrkc.js.gz +0 -0
- package/dist/web/assets/{extension-manager-section-BJr6ccWw.js → extension-manager-section-poMRmwvg.js} +1 -1
- package/dist/web/assets/extension-manager-section-poMRmwvg.js.br +0 -0
- package/dist/web/assets/extension-manager-section-poMRmwvg.js.gz +0 -0
- package/dist/web/assets/{extension-webview-CYNXHd8D.js → extension-webview-0QfzTI6s.js} +1 -1
- package/dist/web/assets/extension-webview-0QfzTI6s.js.br +0 -0
- package/dist/web/assets/extension-webview-0QfzTI6s.js.gz +0 -0
- package/dist/web/assets/{files-settings-section-JX7o4lex.js → files-settings-section-DJptxRgH.js} +1 -1
- package/dist/web/assets/files-settings-section-DJptxRgH.js.br +0 -0
- package/dist/web/assets/files-settings-section-DJptxRgH.js.gz +0 -0
- package/dist/web/assets/{general-settings-section-kF94Mz5q.js → general-settings-section-BFNbdDNv.js} +1 -1
- package/dist/web/assets/general-settings-section-BFNbdDNv.js.br +0 -0
- package/dist/web/assets/general-settings-section-BFNbdDNv.js.gz +0 -0
- package/dist/web/assets/{git-log-panel-Crlp9ofM.js → git-log-panel-SMMIbjSe.js} +1 -1
- package/dist/web/assets/git-log-panel-SMMIbjSe.js.br +0 -0
- package/dist/web/assets/git-log-panel-SMMIbjSe.js.gz +0 -0
- package/dist/web/assets/{glide-data-grid-O6MO5dKU.js → glide-data-grid-DZVmgu0T.js} +3 -3
- package/dist/web/assets/glide-data-grid-DZVmgu0T.js.br +0 -0
- package/dist/web/assets/glide-data-grid-DZVmgu0T.js.gz +0 -0
- package/dist/web/assets/group-chat-tab-d_aaYuK_.js +1 -0
- package/dist/web/assets/group-chat-tab-d_aaYuK_.js.br +0 -0
- package/dist/web/assets/group-chat-tab-d_aaYuK_.js.gz +0 -0
- package/dist/web/assets/{index-DQCw6d-2.js → index-CIscPw4g.js} +6 -6
- package/dist/web/assets/index-CIscPw4g.js.br +0 -0
- package/dist/web/assets/index-CIscPw4g.js.gz +0 -0
- package/dist/web/assets/{jira-watcher-section-DwM7gqHT.js → jira-watcher-section-CsbhnLLn.js} +1 -1
- package/dist/web/assets/keybindings-store-BPs0pM0r.js +1 -0
- package/dist/web/assets/{keyboard-shortcuts-section-CewWGwiF.js → keyboard-shortcuts-section-CXqc2OtM.js} +1 -1
- package/dist/web/assets/keyboard-shortcuts-section-CXqc2OtM.js.br +0 -0
- package/dist/web/assets/keyboard-shortcuts-section-CXqc2OtM.js.gz +0 -0
- package/dist/web/assets/{language-servers-section-nricdxke.js → language-servers-section-B4FsBVvZ.js} +2 -2
- package/dist/web/assets/language-servers-section-B4FsBVvZ.js.br +0 -0
- package/dist/web/assets/language-servers-section-B4FsBVvZ.js.gz +0 -0
- package/dist/web/assets/{lsp-client-DRMEWb-z.js → lsp-client-MQ4mjDfT.js} +1 -1
- package/dist/web/assets/lsp-client-MQ4mjDfT.js.br +0 -0
- package/dist/web/assets/lsp-client-MQ4mjDfT.js.gz +0 -0
- package/dist/web/assets/markdown-renderer-BvCZZlue.js +1 -0
- package/dist/web/assets/{markdown-renderer-Dab9Us6u.js → markdown-renderer-CjPb5pns.js} +3 -3
- package/dist/web/assets/markdown-renderer-CjPb5pns.js.br +0 -0
- package/dist/web/assets/markdown-renderer-CjPb5pns.js.gz +0 -0
- package/dist/web/assets/{mobile-explorer-sheet-DtgdpvJ9.js → mobile-explorer-sheet-0aGhVQtI.js} +1 -1
- package/dist/web/assets/mobile-explorer-sheet-0aGhVQtI.js.br +0 -0
- package/dist/web/assets/mobile-explorer-sheet-0aGhVQtI.js.gz +0 -0
- package/dist/web/assets/{new-design-dialog-Dd5KDB_p.js → new-design-dialog-2oT2oc_F.js} +1 -1
- package/dist/web/assets/new-design-dialog-2oT2oc_F.js.br +0 -0
- package/dist/web/assets/new-design-dialog-2oT2oc_F.js.gz +0 -0
- package/dist/web/assets/notification-store-Dd25PMPQ.js +1 -0
- package/dist/web/assets/{panel-store-DrSH3Nhp.js → panel-store-1X6yM3XZ.js} +1 -1
- package/dist/web/assets/panel-store-1X6yM3XZ.js.br +0 -0
- package/dist/web/assets/panel-store-1X6yM3XZ.js.gz +0 -0
- package/dist/web/assets/performance-panel-Ub96qrEH.js +3 -0
- package/dist/web/assets/performance-panel-Ub96qrEH.js.br +0 -0
- package/dist/web/assets/performance-panel-Ub96qrEH.js.gz +0 -0
- package/dist/web/assets/{ppmbot-settings-section-D67u6gPi.js → ppmbot-settings-section-CPMGP66g.js} +1 -1
- package/dist/web/assets/ppmbot-settings-section-CPMGP66g.js.br +0 -0
- package/dist/web/assets/ppmbot-settings-section-CPMGP66g.js.gz +0 -0
- package/dist/web/assets/{problems-panel-DKgIw4uZ.js → problems-panel-CWwzZIP3.js} +1 -1
- package/dist/web/assets/problems-panel-CWwzZIP3.js.br +0 -0
- package/dist/web/assets/problems-panel-CWwzZIP3.js.gz +0 -0
- package/dist/web/assets/process-table-BwPws3zS.js +1 -0
- package/dist/web/assets/process-table-BwPws3zS.js.br +0 -0
- package/dist/web/assets/process-table-BwPws3zS.js.gz +0 -0
- package/dist/web/assets/{proxy-settings-section-CFiYcTBA.js → proxy-settings-section-nS8VbxI_.js} +1 -1
- package/dist/web/assets/proxy-settings-section-nS8VbxI_.js.br +0 -0
- package/dist/web/assets/proxy-settings-section-nS8VbxI_.js.gz +0 -0
- package/dist/web/assets/{query-audit-section-BybgiPsc.js → query-audit-section-Dv5Pxn-w.js} +1 -1
- package/dist/web/assets/query-audit-section-Dv5Pxn-w.js.br +0 -0
- package/dist/web/assets/query-audit-section-Dv5Pxn-w.js.gz +0 -0
- package/dist/web/assets/{remote-desktop-clipboard-notice-Bs3fZSRw.js → remote-desktop-clipboard-notice-DEbdu0hC.js} +1 -1
- package/dist/web/assets/remote-desktop-clipboard-notice-DEbdu0hC.js.br +0 -0
- package/dist/web/assets/remote-desktop-clipboard-notice-DEbdu0hC.js.gz +0 -0
- package/dist/web/assets/remote-desktop-mobile-sheet-BJm-akVY.js +2 -0
- package/dist/web/assets/remote-desktop-mobile-sheet-BJm-akVY.js.br +0 -0
- package/dist/web/assets/remote-desktop-mobile-sheet-BJm-akVY.js.gz +0 -0
- package/dist/web/assets/{remote-desktop-mobile-view-DgGOfPVY.js → remote-desktop-mobile-view-DPdGFD6C.js} +1 -1
- package/dist/web/assets/remote-desktop-mobile-view-DPdGFD6C.js.br +0 -0
- package/dist/web/assets/remote-desktop-mobile-view-DPdGFD6C.js.gz +0 -0
- package/dist/web/assets/{remote-desktop-readiness-gate-DOWryrQx.js → remote-desktop-readiness-gate-DV7YgSpP.js} +1 -1
- package/dist/web/assets/remote-desktop-readiness-gate-DV7YgSpP.js.br +0 -0
- package/dist/web/assets/remote-desktop-readiness-gate-DV7YgSpP.js.gz +0 -0
- package/dist/web/assets/remote-desktop-window-content-BktcWMjD.js +1 -0
- package/dist/web/assets/remote-desktop-window-content-BktcWMjD.js.br +0 -0
- package/dist/web/assets/remote-desktop-window-content-BktcWMjD.js.gz +0 -0
- package/dist/web/assets/{run-in-terminal-Cdn9PaAl.js → run-in-terminal-B_PCeBqG.js} +1 -1
- package/dist/web/assets/{schedules-settings-section-DGhG-b5O.js → schedules-settings-section-B3HYKSVh.js} +1 -1
- package/dist/web/assets/schedules-settings-section-B3HYKSVh.js.br +0 -0
- package/dist/web/assets/schedules-settings-section-B3HYKSVh.js.gz +0 -0
- package/dist/web/assets/{send-to-chat-B09VjsB5.js → send-to-chat-Bjg6c8Ld.js} +1 -1
- package/dist/web/assets/send-to-chat-Bjg6c8Ld.js.br +0 -0
- package/dist/web/assets/send-to-chat-Bjg6c8Ld.js.gz +0 -0
- package/dist/web/assets/{services-panel-0klE3O4g.js → services-panel-D6k6AVS4.js} +1 -1
- package/dist/web/assets/services-panel-D6k6AVS4.js.br +0 -0
- package/dist/web/assets/services-panel-D6k6AVS4.js.gz +0 -0
- package/dist/web/assets/{settings-body-CfqpqFfq.js → settings-body-Cu0mNA5x.js} +2 -2
- package/dist/web/assets/settings-body-Cu0mNA5x.js.br +0 -0
- package/dist/web/assets/settings-body-Cu0mNA5x.js.gz +0 -0
- package/dist/web/assets/{settings-store-3jIBB8gx.js → settings-store-DIV25PM_.js} +2 -2
- package/dist/web/assets/settings-store-DIV25PM_.js.br +0 -0
- package/dist/web/assets/settings-store-DIV25PM_.js.gz +0 -0
- package/dist/web/assets/{settings-tab-CHweH5Nh.js → settings-tab-C-A6RNQ3.js} +1 -1
- package/dist/web/assets/{settings-window-content-C6Od3qV7.js → settings-window-content-48e5FJAq.js} +1 -1
- package/dist/web/assets/{sql-query-editor-ALQ5_X0j.js → sql-query-editor-CgGWYu9K.js} +1 -1
- package/dist/web/assets/sql-query-editor-CgGWYu9K.js.br +0 -0
- package/dist/web/assets/sql-query-editor-CgGWYu9K.js.gz +0 -0
- package/dist/web/assets/{sqlite-viewer-DKTty_jR.js → sqlite-viewer-Co1Oi3dv.js} +1 -1
- package/dist/web/assets/sqlite-viewer-Co1Oi3dv.js.br +0 -0
- package/dist/web/assets/sqlite-viewer-Co1Oi3dv.js.gz +0 -0
- package/dist/web/assets/{system-monitor-body-D6J6lq7-.js → system-monitor-body-DxPKVa-W.js} +2 -2
- package/dist/web/assets/system-monitor-body-DxPKVa-W.js.br +0 -0
- package/dist/web/assets/system-monitor-body-DxPKVa-W.js.gz +0 -0
- package/dist/web/assets/system-monitor-tab-4miOZuQQ.js +1 -0
- package/dist/web/assets/{system-monitor-window-content-D1t30f32.js → system-monitor-window-content-B-A7DdML.js} +1 -1
- package/dist/web/assets/{tab-host-window-content-DTcx2D0O.js → tab-host-window-content-CChcpg1v.js} +1 -1
- package/dist/web/assets/tab-host-window-content-CChcpg1v.js.br +0 -0
- package/dist/web/assets/tab-host-window-content-CChcpg1v.js.gz +0 -0
- package/dist/web/assets/{tab-store-CUPgQIQz.js → tab-store-D33icgdA.js} +1 -1
- package/dist/web/assets/{terminal-tab-DgTfQKBI.js → terminal-tab-BDqlyBLK.js} +2 -2
- package/dist/web/assets/terminal-tab-BDqlyBLK.js.br +0 -0
- package/dist/web/assets/terminal-tab-BDqlyBLK.js.gz +0 -0
- package/dist/web/assets/tool-cards-iJ8tyrLq.js +9 -0
- package/dist/web/assets/tool-cards-iJ8tyrLq.js.br +0 -0
- package/dist/web/assets/tool-cards-iJ8tyrLq.js.gz +0 -0
- package/dist/web/assets/{use-inline-blame-Dv111BKY.js → use-inline-blame-D9js570a.js} +4 -4
- package/dist/web/assets/use-inline-blame-D9js570a.js.br +0 -0
- package/dist/web/assets/use-inline-blame-D9js570a.js.gz +0 -0
- package/dist/web/assets/{use-lsp-CZTFJIXX.js → use-lsp-BYtylZId.js} +1 -1
- package/dist/web/assets/use-lsp-BYtylZId.js.br +0 -0
- package/dist/web/assets/use-lsp-BYtylZId.js.gz +0 -0
- package/dist/web/assets/{use-monaco-theme-DL2dIG5L.js → use-monaco-theme-DJ8y_7IZ.js} +1 -1
- package/dist/web/assets/{use-process-kill-D7D9QvJG.js → use-process-kill-BS84S-kD.js} +1 -1
- package/dist/web/assets/use-process-kill-BS84S-kD.js.br +0 -0
- package/dist/web/assets/use-process-kill-BS84S-kD.js.gz +0 -0
- package/dist/web/assets/{use-websocket-ziY-Gz_B.js → use-websocket-Mqj27umG.js} +1 -1
- package/dist/web/assets/{video-preview-DzwY3q9W.js → video-preview-BuzUAIma.js} +1 -1
- package/dist/web/assets/video-preview-BuzUAIma.js.br +0 -0
- package/dist/web/assets/video-preview-BuzUAIma.js.gz +0 -0
- package/dist/web/assets/{voice-settings-section-B7zMHsuz.js → voice-settings-section-DnwQjBbw.js} +1 -1
- package/dist/web/assets/voice-settings-section-DnwQjBbw.js.br +0 -0
- package/dist/web/assets/voice-settings-section-DnwQjBbw.js.gz +0 -0
- package/dist/web/assets/{window-pip-registry-dxrBFfkP.js → window-pip-registry-Bl--fK8-.js} +1 -1
- package/dist/web/assets/window-store-vo1N70nK.js +1 -0
- package/dist/web/assets/window-store-vo1N70nK.js.br +0 -0
- package/dist/web/assets/window-store-vo1N70nK.js.gz +0 -0
- package/dist/web/index.html +6 -6
- package/dist/web/index.html.br +0 -0
- package/dist/web/index.html.gz +0 -0
- package/dist/web/sw.js +1 -1
- package/dist/web/sw.js.br +0 -0
- package/dist/web/sw.js.gz +0 -0
- package/docs/architecture/ai-chat-and-providers.md +66 -0
- package/docs/architecture/workspace-and-ui.md +15 -3
- package/docs/design-guidelines.md +6 -1
- package/package.json +1 -1
- package/src/providers/codex-app-server/codex-history.ts +4 -4
- package/src/providers/codex-app-server/codex-provider.ts +1 -1
- package/src/providers/codex-app-server/codex-rollout-tail-parser.ts +203 -0
- package/src/providers/codex-app-server/codex-subagent-thread.ts +7 -1
- package/src/server/index.ts +7 -2
- package/src/server/routes/chat.ts +12 -4
- package/src/server/ws/global.ts +19 -3
- package/src/services/agent-transcript/agent-transcript-activity.ts +72 -0
- package/src/services/agent-transcript/agent-transcript-codex-activity.ts +161 -0
- package/src/services/agent-transcript/agent-transcript-codex-tail-cache.ts +85 -0
- package/src/services/agent-transcript/agent-transcript-envelope.ts +86 -0
- package/src/services/agent-transcript/agent-transcript-error-map.ts +33 -0
- package/src/services/agent-transcript/agent-transcript-file-tail-feed.ts +180 -0
- package/src/services/agent-transcript/agent-transcript-file-tail-state.ts +100 -0
- package/src/services/agent-transcript/agent-transcript-fs-io.ts +79 -0
- package/src/services/agent-transcript/agent-transcript-hub-clock.ts +37 -0
- package/src/services/agent-transcript/agent-transcript-hub-registry.ts +153 -0
- package/src/services/agent-transcript/agent-transcript-hub.ts +103 -0
- package/src/services/agent-transcript/agent-transcript-index-cache.ts +85 -0
- package/src/services/agent-transcript/agent-transcript-session-hub-activity.ts +103 -0
- package/src/services/agent-transcript/agent-transcript-session-hub-read.ts +194 -0
- package/src/services/agent-transcript/agent-transcript-session-hub-types.ts +81 -0
- package/src/services/agent-transcript/agent-transcript-session-hub.ts +196 -0
- package/src/services/agent-transcript/agent-transcript-sources.ts +199 -0
- package/src/services/agent-transcript/agent-transcript-ws-like.ts +19 -0
- package/src/services/agent-transcript/claude-projects-root.ts +21 -0
- package/src/services/agent-transcript/session-ownership.ts +152 -0
- package/src/services/nested-subagent-spy.ts +3 -3
- package/src/services/subagent-transcript-merger.ts +21 -7
- package/src/shared/agent-transcript-protocol.ts +158 -0
- package/src/types/chat.ts +27 -3
- package/src/web/app.tsx +5 -5
- package/src/web/components/chat/agent-card-summary.tsx +73 -0
- package/src/web/components/chat/agent-session-context.ts +34 -0
- package/src/web/components/chat/agent-session-fallback-store.ts +60 -0
- package/src/web/components/chat/agent-session-sheet-store.ts +23 -0
- package/src/web/components/chat/{team-member-sheet.tsx → agent-session-sheet.tsx} +12 -12
- package/src/web/components/chat/agent-session-window-content.tsx +133 -0
- package/src/web/components/chat/chat-history-bar.tsx +1 -0
- package/src/web/components/chat/chat-tab.tsx +21 -4
- package/src/web/components/chat/codex-usage-panel.tsx +26 -8
- package/src/web/components/chat/running-agents-bar.tsx +93 -0
- package/src/web/components/chat/team-activity-panel.tsx +19 -5
- package/src/web/components/chat/tool-cards.tsx +73 -4
- package/src/web/components/chat/use-open-agent-session.ts +83 -0
- package/src/web/components/floating-window/window-content-registry.ts +4 -5
- package/src/web/components/floating-window/window-geometry.ts +18 -0
- package/src/web/components/floating-window/window-persistence.ts +5 -4
- package/src/web/components/floating-window/window-store-types.ts +1 -1
- package/src/web/components/settings/resolve-open-settings-action.ts +1 -1
- package/src/web/components/system/resolve-open-system-monitor-action.ts +1 -1
- package/src/web/hooks/use-agent-activity.ts +65 -0
- package/src/web/hooks/use-agent-session-stream.ts +134 -0
- package/src/web/hooks/use-chat.ts +12 -18
- package/src/web/hooks/use-global-events.ts +35 -2
- package/src/web/lib/agent-session-stream-merge.ts +48 -0
- package/src/web/lib/agent-step-summary.ts +331 -0
- package/src/web/lib/global-ws-channel.ts +41 -0
- package/src/web/lib/running-agent-rows.ts +115 -0
- package/dist/web/assets/accounts-settings-section-BNcPvEq1.js +0 -1
- package/dist/web/assets/accounts-settings-section-BNcPvEq1.js.br +0 -0
- package/dist/web/assets/accounts-settings-section-BNcPvEq1.js.gz +0 -0
- package/dist/web/assets/ai-resource-editor-DfVRMUHS.js.br +0 -0
- package/dist/web/assets/ai-resource-editor-DfVRMUHS.js.gz +0 -0
- package/dist/web/assets/ai-settings-section-B_0-P8jx.js +0 -1
- package/dist/web/assets/ai-settings-section-Ezhw4Fef.js.br +0 -0
- package/dist/web/assets/ai-settings-section-Ezhw4Fef.js.gz +0 -0
- package/dist/web/assets/appearance-settings-section-CPOAp0fY.js.br +0 -0
- package/dist/web/assets/appearance-settings-section-CPOAp0fY.js.gz +0 -0
- package/dist/web/assets/apps-panel-Cz7_o-M4.js.br +0 -0
- package/dist/web/assets/apps-panel-Cz7_o-M4.js.gz +0 -0
- package/dist/web/assets/branch-review-tab-BPx3kHrM.js +0 -1
- package/dist/web/assets/branch-review-tab-BPx3kHrM.js.br +0 -0
- package/dist/web/assets/branch-review-tab-BPx3kHrM.js.gz +0 -0
- package/dist/web/assets/chat-tab-BadDM_j8.js +0 -1
- package/dist/web/assets/chat-tab-BadDM_j8.js.br +0 -0
- package/dist/web/assets/chat-tab-BadDM_j8.js.gz +0 -0
- package/dist/web/assets/chat-tab-IqAY4c7p.js +0 -15
- package/dist/web/assets/chat-tab-IqAY4c7p.js.br +0 -0
- package/dist/web/assets/chat-tab-IqAY4c7p.js.gz +0 -0
- package/dist/web/assets/code-editor-Dmitw9QS.js +0 -7
- package/dist/web/assets/code-editor-Dmitw9QS.js.br +0 -0
- package/dist/web/assets/code-editor-Dmitw9QS.js.gz +0 -0
- package/dist/web/assets/codex-reset-credit-button-D2hal4bG.js.br +0 -0
- package/dist/web/assets/codex-reset-credit-button-D2hal4bG.js.gz +0 -0
- package/dist/web/assets/conflict-editor-Cj_gUncw.js.br +0 -0
- package/dist/web/assets/conflict-editor-Cj_gUncw.js.gz +0 -0
- package/dist/web/assets/csv-preview-Be3ODNj0.js.br +0 -0
- package/dist/web/assets/csv-preview-Be3ODNj0.js.gz +0 -0
- package/dist/web/assets/database-viewer-B1b--4y8.js.br +0 -0
- package/dist/web/assets/database-viewer-B1b--4y8.js.gz +0 -0
- package/dist/web/assets/design-tab-8wi0IHss.js.br +0 -0
- package/dist/web/assets/design-tab-8wi0IHss.js.gz +0 -0
- package/dist/web/assets/designs-sidebar-panel-D5-1Q8zQ.js.br +0 -0
- package/dist/web/assets/designs-sidebar-panel-D5-1Q8zQ.js.gz +0 -0
- package/dist/web/assets/diff-viewer-Dcxk6UTG.js.br +0 -0
- package/dist/web/assets/diff-viewer-Dcxk6UTG.js.gz +0 -0
- package/dist/web/assets/edit-diff-preview-DxDAtYhi.js.br +0 -0
- package/dist/web/assets/editor-language-service-BVJQnTSV.js.br +0 -0
- package/dist/web/assets/editor-language-service-BVJQnTSV.js.gz +0 -0
- package/dist/web/assets/explorer-body-CTkrlrI3.js +0 -2
- package/dist/web/assets/explorer-body-CTkrlrI3.js.br +0 -0
- package/dist/web/assets/explorer-body-CTkrlrI3.js.gz +0 -0
- package/dist/web/assets/explorer-window-content-DH5AD3Ng.js.br +0 -0
- package/dist/web/assets/explorer-window-content-DH5AD3Ng.js.gz +0 -0
- package/dist/web/assets/extension-manager-section-BJr6ccWw.js.br +0 -0
- package/dist/web/assets/extension-manager-section-BJr6ccWw.js.gz +0 -0
- package/dist/web/assets/extension-webview-CYNXHd8D.js.br +0 -0
- package/dist/web/assets/extension-webview-CYNXHd8D.js.gz +0 -0
- package/dist/web/assets/files-settings-section-JX7o4lex.js.br +0 -0
- package/dist/web/assets/files-settings-section-JX7o4lex.js.gz +0 -0
- package/dist/web/assets/general-settings-section-kF94Mz5q.js.br +0 -0
- package/dist/web/assets/general-settings-section-kF94Mz5q.js.gz +0 -0
- package/dist/web/assets/git-log-panel-Crlp9ofM.js.br +0 -0
- package/dist/web/assets/git-log-panel-Crlp9ofM.js.gz +0 -0
- package/dist/web/assets/glide-data-grid-O6MO5dKU.js.br +0 -0
- package/dist/web/assets/glide-data-grid-O6MO5dKU.js.gz +0 -0
- package/dist/web/assets/group-chat-tab-DGLSGf37.js +0 -1
- package/dist/web/assets/group-chat-tab-DGLSGf37.js.br +0 -0
- package/dist/web/assets/group-chat-tab-DGLSGf37.js.gz +0 -0
- package/dist/web/assets/index-DQCw6d-2.js.br +0 -0
- package/dist/web/assets/index-DQCw6d-2.js.gz +0 -0
- package/dist/web/assets/keybindings-store-CJtEHFIQ.js +0 -1
- package/dist/web/assets/keyboard-shortcuts-section-CewWGwiF.js.br +0 -0
- package/dist/web/assets/keyboard-shortcuts-section-CewWGwiF.js.gz +0 -0
- package/dist/web/assets/language-servers-section-nricdxke.js.br +0 -0
- package/dist/web/assets/language-servers-section-nricdxke.js.gz +0 -0
- package/dist/web/assets/lsp-client-DRMEWb-z.js.br +0 -0
- package/dist/web/assets/lsp-client-DRMEWb-z.js.gz +0 -0
- package/dist/web/assets/markdown-renderer-D5-xliE9.js +0 -1
- package/dist/web/assets/markdown-renderer-Dab9Us6u.js.br +0 -0
- package/dist/web/assets/markdown-renderer-Dab9Us6u.js.gz +0 -0
- package/dist/web/assets/mobile-explorer-sheet-DtgdpvJ9.js.br +0 -0
- package/dist/web/assets/mobile-explorer-sheet-DtgdpvJ9.js.gz +0 -0
- package/dist/web/assets/new-design-dialog-Dd5KDB_p.js.br +0 -0
- package/dist/web/assets/new-design-dialog-Dd5KDB_p.js.gz +0 -0
- package/dist/web/assets/notification-store-D2vTyp_Q.js +0 -1
- package/dist/web/assets/panel-store-DrSH3Nhp.js.br +0 -0
- package/dist/web/assets/panel-store-DrSH3Nhp.js.gz +0 -0
- package/dist/web/assets/performance-panel-LMGefOQ2.js +0 -3
- package/dist/web/assets/performance-panel-LMGefOQ2.js.br +0 -0
- package/dist/web/assets/performance-panel-LMGefOQ2.js.gz +0 -0
- package/dist/web/assets/ppmbot-settings-section-D67u6gPi.js.br +0 -0
- package/dist/web/assets/ppmbot-settings-section-D67u6gPi.js.gz +0 -0
- package/dist/web/assets/problems-panel-DKgIw4uZ.js.br +0 -0
- package/dist/web/assets/problems-panel-DKgIw4uZ.js.gz +0 -0
- package/dist/web/assets/process-table-BIAgC_jG.js +0 -1
- package/dist/web/assets/process-table-BIAgC_jG.js.br +0 -0
- package/dist/web/assets/process-table-BIAgC_jG.js.gz +0 -0
- package/dist/web/assets/proxy-settings-section-CFiYcTBA.js.br +0 -0
- package/dist/web/assets/proxy-settings-section-CFiYcTBA.js.gz +0 -0
- package/dist/web/assets/query-audit-section-BybgiPsc.js.br +0 -0
- package/dist/web/assets/query-audit-section-BybgiPsc.js.gz +0 -0
- package/dist/web/assets/remote-desktop-clipboard-notice-Bs3fZSRw.js.br +0 -0
- package/dist/web/assets/remote-desktop-clipboard-notice-Bs3fZSRw.js.gz +0 -0
- package/dist/web/assets/remote-desktop-mobile-sheet-DgxTdWMh.js +0 -2
- package/dist/web/assets/remote-desktop-mobile-sheet-DgxTdWMh.js.br +0 -0
- package/dist/web/assets/remote-desktop-mobile-sheet-DgxTdWMh.js.gz +0 -0
- package/dist/web/assets/remote-desktop-mobile-view-DgGOfPVY.js.br +0 -0
- package/dist/web/assets/remote-desktop-mobile-view-DgGOfPVY.js.gz +0 -0
- package/dist/web/assets/remote-desktop-readiness-gate-DOWryrQx.js.br +0 -0
- package/dist/web/assets/remote-desktop-readiness-gate-DOWryrQx.js.gz +0 -0
- package/dist/web/assets/remote-desktop-window-content-7t0wQsHx.js +0 -1
- package/dist/web/assets/remote-desktop-window-content-7t0wQsHx.js.br +0 -0
- package/dist/web/assets/remote-desktop-window-content-7t0wQsHx.js.gz +0 -0
- package/dist/web/assets/schedules-settings-section-DGhG-b5O.js.br +0 -0
- package/dist/web/assets/schedules-settings-section-DGhG-b5O.js.gz +0 -0
- package/dist/web/assets/send-to-chat-B09VjsB5.js.br +0 -0
- package/dist/web/assets/send-to-chat-B09VjsB5.js.gz +0 -0
- package/dist/web/assets/services-panel-0klE3O4g.js.br +0 -0
- package/dist/web/assets/services-panel-0klE3O4g.js.gz +0 -0
- package/dist/web/assets/settings-body-CfqpqFfq.js.br +0 -0
- package/dist/web/assets/settings-body-CfqpqFfq.js.gz +0 -0
- package/dist/web/assets/settings-store-3jIBB8gx.js.br +0 -0
- package/dist/web/assets/settings-store-3jIBB8gx.js.gz +0 -0
- package/dist/web/assets/sql-query-editor-ALQ5_X0j.js.br +0 -0
- package/dist/web/assets/sql-query-editor-ALQ5_X0j.js.gz +0 -0
- package/dist/web/assets/sqlite-viewer-DKTty_jR.js.br +0 -0
- package/dist/web/assets/sqlite-viewer-DKTty_jR.js.gz +0 -0
- package/dist/web/assets/system-monitor-body-D6J6lq7-.js.br +0 -0
- package/dist/web/assets/system-monitor-body-D6J6lq7-.js.gz +0 -0
- package/dist/web/assets/system-monitor-tab-Di18zNNS.js +0 -1
- package/dist/web/assets/tab-host-window-content-DTcx2D0O.js.br +0 -0
- package/dist/web/assets/tab-host-window-content-DTcx2D0O.js.gz +0 -0
- package/dist/web/assets/team-member-sheet-CFSbPG6n.js +0 -2
- package/dist/web/assets/team-member-sheet-CFSbPG6n.js.br +0 -0
- package/dist/web/assets/team-member-sheet-CFSbPG6n.js.gz +0 -0
- package/dist/web/assets/team-member-window-content-VeI6DGeM.js +0 -1
- package/dist/web/assets/team-member-window-content-VeI6DGeM.js.br +0 -0
- package/dist/web/assets/team-member-window-content-VeI6DGeM.js.gz +0 -0
- package/dist/web/assets/terminal-tab-DgTfQKBI.js.br +0 -0
- package/dist/web/assets/terminal-tab-DgTfQKBI.js.gz +0 -0
- package/dist/web/assets/tool-cards-_fpYI1hB.js +0 -9
- package/dist/web/assets/tool-cards-_fpYI1hB.js.br +0 -0
- package/dist/web/assets/tool-cards-_fpYI1hB.js.gz +0 -0
- package/dist/web/assets/use-inline-blame-Dv111BKY.js.br +0 -0
- package/dist/web/assets/use-inline-blame-Dv111BKY.js.gz +0 -0
- package/dist/web/assets/use-lsp-CZTFJIXX.js.br +0 -0
- package/dist/web/assets/use-lsp-CZTFJIXX.js.gz +0 -0
- package/dist/web/assets/use-open-team-member-ElN3KmCI.js +0 -1
- package/dist/web/assets/use-process-kill-D7D9QvJG.js.br +0 -0
- package/dist/web/assets/use-process-kill-D7D9QvJG.js.gz +0 -0
- package/dist/web/assets/video-preview-DzwY3q9W.js.br +0 -0
- package/dist/web/assets/video-preview-DzwY3q9W.js.gz +0 -0
- package/dist/web/assets/voice-settings-section-B7zMHsuz.js.br +0 -0
- package/dist/web/assets/voice-settings-section-B7zMHsuz.js.gz +0 -0
- package/dist/web/assets/window-store-B4cbZ5rr.js +0 -1
- package/dist/web/assets/window-store-B4cbZ5rr.js.br +0 -0
- package/dist/web/assets/window-store-B4cbZ5rr.js.gz +0 -0
- package/src/web/components/chat/team-member-window-content.tsx +0 -66
- package/src/web/components/chat/team-working-bar.tsx +0 -62
- package/src/web/components/chat/use-open-team-member.ts +0 -41
- package/src/web/hooks/use-member-transcript-tail.ts +0 -95
- /package/dist/web/assets/{api-settings-BeINN5Dy.js → api-settings-BWgfXl7o.js} +0 -0
- /package/dist/web/assets/{api-settings-BeINN5Dy.js.br → api-settings-BWgfXl7o.js.br} +0 -0
- /package/dist/web/assets/{api-settings-BeINN5Dy.js.gz → api-settings-BWgfXl7o.js.gz} +0 -0
- /package/dist/web/assets/{libesm-CxeQSM3t.js → libesm-DAF5_F99.js} +0 -0
- /package/dist/web/assets/{libesm-CxeQSM3t.js.br → libesm-DAF5_F99.js.br} +0 -0
- /package/dist/web/assets/{libesm-CxeQSM3t.js.gz → libesm-DAF5_F99.js.gz} +0 -0
- /package/dist/web/assets/{onboarding-entry-DD3QSDiz.js → onboarding-entry-Bh-WE3vq.js} +0 -0
- /package/dist/web/assets/{onboarding-entry-DD3QSDiz.js.br → onboarding-entry-Bh-WE3vq.js.br} +0 -0
- /package/dist/web/assets/{onboarding-entry-DD3QSDiz.js.gz → onboarding-entry-Bh-WE3vq.js.gz} +0 -0
|
@@ -42,6 +42,9 @@ export const Z_BASE = 30;
|
|
|
42
42
|
|
|
43
43
|
const PREFERRED = { w: 880, h: 560 } as const;
|
|
44
44
|
const SPAWN_MARGIN = 48;
|
|
45
|
+
/** Cap on a portrait window's height — a 9:16 window this tall already shows a full step
|
|
46
|
+
* list; taller than that just wastes space on an ultrawide monitor. */
|
|
47
|
+
const PORTRAIT_MAX_H = 780;
|
|
45
48
|
|
|
46
49
|
const clamp = (v: number, lo: number, hi: number) => Math.min(hi, Math.max(lo, v));
|
|
47
50
|
|
|
@@ -115,6 +118,21 @@ export function cascadeSpawnRect(existing: Rect[], bounds: Bounds): Rect {
|
|
|
115
118
|
);
|
|
116
119
|
}
|
|
117
120
|
|
|
121
|
+
/**
|
|
122
|
+
* Where an agent/teammate session window spawns: a portrait 9:16 rect pinned to the
|
|
123
|
+
* top-right, tall enough to read a step list without scrolling every other line, cascading
|
|
124
|
+
* like any other spawn once more than one is open.
|
|
125
|
+
*/
|
|
126
|
+
export function portraitSpawnRect(existing: Rect[], bounds: Bounds): Rect {
|
|
127
|
+
const h = Math.min(bounds.h - SPAWN_MARGIN * 2, PORTRAIT_MAX_H);
|
|
128
|
+
const w = Math.max(MIN_SIZE.w, Math.round((h * 9) / 16));
|
|
129
|
+
const step = existing.length % MAX_WINDOWS;
|
|
130
|
+
return clampRect(
|
|
131
|
+
{ x: bounds.w - SPAWN_MARGIN - w - step * CASCADE_STEP, y: SPAWN_MARGIN + step * CASCADE_STEP, w, h },
|
|
132
|
+
bounds,
|
|
133
|
+
);
|
|
134
|
+
}
|
|
135
|
+
|
|
118
136
|
/** Nudge step for arrow keys on a focused titlebar (Shift = fine-grained). */
|
|
119
137
|
export function nudgeRect(rect: Rect, dx: number, dy: number, bounds: Bounds): Rect {
|
|
120
138
|
return clampRect({ ...rect, x: rect.x + dx, y: rect.y + dy }, bounds);
|
|
@@ -23,11 +23,12 @@ export interface PersistedWindow {
|
|
|
23
23
|
|
|
24
24
|
/**
|
|
25
25
|
* Kinds that may come back after a reload. Derived from WINDOW_KINDS so a new kind is
|
|
26
|
-
* restorable by default; `
|
|
27
|
-
* live
|
|
28
|
-
* an empty shell the user never asked for.
|
|
26
|
+
* restorable by default; `agent-session` is the one exclusion, because its body streams a
|
|
27
|
+
* live agent/teammate transcript that no longer exists after a reload — restoring it would
|
|
28
|
+
* open an empty shell the user never asked for. A persisted blob from before the kind was
|
|
29
|
+
* renamed (`team-member`) simply matches nothing here and is dropped the same way.
|
|
29
30
|
*/
|
|
30
|
-
const RESTORABLE_KINDS: readonly WindowKind[] = WINDOW_KINDS.filter((k) => k !== "
|
|
31
|
+
const RESTORABLE_KINDS: readonly WindowKind[] = WINDOW_KINDS.filter((k) => k !== "agent-session");
|
|
31
32
|
const STATES: WindowVisualState[] = ["normal", "maximized", "minimized"];
|
|
32
33
|
|
|
33
34
|
function isRect(v: unknown): v is Rect {
|
|
@@ -7,7 +7,7 @@ import type { Rect } from "./window-geometry";
|
|
|
7
7
|
* Single source of truth: the persistence layer filters this list instead of repeating it,
|
|
8
8
|
* so a new kind can never be silently dropped on reload.
|
|
9
9
|
*/
|
|
10
|
-
export const WINDOW_KINDS = ["explorer", "
|
|
10
|
+
export const WINDOW_KINDS = ["explorer", "agent-session", "system-monitor", "tab-host", "remote-desktop", "settings"] as const;
|
|
11
11
|
|
|
12
12
|
export type WindowKind = (typeof WINDOW_KINDS)[number];
|
|
13
13
|
|
|
@@ -10,7 +10,7 @@ export type OpenSettingsAction =
|
|
|
10
10
|
| { kind: "focus"; id: string; category?: SettingsCategoryId };
|
|
11
11
|
|
|
12
12
|
/**
|
|
13
|
-
* There is exactly one set of settings, so unlike `"explorer"`/`"
|
|
13
|
+
* There is exactly one set of settings, so unlike `"explorer"`/`"agent-session"` (legitimately
|
|
14
14
|
* multi-instance — different payload, different folder/teammate), a second `"settings"`
|
|
15
15
|
* window is never a distinct instance, only a duplicate. When one is already open, focus it
|
|
16
16
|
* instead of spawning another.
|
|
@@ -9,7 +9,7 @@ export type OpenSystemMonitorAction =
|
|
|
9
9
|
| { kind: "focus"; id: string };
|
|
10
10
|
|
|
11
11
|
/**
|
|
12
|
-
* There is exactly one machine, so unlike `"explorer"`/`"
|
|
12
|
+
* There is exactly one machine, so unlike `"explorer"`/`"agent-session"` (legitimately
|
|
13
13
|
* multi-instance — different payload, different folder/teammate), a second
|
|
14
14
|
* `"system-monitor"` window is never a distinct instance, only a duplicate. When one
|
|
15
15
|
* is already open, focus it instead of spawning another.
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* "Who is running right now" for one session, from the transcript hub's liveness
|
|
3
|
+
* feed on `/ws/global` (the `agent-activity` push).
|
|
4
|
+
*
|
|
5
|
+
* Mirrors `use-agent-session-stream.ts`'s subscribe contract exactly: a
|
|
6
|
+
* subscription is never queued while the socket is down (`sendIfOpen` drops it
|
|
7
|
+
* instead — a queued subscribe would flush late, against a state the caller has
|
|
8
|
+
* already moved past), and `onGlobalReady` (fired on every connect and every
|
|
9
|
+
* reconnect) is what actually (re)subscribes. There is no cursor to resume from
|
|
10
|
+
* here — each push is a full snapshot of who is running, not a page of steps —
|
|
11
|
+
* so a resubscribe is just the same message again.
|
|
12
|
+
*/
|
|
13
|
+
import { useEffect, useState } from "react";
|
|
14
|
+
import { onGlobalReady, sendIfOpen } from "@/lib/global-ws-channel";
|
|
15
|
+
import type {
|
|
16
|
+
AgentActivityMsg,
|
|
17
|
+
AgentTranscriptProviderId,
|
|
18
|
+
AgentTranscriptRunningEntry,
|
|
19
|
+
} from "../../shared/agent-transcript-protocol";
|
|
20
|
+
|
|
21
|
+
export interface UseAgentActivityOptions {
|
|
22
|
+
projectName: string;
|
|
23
|
+
providerId: AgentTranscriptProviderId;
|
|
24
|
+
/** Empty/absent — a draft chat with no session yet — subscribes to nothing. */
|
|
25
|
+
sessionId: string | null | undefined;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function newSubId(): string {
|
|
29
|
+
return `aa-${Math.random().toString(36).slice(2, 10)}`;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export function useAgentActivity(opts: UseAgentActivityOptions): AgentTranscriptRunningEntry[] {
|
|
33
|
+
const { projectName, providerId, sessionId } = opts;
|
|
34
|
+
const [running, setRunning] = useState<AgentTranscriptRunningEntry[]>([]);
|
|
35
|
+
|
|
36
|
+
useEffect(() => {
|
|
37
|
+
setRunning([]);
|
|
38
|
+
if (!sessionId || !projectName) return;
|
|
39
|
+
const subId = newSubId();
|
|
40
|
+
|
|
41
|
+
const subscribe = () => {
|
|
42
|
+
sendIfOpen(JSON.stringify({ type: "agent-activity:subscribe", subId, projectName, providerId, sessionId }));
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
const onActivity = (e: Event) => {
|
|
46
|
+
const data = (e as CustomEvent<AgentActivityMsg>).detail;
|
|
47
|
+
if (data.subId !== subId) return;
|
|
48
|
+
setRunning(data.running);
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
window.addEventListener("agent-activity", onActivity);
|
|
52
|
+
const stopResubscribing = onGlobalReady(subscribe);
|
|
53
|
+
subscribe();
|
|
54
|
+
|
|
55
|
+
return () => {
|
|
56
|
+
window.removeEventListener("agent-activity", onActivity);
|
|
57
|
+
stopResubscribing();
|
|
58
|
+
sendIfOpen(JSON.stringify({ type: "agent-activity:unsubscribe", subId }));
|
|
59
|
+
};
|
|
60
|
+
// projectName/providerId/sessionId are every real dependency.
|
|
61
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
62
|
+
}, [projectName, providerId, sessionId]);
|
|
63
|
+
|
|
64
|
+
return running;
|
|
65
|
+
}
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Follow one Agent card's or teammate's work session live, from the transcript hub on
|
|
3
|
+
* `/ws/global`.
|
|
4
|
+
*
|
|
5
|
+
* Generalises the old per-teammate HTTP poll (`use-member-transcript-tail.ts`) to the hub
|
|
6
|
+
* protocol: a subscription is never queued while the socket is down — `sendIfOpen` drops it
|
|
7
|
+
* instead, because a queued subscribe would replay later with a cursor that is already
|
|
8
|
+
* stale by the time the flush happens. `onGlobalReady` (fired on every connect and every
|
|
9
|
+
* reconnect) is what actually (re)subscribes, using the cursor the last response handed
|
|
10
|
+
* back, so a reconnect resumes with no gap and no duplicate instead of going quiet.
|
|
11
|
+
*/
|
|
12
|
+
import { useEffect, useMemo, useState } from "react";
|
|
13
|
+
import { onGlobalReady, sendIfOpen } from "@/lib/global-ws-channel";
|
|
14
|
+
import { applyEnvelopeBatch, type StreamEntry } from "@/lib/agent-session-stream-merge";
|
|
15
|
+
import type {
|
|
16
|
+
AgentTranscriptCursor,
|
|
17
|
+
AgentTranscriptErrorCode,
|
|
18
|
+
AgentTranscriptErrorMsg,
|
|
19
|
+
AgentTranscriptEventsMsg,
|
|
20
|
+
AgentTranscriptProviderId,
|
|
21
|
+
AgentTranscriptSourceKind,
|
|
22
|
+
} from "../../shared/agent-transcript-protocol";
|
|
23
|
+
import type { ChatEvent } from "../../types/chat";
|
|
24
|
+
|
|
25
|
+
export interface UseAgentSessionStreamOptions {
|
|
26
|
+
projectName: string;
|
|
27
|
+
providerId: AgentTranscriptProviderId;
|
|
28
|
+
sessionId: string;
|
|
29
|
+
source: AgentTranscriptSourceKind;
|
|
30
|
+
/** Steps already held in chat memory — shown while the transcript stream is unavailable. */
|
|
31
|
+
fallbackEvents?: ChatEvent[];
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export interface UseAgentSessionStream {
|
|
35
|
+
events: ChatEvent[];
|
|
36
|
+
/** True only once the server has answered with at least one readable file for this
|
|
37
|
+
* source. False before the first response, and after one that found no transcript (old
|
|
38
|
+
* session, never had one, or an error) — `events` falls back to `fallbackEvents` both
|
|
39
|
+
* times, so a window shows what is already known rather than a blank list while the
|
|
40
|
+
* first page is still in flight. */
|
|
41
|
+
available: boolean;
|
|
42
|
+
running: boolean;
|
|
43
|
+
loading: boolean;
|
|
44
|
+
error: AgentTranscriptErrorCode | null;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function newSubId(): string {
|
|
48
|
+
return `ats-${Math.random().toString(36).slice(2, 10)}`;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export function useAgentSessionStream(opts: UseAgentSessionStreamOptions): UseAgentSessionStream {
|
|
52
|
+
const { projectName, providerId, sessionId, source, fallbackEvents } = opts;
|
|
53
|
+
const [entries, setEntries] = useState<StreamEntry[]>([]);
|
|
54
|
+
// False until the first response says otherwise, so a window opened with fallback events
|
|
55
|
+
// shows them immediately instead of a blank list while the first page is in flight.
|
|
56
|
+
const [available, setAvailable] = useState(false);
|
|
57
|
+
const [running, setRunning] = useState(false);
|
|
58
|
+
const [loading, setLoading] = useState(true);
|
|
59
|
+
const [error, setError] = useState<AgentTranscriptErrorCode | null>(null);
|
|
60
|
+
|
|
61
|
+
// Any of these changing is a different subscription, not a resume of the same one —
|
|
62
|
+
// JSON-stringified so the effect only re-runs when the actual identity changes, not on
|
|
63
|
+
// every render a caller passes a fresh `source` object literal.
|
|
64
|
+
const sourceKey = JSON.stringify([projectName, providerId, sessionId, source]);
|
|
65
|
+
|
|
66
|
+
useEffect(() => {
|
|
67
|
+
const subId = newSubId();
|
|
68
|
+
let entriesBuf: StreamEntry[] = [];
|
|
69
|
+
let cursor: AgentTranscriptCursor | undefined;
|
|
70
|
+
setEntries([]);
|
|
71
|
+
setAvailable(false);
|
|
72
|
+
setRunning(false);
|
|
73
|
+
setLoading(true);
|
|
74
|
+
setError(null);
|
|
75
|
+
|
|
76
|
+
const subscribe = () => {
|
|
77
|
+
sendIfOpen(
|
|
78
|
+
JSON.stringify({
|
|
79
|
+
type: "agent-transcript:subscribe",
|
|
80
|
+
subId,
|
|
81
|
+
projectName,
|
|
82
|
+
providerId,
|
|
83
|
+
sessionId,
|
|
84
|
+
source,
|
|
85
|
+
...(cursor ? { cursor } : {}),
|
|
86
|
+
}),
|
|
87
|
+
);
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
const onEvents = (e: Event) => {
|
|
91
|
+
const data = (e as CustomEvent<AgentTranscriptEventsMsg>).detail;
|
|
92
|
+
if (data.subId !== subId) return;
|
|
93
|
+
entriesBuf = applyEnvelopeBatch(entriesBuf, data);
|
|
94
|
+
cursor = data.cursor;
|
|
95
|
+
setEntries(entriesBuf);
|
|
96
|
+
setAvailable(data.available);
|
|
97
|
+
setRunning(data.running);
|
|
98
|
+
setLoading(false);
|
|
99
|
+
setError(null);
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
const onError = (e: Event) => {
|
|
103
|
+
const data = (e as CustomEvent<AgentTranscriptErrorMsg>).detail;
|
|
104
|
+
if (data.subId !== subId) return;
|
|
105
|
+
setAvailable(false);
|
|
106
|
+
setLoading(false);
|
|
107
|
+
setError(data.code);
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
window.addEventListener("agent-transcript:events", onEvents);
|
|
111
|
+
window.addEventListener("agent-transcript:error", onError);
|
|
112
|
+
const stopResubscribing = onGlobalReady(subscribe);
|
|
113
|
+
subscribe();
|
|
114
|
+
|
|
115
|
+
return () => {
|
|
116
|
+
window.removeEventListener("agent-transcript:events", onEvents);
|
|
117
|
+
window.removeEventListener("agent-transcript:error", onError);
|
|
118
|
+
stopResubscribing();
|
|
119
|
+
sendIfOpen(JSON.stringify({ type: "agent-transcript:unsubscribe", subId }));
|
|
120
|
+
};
|
|
121
|
+
// sourceKey captures every real dependency (projectName/providerId/sessionId/source).
|
|
122
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
123
|
+
}, [sourceKey]);
|
|
124
|
+
|
|
125
|
+
const events = useMemo(() => entries.map((e) => e.ev), [entries]);
|
|
126
|
+
|
|
127
|
+
return {
|
|
128
|
+
events: available ? events : (fallbackEvents ?? []),
|
|
129
|
+
available,
|
|
130
|
+
running,
|
|
131
|
+
loading,
|
|
132
|
+
error,
|
|
133
|
+
};
|
|
134
|
+
}
|
|
@@ -16,6 +16,13 @@ import type { ChatWsServerMessage, SessionPhase, BackgroundShell, VersionGroup }
|
|
|
16
16
|
import { useBackgroundOutputStore } from "../stores/background-output-store";
|
|
17
17
|
import { useSessionListStore } from "@/stores/session-list-store";
|
|
18
18
|
import { projectRefForName } from "@/stores/session-list-sync-triggers";
|
|
19
|
+
import { applyChildToParent, slimHistoryEvents } from "@/lib/agent-step-summary";
|
|
20
|
+
|
|
21
|
+
/** Slim every Agent/Task card a provider stamped `transcriptAvailable` on, walking
|
|
22
|
+
* in from the REST history response before it ever reaches React state. */
|
|
23
|
+
function slimHistoryMessages(messages: ChatMessage[]): ChatMessage[] {
|
|
24
|
+
return messages.map((m) => (m.events ? { ...m, events: slimHistoryEvents(m.events) } : m));
|
|
25
|
+
}
|
|
19
26
|
|
|
20
27
|
interface ApprovalRequest {
|
|
21
28
|
requestId: string;
|
|
@@ -316,12 +323,8 @@ export function useChat(
|
|
|
316
323
|
const event = events[i]!;
|
|
317
324
|
if (event.type !== "tool_use") continue;
|
|
318
325
|
if ((event.tool === "Agent" || event.tool === "Task") && (event as any).toolUseId === parentToolUseId) {
|
|
319
|
-
const
|
|
320
|
-
|
|
321
|
-
const duplicate = id ? children.findIndex((c) => c.type === childEvent.type && (c as any).toolUseId === id) : -1;
|
|
322
|
-
if (duplicate === -1) children.push(childEvent);
|
|
323
|
-
else children[duplicate] = childEvent;
|
|
324
|
-
return [[...events.slice(0, i), { ...event, children }, ...events.slice(i + 1)], true];
|
|
326
|
+
const updated = applyChildToParent(event, childEvent);
|
|
327
|
+
return [[...events.slice(0, i), updated, ...events.slice(i + 1)], true];
|
|
325
328
|
}
|
|
326
329
|
if (event.children?.length) {
|
|
327
330
|
const [children, found] = append(event.children);
|
|
@@ -360,16 +363,7 @@ export function useChat(
|
|
|
360
363
|
}
|
|
361
364
|
if (idx === -1) return prev;
|
|
362
365
|
const msg = prev[idx]!;
|
|
363
|
-
const events = msg.events!.map((e) =>
|
|
364
|
-
if (!isParent(e) || e.type !== "tool_use") return e;
|
|
365
|
-
const children = [...(e.children ?? [])];
|
|
366
|
-
// Replays can redeliver id-bearing children — upsert instead of duplicating
|
|
367
|
-
const cid = (childEvent as any).toolUseId as string | undefined;
|
|
368
|
-
const dup = cid ? children.findIndex((c) => c.type === childEvent.type && (c as any).toolUseId === cid) : -1;
|
|
369
|
-
if (dup !== -1) children[dup] = childEvent;
|
|
370
|
-
else children.push(childEvent);
|
|
371
|
-
return { ...e, children };
|
|
372
|
-
});
|
|
366
|
+
const events = msg.events!.map((e) => (isParent(e) ? applyChildToParent(e, childEvent) : e));
|
|
373
367
|
return [...prev.slice(0, idx), { ...msg, events }, ...prev.slice(idx + 1)];
|
|
374
368
|
});
|
|
375
369
|
return true;
|
|
@@ -1237,7 +1231,7 @@ export function useChat(
|
|
|
1237
1231
|
// cached bundle from before the upgrade would otherwise render an empty
|
|
1238
1232
|
// history with no error.
|
|
1239
1233
|
const payload = Array.isArray(data) ? { messages: data, versionMap: {} } : data;
|
|
1240
|
-
let history: ChatMessage[] = Array.isArray(payload?.messages) ? payload.messages : [];
|
|
1234
|
+
let history: ChatMessage[] = slimHistoryMessages(Array.isArray(payload?.messages) ? payload.messages : []);
|
|
1241
1235
|
if (payload?.versionMap) setVersionMap(payload.versionMap);
|
|
1242
1236
|
// The server served this transcript from a different id than the one
|
|
1243
1237
|
// asked for: the provider had renamed the session and this tab kept the
|
|
@@ -1529,7 +1523,7 @@ export function useChat(
|
|
|
1529
1523
|
if (Array.isArray(payload?.messages) && payload.messages.length > 0) {
|
|
1530
1524
|
historyReconciledRef.current++;
|
|
1531
1525
|
if (syncRafRef.current) { clearTimeout(syncRafRef.current); syncRafRef.current = 0; }
|
|
1532
|
-
setMessages(payload.messages);
|
|
1526
|
+
setMessages(slimHistoryMessages(payload.messages));
|
|
1533
1527
|
streamingContentRef.current = "";
|
|
1534
1528
|
streamingEventsRef.current = [];
|
|
1535
1529
|
}
|
|
@@ -5,6 +5,14 @@ import { useNotificationStore } from "@/stores/notification-store";
|
|
|
5
5
|
import { useStreamingStore } from "@/stores/streaming-store";
|
|
6
6
|
import { syncRunningSessions } from "@/lib/sync-running-sessions";
|
|
7
7
|
import { syncAllKnownProjects } from "@/stores/session-list-sync-triggers";
|
|
8
|
+
import { notifyGlobalReady, sendIfOpen, setGlobalWsClient } from "@/lib/global-ws-channel";
|
|
9
|
+
|
|
10
|
+
/** How often the client pings — well under the server's own idle timeout, so a live but
|
|
11
|
+
* silent socket never gets reaped as dead. */
|
|
12
|
+
const PING_INTERVAL_MS = 20_000;
|
|
13
|
+
/** No message (including a pong) for this long means the socket is dead even though it
|
|
14
|
+
* never fired close/error — force a fresh connect rather than waiting on the browser. */
|
|
15
|
+
const IDLE_TIMEOUT_MS = 45_000;
|
|
8
16
|
|
|
9
17
|
/**
|
|
10
18
|
* App-wide event bus client (`/ws/global`).
|
|
@@ -30,8 +38,18 @@ import { syncAllKnownProjects } from "@/stores/session-list-sync-triggers";
|
|
|
30
38
|
* `design:comments_changed`). A design's `.design/` folder is not watched, so these
|
|
31
39
|
* are the only signal that its snapshots or comments changed.
|
|
32
40
|
*
|
|
41
|
+
* - `agent-transcript:events` / `agent-transcript:error` / `agent-activity` → re-dispatched
|
|
42
|
+
* as window events for `useAgentSessionStream` and the running-agents bar. Several of
|
|
43
|
+
* those hooks can be subscribed at once (one per open session window), each filtering the
|
|
44
|
+
* broadcast down to its own `subId` — same pattern as the other re-dispatches here.
|
|
45
|
+
*
|
|
33
46
|
* Also tells the server which project to watch, so file watching follows the
|
|
34
47
|
* active project instead of depending on a chat socket existing.
|
|
48
|
+
*
|
|
49
|
+
* Registers its `WsClient` with `global-ws-channel` and pings every 20s: the hub protocol's
|
|
50
|
+
* subscriptions (`useAgentSessionStream`) must never queue a send while this socket is
|
|
51
|
+
* down, so they need to know the instant it actually goes idle rather than only on the next
|
|
52
|
+
* browser-level close/error.
|
|
35
53
|
*/
|
|
36
54
|
export function useGlobalEvents(enabled: boolean, projectName?: string): void {
|
|
37
55
|
const clientRef = useRef<WsClient | null>(null);
|
|
@@ -44,8 +62,11 @@ export function useGlobalEvents(enabled: boolean, projectName?: string): void {
|
|
|
44
62
|
if (!enabled) return;
|
|
45
63
|
|
|
46
64
|
const token = getAuthToken();
|
|
47
|
-
const client = new WsClient(`/ws/global${token ? `?token=${encodeURIComponent(token)}` : ""}
|
|
65
|
+
const client = new WsClient(`/ws/global${token ? `?token=${encodeURIComponent(token)}` : ""}`, {
|
|
66
|
+
idleTimeoutMs: IDLE_TIMEOUT_MS,
|
|
67
|
+
});
|
|
48
68
|
clientRef.current = client;
|
|
69
|
+
setGlobalWsClient(client);
|
|
49
70
|
|
|
50
71
|
const unsubscribe = client.onMessage((event) => {
|
|
51
72
|
let data: { type?: string; [k: string]: unknown };
|
|
@@ -68,6 +89,7 @@ export function useGlobalEvents(enabled: boolean, projectName?: string): void {
|
|
|
68
89
|
// device renamed, pinned or deleted a session) — re-sync every
|
|
69
90
|
// project this browser already knows about.
|
|
70
91
|
syncAllKnownProjects();
|
|
92
|
+
notifyGlobalReady();
|
|
71
93
|
return;
|
|
72
94
|
}
|
|
73
95
|
|
|
@@ -103,17 +125,28 @@ export function useGlobalEvents(enabled: boolean, projectName?: string): void {
|
|
|
103
125
|
return;
|
|
104
126
|
}
|
|
105
127
|
|
|
106
|
-
if (
|
|
128
|
+
if (
|
|
129
|
+
type.startsWith("jira:")
|
|
130
|
+
|| type.startsWith("tunnel:")
|
|
131
|
+
|| type.startsWith("design:")
|
|
132
|
+
|| type.startsWith("agent-transcript:")
|
|
133
|
+
|| type === "agent-activity"
|
|
134
|
+
) {
|
|
107
135
|
window.dispatchEvent(new CustomEvent(type, { detail: data }));
|
|
108
136
|
}
|
|
109
137
|
});
|
|
110
138
|
|
|
111
139
|
client.connect();
|
|
140
|
+
const pingTimer = setInterval(() => {
|
|
141
|
+
sendIfOpen(JSON.stringify({ type: "ping" }));
|
|
142
|
+
}, PING_INTERVAL_MS);
|
|
112
143
|
|
|
113
144
|
return () => {
|
|
145
|
+
clearInterval(pingTimer);
|
|
114
146
|
unsubscribe();
|
|
115
147
|
client.disconnect();
|
|
116
148
|
clientRef.current = null;
|
|
149
|
+
setGlobalWsClient(null);
|
|
117
150
|
};
|
|
118
151
|
}, [enabled]);
|
|
119
152
|
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure event-buffer reducer for the agent session stream hook.
|
|
3
|
+
*
|
|
4
|
+
* Split out from the hook itself so the ordering/de-dupe/replace/reset/cap rules are
|
|
5
|
+
* testable under `bun:test` with no WebSocket, no React and no store to mount. Two pushes
|
|
6
|
+
* for the same subscription are not guaranteed to arrive in `ts` order (a live tick can
|
|
7
|
+
* land while a catch-up backlog is still draining), and a Codex `replace` updates a step
|
|
8
|
+
* already shown in place rather than appending a new one.
|
|
9
|
+
*/
|
|
10
|
+
import type { AgentTranscriptEventsMsg } from "../../shared/agent-transcript-protocol";
|
|
11
|
+
import type { ChatEvent } from "../../types/chat";
|
|
12
|
+
|
|
13
|
+
/** One accumulated step, kept alongside its de-dupe key and timestamp for re-ordering. */
|
|
14
|
+
export interface StreamEntry {
|
|
15
|
+
k: string;
|
|
16
|
+
ts: number;
|
|
17
|
+
ev: ChatEvent;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/** Mirrors the hub's own per-subscription cap so a long session stays renderable. */
|
|
21
|
+
export const MAX_STREAM_ENTRIES = 2000;
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Apply one `agent-transcript:events` page to the entries accumulated so far.
|
|
25
|
+
*
|
|
26
|
+
* `reset` discards everything first — a truncated/rotated file or a Codex compaction means
|
|
27
|
+
* the server is telling us history itself changed, not just that more of it arrived.
|
|
28
|
+
* Otherwise: a `k` already held is either a `replace` (update in place) or a plain duplicate
|
|
29
|
+
* from an overlapping catch-up/live-tick window (skipped); a new `k` is inserted at the
|
|
30
|
+
* position its `ts` sorts into rather than appended blindly.
|
|
31
|
+
*/
|
|
32
|
+
export function applyEnvelopeBatch(
|
|
33
|
+
current: StreamEntry[],
|
|
34
|
+
batch: AgentTranscriptEventsMsg,
|
|
35
|
+
): StreamEntry[] {
|
|
36
|
+
const next = batch.reset ? [] : current.slice();
|
|
37
|
+
for (const { ev, ts, k, replace } of batch.events) {
|
|
38
|
+
const existingIndex = next.findIndex((e) => e.k === k);
|
|
39
|
+
if (existingIndex !== -1) {
|
|
40
|
+
if (replace) next[existingIndex] = { k, ts, ev };
|
|
41
|
+
continue;
|
|
42
|
+
}
|
|
43
|
+
let insertAt = next.length;
|
|
44
|
+
while (insertAt > 0 && next[insertAt - 1]!.ts > ts) insertAt--;
|
|
45
|
+
next.splice(insertAt, 0, { k, ts, ev });
|
|
46
|
+
}
|
|
47
|
+
return next.length > MAX_STREAM_ENTRIES ? next.slice(next.length - MAX_STREAM_ENTRIES) : next;
|
|
48
|
+
}
|