@oh-my-pi/pi-tui 18.4.2 → 18.4.4
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 +56 -0
- package/dist/types/apps/cleanse-board.d.ts +17 -0
- package/dist/types/apps/git/help.d.ts +16 -0
- package/dist/types/apps/git/sidebar.d.ts +18 -0
- package/dist/types/apps/live-visualizer.d.ts +10 -0
- package/dist/types/apps/ps-top.d.ts +15 -0
- package/dist/types/apps/standalone-picker.d.ts +3 -0
- package/dist/types/autocomplete.d.ts +8 -0
- package/dist/types/chat/advisor-message.d.ts +12 -2
- package/dist/types/chat/assistant-message.d.ts +20 -0
- package/dist/types/chat/bash-execution.d.ts +9 -0
- package/dist/types/chat/chat-transcript-builder.d.ts +6 -1
- package/dist/types/chat/collab-prompt-message.d.ts +4 -0
- package/dist/types/chat/compaction-summary-message.d.ts +15 -0
- package/dist/types/chat/eval-execution.d.ts +8 -0
- package/dist/types/chat/execution-shared.d.ts +58 -0
- package/dist/types/chat/late-diagnostics-message.d.ts +15 -1
- package/dist/types/chat/read-tool-group.d.ts +15 -0
- package/dist/types/chat/skill-message.d.ts +9 -0
- package/dist/types/chat/stripped-tool-calls-placeholder.d.ts +3 -0
- package/dist/types/chat/thinking-display.d.ts +2 -0
- package/dist/types/chat/todo-reminder.d.ts +4 -1
- package/dist/types/chat/tool-execution.d.ts +25 -1
- package/dist/types/chat/transcript-actions.d.ts +37 -0
- package/dist/types/chat/transcript-entry.d.ts +3 -1
- package/dist/types/chat/user-message.d.ts +25 -0
- package/dist/types/chrome/collab-qrcode.d.ts +7 -0
- package/dist/types/chrome/context-thresholds.d.ts +11 -0
- package/dist/types/chrome/countdown-timer.d.ts +9 -3
- package/dist/types/chrome/diff.d.ts +12 -0
- package/dist/types/chrome/dynamic-border.d.ts +3 -0
- package/dist/types/chrome/format.d.ts +6 -0
- package/dist/types/chrome/message-divider.d.ts +13 -0
- package/dist/types/chrome/message-frame.d.ts +10 -0
- package/dist/types/chrome/message-notice.d.ts +23 -0
- package/dist/types/chrome/overlay-box.d.ts +11 -2
- package/dist/types/chrome/qrcode.d.ts +7 -0
- package/dist/types/chrome/segment-track.d.ts +7 -0
- package/dist/types/chrome/selector-helpers.d.ts +0 -6
- package/dist/types/chrome/status-notice.d.ts +14 -0
- package/dist/types/chrome/streaming-panel.d.ts +3 -0
- package/dist/types/chrome/tool-activity.d.ts +3 -0
- package/dist/types/chrome/transcript-container.d.ts +13 -0
- package/dist/types/chrome/transcript-status.d.ts +15 -3
- package/dist/types/components/box.d.ts +7 -0
- package/dist/types/components/disclosure.d.ts +9 -0
- package/dist/types/components/editor.d.ts +42 -0
- package/dist/types/components/form.d.ts +14 -0
- package/dist/types/components/image.d.ts +7 -0
- package/dist/types/components/input.d.ts +20 -1
- package/dist/types/components/key-value-list.d.ts +3 -0
- package/dist/types/components/layout/geometry.d.ts +16 -0
- package/dist/types/components/layout/row.d.ts +7 -0
- package/dist/types/components/layout/split-pane.d.ts +8 -0
- package/dist/types/components/layout/stack.d.ts +6 -0
- package/dist/types/components/loader.d.ts +48 -0
- package/dist/types/components/markdown.d.ts +8 -0
- package/dist/types/components/metric.d.ts +7 -0
- package/dist/types/components/progress-bar.d.ts +3 -0
- package/dist/types/components/scroll-view.d.ts +6 -0
- package/dist/types/components/section.d.ts +3 -0
- package/dist/types/components/select-list.d.ts +33 -0
- package/dist/types/components/settings-list.d.ts +68 -0
- package/dist/types/components/spacer.d.ts +4 -0
- package/dist/types/components/tab-bar.d.ts +9 -0
- package/dist/types/components/table.d.ts +7 -0
- package/dist/types/components/text.d.ts +6 -0
- package/dist/types/components/tree-view.d.ts +9 -0
- package/dist/types/components/truncated-text.d.ts +3 -0
- package/dist/types/components/wizard-step.d.ts +6 -0
- package/dist/types/hotkeys-markdown.d.ts +24 -0
- package/dist/types/index.d.ts +5 -0
- package/dist/types/latex-block.d.ts +7 -0
- package/dist/types/loop-watchdog.d.ts +9 -0
- package/dist/types/native/apply.d.ts +39 -0
- package/dist/types/native/backend.d.ts +94 -0
- package/dist/types/native/blobs.d.ts +29 -0
- package/dist/types/native/describe.d.ts +56 -0
- package/dist/types/native/encode.d.ts +50 -0
- package/dist/types/native/icons.d.ts +18 -0
- package/dist/types/native/memo.d.ts +27 -0
- package/dist/types/native/node.d.ts +107 -0
- package/dist/types/native/overlay.d.ts +64 -0
- package/dist/types/native/picker.d.ts +124 -0
- package/dist/types/native/reconcile.d.ts +41 -0
- package/dist/types/native/settle.d.ts +11 -0
- package/dist/types/native/spans.d.ts +21 -0
- package/dist/types/native/state.d.ts +6 -0
- package/dist/types/native/tone.d.ts +24 -0
- package/dist/types/overlays/advisor-config.d.ts +20 -0
- package/dist/types/overlays/agent-hub-renderer.d.ts +27 -11
- package/dist/types/overlays/agent-hub.d.ts +10 -0
- package/dist/types/overlays/agent-transcript-viewer.d.ts +7 -0
- package/dist/types/overlays/agents-hub.d.ts +8 -0
- package/dist/types/overlays/annotation-overlay.d.ts +3 -0
- package/dist/types/overlays/ask-dialog.d.ts +23 -0
- package/dist/types/overlays/bordered-loader.d.ts +3 -0
- package/dist/types/overlays/btw-history-panel.d.ts +9 -0
- package/dist/types/overlays/btw-panel.d.ts +3 -0
- package/dist/types/overlays/cleanse-panel.d.ts +5 -2
- package/dist/types/overlays/composer-shape-preview.d.ts +7 -1
- package/dist/types/overlays/copy-selector.d.ts +38 -0
- package/dist/types/overlays/error-banner.d.ts +9 -0
- package/dist/types/overlays/extensions/extension-list.d.ts +1 -0
- package/dist/types/overlays/history-search.d.ts +18 -0
- package/dist/types/overlays/hook-editor.d.ts +28 -1
- package/dist/types/overlays/hook-input.d.ts +3 -0
- package/dist/types/overlays/hook-selector.d.ts +10 -0
- package/dist/types/overlays/hub-frame.d.ts +18 -0
- package/dist/types/overlays/jobs-panel.d.ts +24 -0
- package/dist/types/overlays/login-dialog.d.ts +10 -0
- package/dist/types/overlays/logout-account-selector.d.ts +4 -0
- package/dist/types/overlays/mcp-add-wizard.d.ts +4 -0
- package/dist/types/overlays/model-browser.d.ts +69 -0
- package/dist/types/overlays/model-hub.d.ts +7 -0
- package/dist/types/overlays/model-picker.d.ts +9 -1
- package/dist/types/overlays/move-overlay.d.ts +3 -0
- package/dist/types/overlays/oauth-selector.d.ts +4 -0
- package/dist/types/overlays/omfg-panel.d.ts +3 -0
- package/dist/types/overlays/pause-screen.d.ts +12 -0
- package/dist/types/overlays/plan-review-overlay.d.ts +5 -0
- package/dist/types/overlays/plan-save-overlay.d.ts +10 -0
- package/dist/types/overlays/plugin-selector.d.ts +4 -0
- package/dist/types/overlays/plugin-settings.d.ts +30 -0
- package/dist/types/overlays/queue-mode-selector.d.ts +4 -0
- package/dist/types/overlays/reset-usage-selector.d.ts +8 -0
- package/dist/types/overlays/rewind-selector.d.ts +5 -0
- package/dist/types/overlays/session-info-overlay.d.ts +19 -1
- package/dist/types/overlays/session-selector.d.ts +64 -1
- package/dist/types/overlays/settings-defs.d.ts +14 -0
- package/dist/types/overlays/settings-selector.d.ts +21 -0
- package/dist/types/overlays/show-images-selector.d.ts +4 -0
- package/dist/types/overlays/snapcompact-shape-preview.d.ts +2 -0
- package/dist/types/overlays/stats-notice.d.ts +8 -0
- package/dist/types/overlays/theme-selector.d.ts +11 -0
- package/dist/types/overlays/thinking-selector.d.ts +4 -0
- package/dist/types/overlays/tree-selector.d.ts +60 -2
- package/dist/types/overlays/usage-dashboard.d.ts +26 -3
- package/dist/types/overlays/usage-row.d.ts +36 -2
- package/dist/types/prompt/attachment-chips.d.ts +8 -0
- package/dist/types/prompt/composer-attachments.d.ts +15 -3
- package/dist/types/prompt/composer.d.ts +18 -1
- package/dist/types/prompt/custom-editor.d.ts +48 -2
- package/dist/types/prompt/editor-top-gap.d.ts +2 -0
- package/dist/types/prompt/macos-spelling.d.ts +6 -0
- package/dist/types/prompt/magic-keywords.d.ts +9 -0
- package/dist/types/prompt/queued-messages.d.ts +23 -0
- package/dist/types/prompt/skill-tokens.d.ts +4 -2
- package/dist/types/prompt/welcome.d.ts +16 -0
- package/dist/types/render/code-cell.d.ts +10 -0
- package/dist/types/render/file-list.d.ts +8 -0
- package/dist/types/render/hyperlink.d.ts +14 -0
- package/dist/types/render/output-block.d.ts +43 -0
- package/dist/types/render/output-pane.d.ts +17 -0
- package/dist/types/render/tool-card.d.ts +8 -0
- package/dist/types/render/tree-list.d.ts +8 -0
- package/dist/types/render/width-aware-text.d.ts +7 -0
- package/dist/types/setup/scenes/outro.d.ts +7 -0
- package/dist/types/setup/scenes/sign-in.d.ts +16 -9
- package/dist/types/setup/scenes/splash.d.ts +8 -0
- package/dist/types/setup/scenes/types.d.ts +6 -27
- package/dist/types/setup/wizard-overlay.d.ts +9 -0
- package/dist/types/status-line/component.d.ts +49 -5
- package/dist/types/status-line/context-usage.d.ts +20 -0
- package/dist/types/status-line/footer.d.ts +8 -0
- package/dist/types/status-line/metrics.d.ts +1 -2
- package/dist/types/status-line/segments.d.ts +10 -1
- package/dist/types/status-line/types.d.ts +40 -1
- package/dist/types/stdin-buffer.d.ts +10 -7
- package/dist/types/terminal-capabilities.d.ts +1 -1
- package/dist/types/terminal.d.ts +36 -0
- package/dist/types/theme/defaults/index.d.ts +100 -0
- package/dist/types/theme/schema.d.ts +2 -0
- package/dist/types/theme/shimmer.d.ts +8 -0
- package/dist/types/theme/theme-class.d.ts +2 -0
- package/dist/types/theme/theme.d.ts +33 -1
- package/dist/types/tools/ask.d.ts +3 -0
- package/dist/types/tools/ast-edit.d.ts +10 -1
- package/dist/types/tools/ast-grep.d.ts +10 -1
- package/dist/types/tools/autoresearch.d.ts +21 -4
- package/dist/types/tools/bash.d.ts +30 -1
- package/dist/types/tools/cfg-render.d.ts +5 -1
- package/dist/types/tools/debug.d.ts +3 -1
- package/dist/types/tools/default-renderer.d.ts +6 -1
- package/dist/types/tools/edit.d.ts +14 -1
- package/dist/types/tools/eval.d.ts +12 -1
- package/dist/types/tools/fetch.d.ts +16 -1
- package/dist/types/tools/find.d.ts +15 -1
- package/dist/types/tools/github.d.ts +3 -0
- package/dist/types/tools/glob.d.ts +14 -1
- package/dist/types/tools/goal.d.ts +3 -1
- package/dist/types/tools/grep.d.ts +14 -1
- package/dist/types/tools/grouped-file-output.d.ts +58 -0
- package/dist/types/tools/json-tree.d.ts +20 -0
- package/dist/types/tools/lsp.d.ts +3 -10
- package/dist/types/tools/mcp.d.ts +16 -1
- package/dist/types/tools/memory.d.ts +26 -0
- package/dist/types/tools/native-view.d.ts +90 -0
- package/dist/types/tools/proc-render.d.ts +7 -1
- package/dist/types/tools/read.d.ts +23 -1
- package/dist/types/tools/renderer.d.ts +57 -0
- package/dist/types/tools/report-tool-issue.d.ts +3 -0
- package/dist/types/tools/resolve.d.ts +14 -1
- package/dist/types/tools/task.d.ts +3 -0
- package/dist/types/tools/think.d.ts +3 -0
- package/dist/types/tools/todo.d.ts +12 -0
- package/dist/types/tools/vibe.d.ts +3 -1
- package/dist/types/tools/wait.d.ts +3 -0
- package/dist/types/tools/web-search-types.d.ts +90 -0
- package/dist/types/tools/web-search.d.ts +9 -168
- package/dist/types/tools/write.d.ts +14 -1
- package/dist/types/tools/xdev.d.ts +13 -0
- package/dist/types/tui.d.ts +49 -0
- package/package.json +9 -9
- package/src/apps/autoresearch-dashboard.ts +362 -4
- package/src/apps/cleanse-board.ts +219 -1
- package/src/apps/cleanse-picker.ts +9 -1
- package/src/apps/debug/log-viewer.ts +15 -15
- package/src/apps/debug/protocol-probe.ts +2 -20
- package/src/apps/git/avatar.ts +2 -22
- package/src/apps/git/git-tui.ts +406 -59
- package/src/apps/git/help.ts +130 -0
- package/src/apps/git/sidebar.ts +491 -0
- package/src/apps/live-visualizer.ts +65 -0
- package/src/apps/ps-top.ts +448 -17
- package/src/apps/session-picker.ts +1 -0
- package/src/apps/setup-model-picker.ts +5 -1
- package/src/apps/standalone-picker.ts +55 -1
- package/src/autocomplete.ts +44 -1
- package/src/chat/advisor-message.ts +72 -3
- package/src/chat/assistant-message.ts +538 -51
- package/src/chat/background-tan-message.ts +4 -4
- package/src/chat/bash-execution.ts +81 -0
- package/src/chat/cache-invalidation-marker.ts +8 -0
- package/src/chat/chat-transcript-builder.ts +37 -6
- package/src/chat/collab-prompt-message.ts +15 -3
- package/src/chat/compaction-summary-message.ts +74 -1
- package/src/chat/custom-message.ts +3 -1
- package/src/chat/eval-execution.ts +57 -0
- package/src/chat/execution-shared.ts +153 -1
- package/src/chat/hook-message.ts +1 -0
- package/src/chat/late-diagnostics-message.ts +85 -2
- package/src/chat/read-tool-group.ts +341 -1
- package/src/chat/served-model-marker.ts +8 -0
- package/src/chat/skill-message.ts +63 -0
- package/src/chat/stripped-tool-calls-placeholder.ts +18 -10
- package/src/chat/thinking-display.ts +73 -11
- package/src/chat/todo-reminder.ts +34 -4
- package/src/chat/tool-execution.ts +385 -10
- package/src/chat/transcript-actions.ts +38 -0
- package/src/chat/transcript-browser.ts +2 -9
- package/src/chat/transcript-entry.ts +10 -1
- package/src/chat/transcript-render-helpers.ts +16 -15
- package/src/chat/ttsr-notification.ts +38 -1
- package/src/chat/user-message.ts +167 -4
- package/src/chrome/collab-qrcode.ts +27 -2
- package/src/chrome/context-thresholds.ts +34 -0
- package/src/chrome/countdown-timer.ts +24 -1
- package/src/chrome/diff.ts +60 -1
- package/src/chrome/dynamic-border.ts +7 -0
- package/src/chrome/format.ts +12 -0
- package/src/chrome/message-divider.ts +33 -1
- package/src/chrome/message-frame.ts +53 -10
- package/src/chrome/message-notice.ts +71 -0
- package/src/chrome/overlay-box.ts +45 -2
- package/src/chrome/qrcode.ts +28 -0
- package/src/chrome/segment-track.ts +26 -0
- package/src/chrome/selector-helpers.ts +1 -17
- package/src/chrome/status-notice.ts +46 -0
- package/src/chrome/streaming-panel.ts +25 -1
- package/src/chrome/tool-activity.ts +20 -0
- package/src/chrome/transcript-container.ts +139 -17
- package/src/chrome/transcript-status.ts +40 -11
- package/src/components/box.ts +51 -0
- package/src/components/disclosure.ts +33 -0
- package/src/components/editor.ts +239 -36
- package/src/components/form.ts +93 -9
- package/src/components/image.ts +30 -0
- package/src/components/input.ts +77 -26
- package/src/components/key-value-list.ts +18 -0
- package/src/components/layout/geometry.ts +44 -0
- package/src/components/layout/row.ts +30 -0
- package/src/components/layout/split-pane.ts +65 -0
- package/src/components/layout/stack.ts +27 -0
- package/src/components/loader.ts +183 -1
- package/src/components/markdown.ts +131 -56
- package/src/components/metric.ts +39 -0
- package/src/components/progress-bar.ts +28 -0
- package/src/components/scroll-view.ts +23 -0
- package/src/components/section.ts +16 -0
- package/src/components/select-list.ts +174 -25
- package/src/components/settings-list.ts +264 -7
- package/src/components/spacer.ts +17 -0
- package/src/components/tab-bar.ts +42 -0
- package/src/components/table.ts +34 -0
- package/src/components/text.ts +23 -0
- package/src/components/tree-view.ts +65 -0
- package/src/components/truncated-text.ts +14 -0
- package/src/components/wizard-step.ts +18 -9
- package/src/debug-server.ts +14 -7
- package/src/hotkeys-markdown.ts +251 -69
- package/src/index.ts +6 -0
- package/src/latex-block.ts +11 -0
- package/src/loop-watchdog.ts +38 -10
- package/src/native/apply.ts +275 -0
- package/src/native/backend.ts +682 -0
- package/src/native/blobs.ts +70 -0
- package/src/native/describe.ts +124 -0
- package/src/native/encode.ts +233 -0
- package/src/native/icons.ts +184 -0
- package/src/native/memo.ts +64 -0
- package/src/native/node.ts +112 -0
- package/src/native/overlay.ts +131 -0
- package/src/native/picker.ts +333 -0
- package/src/native/reconcile.ts +1063 -0
- package/src/native/settle.ts +23 -0
- package/src/native/spans.ts +308 -0
- package/src/native/state.ts +28 -0
- package/src/native/tone.ts +79 -0
- package/src/overlays/advisor-config.ts +420 -0
- package/src/overlays/agent-hub-renderer.ts +155 -18
- package/src/overlays/agent-hub.ts +1037 -44
- package/src/overlays/agent-transcript-viewer.ts +129 -0
- package/src/overlays/agents-hub.ts +688 -53
- package/src/overlays/annotation-overlay.ts +391 -52
- package/src/overlays/ask-dialog.ts +435 -24
- package/src/overlays/bordered-loader.ts +17 -0
- package/src/overlays/btw-history-panel.ts +182 -0
- package/src/overlays/btw-panel.ts +68 -2
- package/src/overlays/cleanse-panel.ts +105 -15
- package/src/overlays/codex-reset-fireworks.ts +65 -5
- package/src/overlays/composer-shape-preview.ts +26 -1
- package/src/overlays/copy-selector.ts +545 -23
- package/src/overlays/error-banner.ts +44 -0
- package/src/overlays/extensions/extension-list.ts +23 -17
- package/src/overlays/history-search.ts +175 -33
- package/src/overlays/hook-editor.ts +113 -6
- package/src/overlays/hook-input.ts +22 -1
- package/src/overlays/hook-selector.ts +311 -22
- package/src/overlays/hub-frame.ts +80 -0
- package/src/overlays/jobs-panel.ts +112 -0
- package/src/overlays/login-dialog.ts +183 -1
- package/src/overlays/logout-account-selector.ts +102 -4
- package/src/overlays/mcp-add-wizard.ts +232 -53
- package/src/overlays/model-browser.ts +681 -22
- package/src/overlays/model-hub.ts +1402 -78
- package/src/overlays/model-picker.ts +184 -7
- package/src/overlays/move-overlay.ts +45 -0
- package/src/overlays/oauth-selector.ts +227 -23
- package/src/overlays/omfg-panel.ts +86 -2
- package/src/overlays/pause-screen.ts +55 -4
- package/src/overlays/plan-review-overlay.ts +352 -0
- package/src/overlays/plan-save-overlay.ts +45 -0
- package/src/overlays/plugin-selector.ts +21 -1
- package/src/overlays/plugin-settings.ts +195 -5
- package/src/overlays/queue-mode-selector.ts +19 -1
- package/src/overlays/reset-usage-selector.ts +110 -40
- package/src/overlays/rewind-selector.ts +458 -44
- package/src/overlays/session-account-selector.ts +1 -1
- package/src/overlays/session-info-overlay.ts +137 -1
- package/src/overlays/session-selector.ts +807 -46
- package/src/overlays/settings-defs.ts +41 -0
- package/src/overlays/settings-selector.ts +585 -33
- package/src/overlays/show-images-selector.ts +18 -1
- package/src/overlays/snapcompact-shape-preview.ts +48 -3
- package/src/overlays/stats-notice.ts +31 -0
- package/src/overlays/theme-selector.ts +31 -1
- package/src/overlays/thinking-selector.ts +22 -1
- package/src/overlays/tree-selector.ts +570 -110
- package/src/overlays/usage-dashboard.ts +587 -12
- package/src/overlays/usage-row.ts +161 -25
- package/src/prompt/attachment-chips.ts +60 -0
- package/src/prompt/composer-attachments.ts +31 -9
- package/src/prompt/composer.ts +55 -2
- package/src/prompt/custom-editor.ts +302 -4
- package/src/prompt/editor-top-gap.ts +8 -0
- package/src/prompt/macos-spelling.ts +24 -9
- package/src/prompt/magic-keywords.ts +22 -1
- package/src/prompt/model-mention-autocomplete.ts +1 -0
- package/src/prompt/queued-messages.ts +101 -0
- package/src/prompt/skill-tokens.ts +5 -3
- package/src/prompt/welcome.ts +213 -6
- package/src/render/code-cell.ts +103 -2
- package/src/render/file-list.ts +29 -0
- package/src/render/hyperlink.ts +32 -0
- package/src/render/output-block.ts +110 -0
- package/src/render/output-pane.ts +37 -0
- package/src/render/tool-card.ts +137 -0
- package/src/render/tree-list.ts +39 -0
- package/src/render/width-aware-text.ts +20 -0
- package/src/setup/scenes/composer.ts +29 -0
- package/src/setup/scenes/glyph.ts +15 -0
- package/src/setup/scenes/model.ts +36 -8
- package/src/setup/scenes/outro.ts +28 -1
- package/src/setup/scenes/sign-in.ts +138 -24
- package/src/setup/scenes/splash.ts +28 -1
- package/src/setup/scenes/theme.ts +88 -3
- package/src/setup/scenes/types.ts +7 -28
- package/src/setup/startup-splash.ts +46 -9
- package/src/setup/wizard-overlay.ts +140 -23
- package/src/setup/wizard.ts +1 -1
- package/src/status-line/component.ts +414 -18
- package/src/status-line/context-usage.ts +279 -50
- package/src/status-line/footer.ts +94 -1
- package/src/status-line/metrics.ts +3 -6
- package/src/status-line/segments.ts +463 -97
- package/src/status-line/separators.ts +3 -16
- package/src/status-line/types.ts +43 -1
- package/src/stdin-buffer.ts +27 -21
- package/src/terminal-capabilities.ts +7 -0
- package/src/terminal.ts +153 -11
- package/src/theme/defaults/dark-neon-noir.json +100 -0
- package/src/theme/defaults/index.ts +2 -0
- package/src/theme/defaults/light-prism.json +5 -5
- package/src/theme/defaults/onyx.json +62 -62
- package/src/theme/mermaid-cache.ts +10 -1
- package/src/theme/schema.ts +15 -0
- package/src/theme/shimmer.ts +33 -0
- package/src/theme/theme-class.ts +24 -19
- package/src/theme/theme.ts +111 -9
- package/src/theme/tui-adapters.ts +14 -1
- package/src/tools/ask.ts +157 -1
- package/src/tools/ast-edit.ts +174 -1
- package/src/tools/ast-grep.ts +82 -2
- package/src/tools/autoresearch.ts +149 -1
- package/src/tools/bash.ts +233 -62
- package/src/tools/cfg-render.ts +66 -1
- package/src/tools/debug.ts +78 -1
- package/src/tools/default-renderer.ts +35 -1
- package/src/tools/edit.ts +357 -31
- package/src/tools/eval.ts +257 -1
- package/src/tools/fetch.ts +102 -3
- package/src/tools/find.ts +127 -1
- package/src/tools/github.ts +119 -10
- package/src/tools/glob.ts +116 -1
- package/src/tools/goal.ts +56 -1
- package/src/tools/grep.ts +83 -2
- package/src/tools/grouped-file-output.ts +205 -0
- package/src/tools/json-tree.ts +116 -1
- package/src/tools/lsp.ts +318 -1
- package/src/tools/mcp.ts +80 -1
- package/src/tools/memory.ts +93 -11
- package/src/tools/native-view.ts +292 -0
- package/src/tools/proc-render.ts +221 -1
- package/src/tools/read.ts +199 -4
- package/src/tools/renderer.ts +61 -0
- package/src/tools/report-tool-issue.ts +7 -1
- package/src/tools/resolve.ts +63 -1
- package/src/tools/streaming-output.ts +79 -16
- package/src/tools/task.ts +406 -4
- package/src/tools/think.ts +19 -1
- package/src/tools/todo.ts +89 -0
- package/src/tools/vibe.ts +230 -2
- package/src/tools/wait.ts +181 -1
- package/src/tools/web-search-types.ts +99 -0
- package/src/tools/web-search.ts +111 -170
- package/src/tools/write.ts +144 -5
- package/src/tools/xdev.ts +64 -1
- package/src/tui.ts +295 -4
- package/dist/types/setup/scenes/providers.d.ts +0 -3
- package/dist/types/setup/scenes/web-search.d.ts +0 -22
- package/src/setup/scenes/providers.ts +0 -110
- package/src/setup/scenes/web-search.ts +0 -165
|
@@ -14,11 +14,20 @@
|
|
|
14
14
|
* Replaces the old SessionObserverOverlayComponent (ctrl+s observer).
|
|
15
15
|
*/
|
|
16
16
|
import type { AgentTool } from "@oh-my-pi/pi-agent-core";
|
|
17
|
+
import type {
|
|
18
|
+
TspPickerAction,
|
|
19
|
+
TspPickerColumn,
|
|
20
|
+
TspPickerItem,
|
|
21
|
+
TspPickerProps,
|
|
22
|
+
TspPickerScope,
|
|
23
|
+
TspSpan,
|
|
24
|
+
TspTreeNode,
|
|
25
|
+
} from "@oh-my-pi/pi-wire";
|
|
17
26
|
import { Container, type OverlayHandle, type TUI } from "../tui";
|
|
18
27
|
import { matchesKey } from "../keys";
|
|
19
28
|
import { routeSelectListMouse, routeSgrMouseInput, type SelectListMouseTarget } from "../mouse";
|
|
20
29
|
import { padding, visibleWidth, wrapTextWithAnsi } from "../utils";
|
|
21
|
-
import { formatAge, formatNumber, getProjectDir, logger } from "@oh-my-pi/pi-utils";
|
|
30
|
+
import { formatAge, formatDuration, formatNumber, getProjectDir, logger } from "@oh-my-pi/pi-utils";
|
|
22
31
|
import {
|
|
23
32
|
type AgentActivitySource,
|
|
24
33
|
type AgentActivityKind,
|
|
@@ -32,6 +41,7 @@ import { type AgentRecordLike, type AgentHubRegistry, type AgentStatus, MAIN_AGE
|
|
|
32
41
|
import { USER_INTERRUPT_LABEL } from "../chat/messages";
|
|
33
42
|
import { shortenPath, truncateToWidth } from "../render/render-utils";
|
|
34
43
|
import { formatLocalDateTimeWithOffset } from "../chrome/local-date";
|
|
44
|
+
import { getContextUsageLevel, getContextUsageTone } from "../chrome/context-thresholds";
|
|
35
45
|
import type { ObservableSession, SessionObserverRegistry } from "./session-observer-registry";
|
|
36
46
|
import { theme } from "../theme/theme";
|
|
37
47
|
import { matchesSelectDown, matchesSelectUp } from "../keybinding-matchers";
|
|
@@ -52,11 +62,19 @@ import {
|
|
|
52
62
|
formatMetricDuration,
|
|
53
63
|
formatMetrics,
|
|
54
64
|
formatRoleBadge,
|
|
65
|
+
metricsText,
|
|
55
66
|
modelBadge,
|
|
67
|
+
modelBadgeSpans,
|
|
68
|
+
modelChip,
|
|
69
|
+
roleBadgeSpan,
|
|
56
70
|
type RosterRender,
|
|
57
71
|
sanitizeLine,
|
|
58
72
|
statusGlyph,
|
|
73
|
+
statusDot,
|
|
74
|
+
statusGlyphSpan,
|
|
59
75
|
statusText,
|
|
76
|
+
statusTone,
|
|
77
|
+
taskSummary,
|
|
60
78
|
treeBranch,
|
|
61
79
|
treeContinuation,
|
|
62
80
|
treeMetadataIndent,
|
|
@@ -68,6 +86,23 @@ import { fuzzyMatch } from "../fuzzy";
|
|
|
68
86
|
import { bottomBorder, divider, dividerSplit, PanelRows, row, topBorder, topBorderSplit } from "../chrome/overlay-box";
|
|
69
87
|
import { SplitPane } from "../components/layout/split-pane";
|
|
70
88
|
import { Stack } from "../components/layout/stack";
|
|
89
|
+
import { node, span, stableKey, text } from "../native/describe";
|
|
90
|
+
import { type DescribeContext, leafKey, type NativeChild, type NativeNode, type NativeUiEvent } from "../native/node";
|
|
91
|
+
import { hintsRow, type NativeHint, overlayCard } from "../native/overlay";
|
|
92
|
+
import { CLOSE_ACTION, picker, pickerQuery } from "../native/picker";
|
|
93
|
+
import { Input } from "../components/input";
|
|
94
|
+
|
|
95
|
+
/** A chrome-less search field: the hub draws its own prompt. */
|
|
96
|
+
function filterInput(): Input {
|
|
97
|
+
const input = new Input();
|
|
98
|
+
input.prompt = "";
|
|
99
|
+
return input;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/** The field's text with its caret drawn, sized to the value (no trailing padding). */
|
|
103
|
+
function filterText(input: Input): string {
|
|
104
|
+
return input.render(visibleWidth(input.getValue()) + 1)[0] ?? "";
|
|
105
|
+
}
|
|
71
106
|
|
|
72
107
|
/** Two-pane mode needs a useful roster and a readable inspector. */
|
|
73
108
|
const SPLIT_MIN_WIDTH = 96;
|
|
@@ -80,6 +115,84 @@ type ActivityScope = "all" | "agent" | "subtree";
|
|
|
80
115
|
|
|
81
116
|
type HubViewMode = "roster" | "tree";
|
|
82
117
|
|
|
118
|
+
const SECTION_TABS = [
|
|
119
|
+
{ id: "agents", label: "1 Agents" },
|
|
120
|
+
{ id: "activity", label: "2 Activity" },
|
|
121
|
+
] as const satisfies ReadonlyArray<{ id: AgentHubSection; label: string }>;
|
|
122
|
+
const VIEW_TABS = [
|
|
123
|
+
{ id: "roster", label: "Flat" },
|
|
124
|
+
{ id: "tree", label: "By parent" },
|
|
125
|
+
] as const satisfies ReadonlyArray<{ id: HubViewMode; label: string }>;
|
|
126
|
+
const AGENT_HINTS: Readonly<Record<HubViewMode, readonly NativeHint[]>> = {
|
|
127
|
+
roster: agentHints("by parent"),
|
|
128
|
+
tree: agentHints("flat"),
|
|
129
|
+
};
|
|
130
|
+
const ACTIVITY_HINTS: readonly NativeHint[] = [
|
|
131
|
+
{ keys: ["j", "k"], label: "select" },
|
|
132
|
+
{ keys: ["enter"], label: "transcript" },
|
|
133
|
+
{ keys: ["space"], label: "follow" },
|
|
134
|
+
{ keys: ["f"], label: "filter" },
|
|
135
|
+
{ keys: ["s"], label: "scope" },
|
|
136
|
+
{ keys: ["/"], label: "search" },
|
|
137
|
+
{ keys: ["escape"], label: "close" },
|
|
138
|
+
];
|
|
139
|
+
/** Recent-activity entries the native inspector lists for the selected agent. */
|
|
140
|
+
const NATIVE_RECENT_ACTIVITY = 20;
|
|
141
|
+
/** Child ids the native inspector names before summarizing the rest. */
|
|
142
|
+
const NATIVE_CHILD_IDS = 12;
|
|
143
|
+
|
|
144
|
+
/** Picker tabs: the two top-level projections (keys 1/2). */
|
|
145
|
+
const PICKER_TABS = [
|
|
146
|
+
{ id: "agents", label: "Agents" },
|
|
147
|
+
{ id: "activity", label: "Activity" },
|
|
148
|
+
] as const satisfies ReadonlyArray<{ id: AgentHubSection; label: string }>;
|
|
149
|
+
/** Roster fact columns; lower priorities hide first on narrow sheets. */
|
|
150
|
+
const AGENT_COLUMNS: readonly TspPickerColumn[] = [
|
|
151
|
+
{ id: "cost", head: "Cost", format: "price", priority: 6 },
|
|
152
|
+
{ id: "time", head: "Time", format: "elapsed", priority: 5 },
|
|
153
|
+
{ id: "req", head: "Req", format: "num", priority: 2 },
|
|
154
|
+
{ id: "tools", head: "Tools", format: "num", priority: 1 },
|
|
155
|
+
{ id: "tok", head: "Tok", format: "num", priority: 3 },
|
|
156
|
+
{ id: "ctx", head: "Ctx", format: "bar", priority: 4 },
|
|
157
|
+
];
|
|
158
|
+
const ACTIVITY_COLUMNS: readonly TspPickerColumn[] = [
|
|
159
|
+
{ id: "agent", head: "Agent", format: "text", priority: 2 },
|
|
160
|
+
{ id: "at", head: "Time", format: "dim", priority: 1 },
|
|
161
|
+
];
|
|
162
|
+
/** What the roster/activity projections contribute to the picker (the shared head is added once). */
|
|
163
|
+
type PickerBody = Omit<Partial<TspPickerProps>, "title">;
|
|
164
|
+
const ACTIVITY_FILTERS: readonly ActivityFilter[] = ["all", "errors", "responses", "tools"];
|
|
165
|
+
/** Activity filters as the picker's scope column (`f` cycles them). */
|
|
166
|
+
const ACTIVITY_FILTER_SCOPES: readonly TspPickerScope[] = [
|
|
167
|
+
{ id: "all", label: "All", icon: "list", group: "Show" },
|
|
168
|
+
{ id: "errors", label: "Errors", icon: "x-circle", group: "Show" },
|
|
169
|
+
{ id: "responses", label: "Responses", icon: "message", group: "Show" },
|
|
170
|
+
{ id: "tools", label: "Tools", icon: "wrench", group: "Show" },
|
|
171
|
+
];
|
|
172
|
+
const ACTIVITY_KIND_ICON: Readonly<Record<AgentActivityKind, string>> = {
|
|
173
|
+
response: "message",
|
|
174
|
+
tool: "wrench",
|
|
175
|
+
irc: "send",
|
|
176
|
+
lifecycle: "activity",
|
|
177
|
+
};
|
|
178
|
+
|
|
179
|
+
function agentHints(nextView: string): readonly NativeHint[] {
|
|
180
|
+
return [
|
|
181
|
+
{ keys: ["j", "k"], label: "select" },
|
|
182
|
+
{ keys: ["enter"], label: "open" },
|
|
183
|
+
{ keys: ["t"], label: nextView },
|
|
184
|
+
{ keys: ["/"], label: "filter" },
|
|
185
|
+
{ keys: ["r"], label: "revive" },
|
|
186
|
+
{ keys: ["x"], label: "kill" },
|
|
187
|
+
{ keys: ["escape"], label: "close" },
|
|
188
|
+
];
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
/** A tree node whose children are still being attached while the roster projects. */
|
|
192
|
+
interface AgentTreeEntry extends TspTreeNode {
|
|
193
|
+
children?: AgentTreeEntry[];
|
|
194
|
+
}
|
|
195
|
+
|
|
83
196
|
/** Refresh cadence for the relative-time column. */
|
|
84
197
|
const AGE_TICK_MS = 5_000;
|
|
85
198
|
const DATA_CHANGE_RENDER_COALESCE_MS = 100;
|
|
@@ -102,6 +215,23 @@ function activityGlyph(row: AgentActivityRow): string {
|
|
|
102
215
|
}
|
|
103
216
|
}
|
|
104
217
|
|
|
218
|
+
/** Native span of {@link activityGlyph}. */
|
|
219
|
+
function activityGlyphSpan(row: AgentActivityRow): TspSpan {
|
|
220
|
+
if (row.status === "error") return span(theme.status.error, "error");
|
|
221
|
+
if (row.status === "aborted") return span(theme.status.aborted, "warning");
|
|
222
|
+
if (row.status === "pending") return span(theme.status.running, "accent");
|
|
223
|
+
switch (row.kind) {
|
|
224
|
+
case "response":
|
|
225
|
+
return span("◆", "success");
|
|
226
|
+
case "tool":
|
|
227
|
+
return span(theme.status.success, "success");
|
|
228
|
+
case "irc":
|
|
229
|
+
return span("→", "accent");
|
|
230
|
+
case "lifecycle":
|
|
231
|
+
return span("○", "muted");
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
|
|
105
235
|
const ACTIVITY_CLOCK_FORMAT = new Intl.DateTimeFormat(undefined, {
|
|
106
236
|
hour: "2-digit",
|
|
107
237
|
minute: "2-digit",
|
|
@@ -215,7 +345,8 @@ export class AgentHubOverlayComponent<TRecord extends AgentRecordLike = AgentRec
|
|
|
215
345
|
#selectedActivityRow = 0;
|
|
216
346
|
#activityFilter: ActivityFilter = "all";
|
|
217
347
|
#activityScope: ActivityScope = "all";
|
|
218
|
-
|
|
348
|
+
/** Activity search (`/`); shown while editing or non-empty. */
|
|
349
|
+
readonly #activitySearch = filterInput();
|
|
219
350
|
#activitySearchEditing = false;
|
|
220
351
|
#activityFollow = true;
|
|
221
352
|
#activitySyncGeneration = 0;
|
|
@@ -243,7 +374,7 @@ export class AgentHubOverlayComponent<TRecord extends AgentRecordLike = AgentRec
|
|
|
243
374
|
#treeLastSiblingById = new Map<string, boolean>();
|
|
244
375
|
#treeMaxDepth = 0;
|
|
245
376
|
/** Fuzzy agent filter (`/`), applied to id and display name. */
|
|
246
|
-
#agentFilter =
|
|
377
|
+
readonly #agentFilter = filterInput();
|
|
247
378
|
#agentFilterEditing = false;
|
|
248
379
|
/** Current observer index and summary data, rebuilt on source changes rather than every paint. */
|
|
249
380
|
#observedById = new Map<string, ObservableSession>();
|
|
@@ -320,6 +451,13 @@ export class AgentHubOverlayComponent<TRecord extends AgentRecordLike = AgentRec
|
|
|
320
451
|
#transcriptOverlay: OverlayHandle | undefined;
|
|
321
452
|
#transcriptViewer: AgentTranscriptViewer | undefined;
|
|
322
453
|
|
|
454
|
+
/** Last native description; every visible state change goes through requestRender, which drops it. */
|
|
455
|
+
#native: NativeNode | undefined;
|
|
456
|
+
/** Which terminal kinds {@link #native} was described for (support may change between frames). */
|
|
457
|
+
#nativeVariant = "";
|
|
458
|
+
/** Preview recent-activity rows by node key, for their pointer actions. */
|
|
459
|
+
#recentByKey = new Map<string, AgentActivityRow>();
|
|
460
|
+
|
|
323
461
|
constructor(deps: AgentHubDeps<TRecord>) {
|
|
324
462
|
super();
|
|
325
463
|
this.#section = deps.initialSection ?? "agents";
|
|
@@ -332,7 +470,10 @@ export class AgentHubOverlayComponent<TRecord extends AgentRecordLike = AgentRec
|
|
|
332
470
|
this.#irc = deps.irc;
|
|
333
471
|
this.#lifecycle = deps.lifecycle;
|
|
334
472
|
this.#onDone = deps.onDone;
|
|
335
|
-
this.#requestRender =
|
|
473
|
+
this.#requestRender = () => {
|
|
474
|
+
this.#native = undefined;
|
|
475
|
+
deps.requestRender();
|
|
476
|
+
};
|
|
336
477
|
this.#hubKeys = deps.hubKeys;
|
|
337
478
|
this.#remote = deps.remote;
|
|
338
479
|
this.#loadingPersistedSubagents = !this.#remote && Boolean(deps.sessionFile?.endsWith(".jsonl"));
|
|
@@ -529,6 +670,827 @@ export class AgentHubOverlayComponent<TRecord extends AgentRecordLike = AgentRec
|
|
|
529
670
|
}
|
|
530
671
|
}
|
|
531
672
|
|
|
673
|
+
// ========================================================================
|
|
674
|
+
// Native description
|
|
675
|
+
// ========================================================================
|
|
676
|
+
|
|
677
|
+
/** A `picker` is its own sheet: the backend mounts it in `layer` without an `overlay` wrapper. */
|
|
678
|
+
nativeSheet(cx: DescribeContext): boolean {
|
|
679
|
+
return cx.supports("picker");
|
|
680
|
+
}
|
|
681
|
+
|
|
682
|
+
override describe(cx: DescribeContext): NativeNode {
|
|
683
|
+
const usePicker = cx.supports("picker");
|
|
684
|
+
const nativeTree = cx.supports("tree");
|
|
685
|
+
const meter = cx.supports("meter");
|
|
686
|
+
const variant = usePicker ? `picker:${meter}` : nativeTree ? "tree" : "list";
|
|
687
|
+
if (this.#native && this.#nativeVariant === variant) return this.#native;
|
|
688
|
+
this.#nativeVariant = variant;
|
|
689
|
+
if (usePicker) {
|
|
690
|
+
this.#native = this.#describePicker(meter);
|
|
691
|
+
return this.#native;
|
|
692
|
+
}
|
|
693
|
+
const tabs = node("tabs", { items: SECTION_TABS, active: this.#section }, undefined, "section");
|
|
694
|
+
const body = this.#section === "activity" ? this.#describeActivity() : this.#describeAgents(nativeTree);
|
|
695
|
+
this.#native = overlayCard("omp.overlay.agentHub", "Agent Hub", [tabs, ...body]);
|
|
696
|
+
return this.#native;
|
|
697
|
+
}
|
|
698
|
+
|
|
699
|
+
/**
|
|
700
|
+
* Pointer actions on the described hub. `select` mirrors keyboard selection
|
|
701
|
+
* (roster row, activity row, section tab, projection tab); `activate` does
|
|
702
|
+
* what Enter does on that row. Picker actions run the key handlers' paths.
|
|
703
|
+
*/
|
|
704
|
+
handleNativeEvent(event: NativeUiEvent): void {
|
|
705
|
+
if (event.key === "") {
|
|
706
|
+
this.#handlePickerEvent(event);
|
|
707
|
+
return;
|
|
708
|
+
}
|
|
709
|
+
if (event.type === "action") {
|
|
710
|
+
const activity = this.#recentByKey.get(leafKey(event.key));
|
|
711
|
+
if (event.act === "transcript" && activity) this.openChat(activity.agentId, activity.entryId);
|
|
712
|
+
return;
|
|
713
|
+
}
|
|
714
|
+
if (event.type !== "select" && event.type !== "activate") return;
|
|
715
|
+
const target = leafKey(event.key);
|
|
716
|
+
const item = event.item;
|
|
717
|
+
switch (target) {
|
|
718
|
+
case "section":
|
|
719
|
+
if (item === "agents" || item === "activity") this.#switchSection(item);
|
|
720
|
+
return;
|
|
721
|
+
case "view":
|
|
722
|
+
if ((item === "roster" || item === "tree") && item !== this.#viewMode) {
|
|
723
|
+
this.#hoveredRow = null;
|
|
724
|
+
this.#viewMode = item;
|
|
725
|
+
this.#refreshRows();
|
|
726
|
+
this.#requestRender();
|
|
727
|
+
}
|
|
728
|
+
return;
|
|
729
|
+
case "agents": {
|
|
730
|
+
const index = this.#rows.findIndex(ref => ref.id === item);
|
|
731
|
+
const ref = this.#rows[index];
|
|
732
|
+
if (!ref) return;
|
|
733
|
+
this.#hoveredRow = null;
|
|
734
|
+
this.#selectRow(index);
|
|
735
|
+
this.#requestRender();
|
|
736
|
+
if (event.type === "activate") this.#activateAgent(ref);
|
|
737
|
+
return;
|
|
738
|
+
}
|
|
739
|
+
case "activity": {
|
|
740
|
+
const index = this.#activityRows.findIndex(row => row.id === item);
|
|
741
|
+
const activity = this.#activityRows[index];
|
|
742
|
+
if (!activity) return;
|
|
743
|
+
if (event.type === "activate") {
|
|
744
|
+
this.openChat(activity.agentId, activity.entryId);
|
|
745
|
+
return;
|
|
746
|
+
}
|
|
747
|
+
this.#activityFollow = false;
|
|
748
|
+
this.#selectedActivityRow = index;
|
|
749
|
+
this.#requestRender();
|
|
750
|
+
return;
|
|
751
|
+
}
|
|
752
|
+
case "recent": {
|
|
753
|
+
const agentId = this.#rows[this.#selectedRow]?.id;
|
|
754
|
+
if (event.type !== "activate" || !agentId) return;
|
|
755
|
+
const activity = this.#activity.recent(agentId, NATIVE_RECENT_ACTIVITY).find(row => row.id === item);
|
|
756
|
+
if (activity) this.openChat(activity.agentId, activity.entryId);
|
|
757
|
+
return;
|
|
758
|
+
}
|
|
759
|
+
}
|
|
760
|
+
}
|
|
761
|
+
|
|
762
|
+
/** Picker events (the sheet is this component's root): same paths as the keys. */
|
|
763
|
+
#handlePickerEvent(event: NativeUiEvent): void {
|
|
764
|
+
const activity = this.#section === "activity";
|
|
765
|
+
if (event.type === "select" || event.type === "activate") {
|
|
766
|
+
if (activity) {
|
|
767
|
+
const index = this.#activityRows.findIndex(row => row.id === event.item);
|
|
768
|
+
const row = this.#activityRows[index];
|
|
769
|
+
if (!row) return;
|
|
770
|
+
if (event.type === "activate") {
|
|
771
|
+
this.openChat(row.agentId, row.entryId);
|
|
772
|
+
return;
|
|
773
|
+
}
|
|
774
|
+
this.#activityFollow = false;
|
|
775
|
+
this.#selectedActivityRow = index;
|
|
776
|
+
this.#requestRender();
|
|
777
|
+
return;
|
|
778
|
+
}
|
|
779
|
+
const index = this.#rows.findIndex(ref => ref.id === event.item);
|
|
780
|
+
const ref = this.#rows[index];
|
|
781
|
+
if (!ref) return;
|
|
782
|
+
this.#hoveredRow = null;
|
|
783
|
+
this.#selectRow(index);
|
|
784
|
+
this.#requestRender();
|
|
785
|
+
if (event.type === "activate") this.#activateAgent(ref);
|
|
786
|
+
return;
|
|
787
|
+
}
|
|
788
|
+
if (event.type !== "action") return;
|
|
789
|
+
switch (event.act) {
|
|
790
|
+
case "tab":
|
|
791
|
+
if (event.value === "agents" || event.value === "activity") this.#switchSection(event.value);
|
|
792
|
+
return;
|
|
793
|
+
case "scope":
|
|
794
|
+
if (activity && ACTIVITY_FILTERS.includes(event.value as ActivityFilter)) {
|
|
795
|
+
this.#setActivityFilter(event.value as ActivityFilter);
|
|
796
|
+
}
|
|
797
|
+
return;
|
|
798
|
+
case "close":
|
|
799
|
+
this.#onDone();
|
|
800
|
+
return;
|
|
801
|
+
case "filter":
|
|
802
|
+
if (activity) this.#activitySearchEditing = true;
|
|
803
|
+
else this.#agentFilterEditing = true;
|
|
804
|
+
this.#requestRender();
|
|
805
|
+
return;
|
|
806
|
+
}
|
|
807
|
+
if (activity) {
|
|
808
|
+
switch (event.act) {
|
|
809
|
+
case "open": {
|
|
810
|
+
const row = this.#activityRows[this.#selectedActivityRow];
|
|
811
|
+
if (row) this.openChat(row.agentId, row.entryId);
|
|
812
|
+
return;
|
|
813
|
+
}
|
|
814
|
+
case "follow":
|
|
815
|
+
this.#toggleActivityFollow();
|
|
816
|
+
return;
|
|
817
|
+
case "agents":
|
|
818
|
+
this.#cycleActivityScope();
|
|
819
|
+
return;
|
|
820
|
+
}
|
|
821
|
+
return;
|
|
822
|
+
}
|
|
823
|
+
switch (event.act) {
|
|
824
|
+
case "open": {
|
|
825
|
+
const ref = this.#rows[this.#selectedRow];
|
|
826
|
+
if (ref) this.#activateAgent(ref);
|
|
827
|
+
return;
|
|
828
|
+
}
|
|
829
|
+
case "revive":
|
|
830
|
+
this.#reviveSelected();
|
|
831
|
+
return;
|
|
832
|
+
case "kill":
|
|
833
|
+
this.#killSelected();
|
|
834
|
+
return;
|
|
835
|
+
case "view":
|
|
836
|
+
this.#toggleViewMode();
|
|
837
|
+
return;
|
|
838
|
+
}
|
|
839
|
+
}
|
|
840
|
+
|
|
841
|
+
/** The hub as a data-first `picker` (§9.1): roster or activity log, selected agent as preview. */
|
|
842
|
+
#describePicker(meter: boolean): NativeNode {
|
|
843
|
+
const activity = this.#section === "activity";
|
|
844
|
+
const props: TspPickerProps = {
|
|
845
|
+
title: "Agents",
|
|
846
|
+
subtitle: this.#pickerSubtitle(),
|
|
847
|
+
icon: "users",
|
|
848
|
+
size: "lg",
|
|
849
|
+
tabs: PICKER_TABS,
|
|
850
|
+
tab: this.#section,
|
|
851
|
+
...(activity ? this.#activityPickerProps() : this.#agentPickerProps()),
|
|
852
|
+
};
|
|
853
|
+
const preview = activity ? [] : this.#describePreview(this.#rows[this.#selectedRow], meter);
|
|
854
|
+
return picker(props, preview);
|
|
855
|
+
}
|
|
856
|
+
|
|
857
|
+
/** Roster totals: `$0.389 · 18.7s · 6 req · 114K tok`. */
|
|
858
|
+
#pickerSubtitle(): string {
|
|
859
|
+
const metrics = this.#aggregate;
|
|
860
|
+
if (metrics.reportedAgents === 0) return "No usage reported yet";
|
|
861
|
+
const parts = [formatCost(metrics.cost)];
|
|
862
|
+
if (metrics.durationMs > 0) parts.push(formatDuration(metrics.durationMs));
|
|
863
|
+
parts.push(`${formatNumber(metrics.requests)} req`, `${formatNumber(metrics.tokens)} tok`);
|
|
864
|
+
return parts.join(theme.sep.dot);
|
|
865
|
+
}
|
|
866
|
+
|
|
867
|
+
#agentPickerProps(): PickerBody {
|
|
868
|
+
const selected = this.#rows[this.#selectedRow];
|
|
869
|
+
const readOnly = selected?.kind === "advisor" ? "Read-only advisor transcript" : undefined;
|
|
870
|
+
const actions: TspPickerAction[] = [
|
|
871
|
+
{
|
|
872
|
+
id: "open",
|
|
873
|
+
label: "Open transcript",
|
|
874
|
+
keys: ["enter"],
|
|
875
|
+
primary: true,
|
|
876
|
+
disabled: selected ? undefined : true,
|
|
877
|
+
},
|
|
878
|
+
{
|
|
879
|
+
id: "revive",
|
|
880
|
+
label: "Revive",
|
|
881
|
+
keys: ["r"],
|
|
882
|
+
disabled: readOnly ?? (selected?.status === "parked" ? undefined : "Only parked agents can be revived"),
|
|
883
|
+
},
|
|
884
|
+
{ id: "kill", label: "Kill", keys: ["x"], danger: true, disabled: readOnly ?? (selected ? undefined : true) },
|
|
885
|
+
{ id: "view", label: "By parent", keys: ["t"], on: this.#viewMode === "tree" },
|
|
886
|
+
{ id: "filter", label: "Filter", keys: ["/"] },
|
|
887
|
+
CLOSE_ACTION,
|
|
888
|
+
];
|
|
889
|
+
return {
|
|
890
|
+
noun: "agents",
|
|
891
|
+
layout: this.#viewMode === "tree" ? "tree" : "rows",
|
|
892
|
+
preview: "side",
|
|
893
|
+
...pickerQuery(this.#agentFilterEditing || this.#agentFilter.getValue() ? this.#agentFilter : null),
|
|
894
|
+
placeholder: "Filter agents…",
|
|
895
|
+
columns: AGENT_COLUMNS,
|
|
896
|
+
items: this.#rows.map(ref => this.#pickerAgentItem(ref)),
|
|
897
|
+
selected: selected?.id ?? null,
|
|
898
|
+
total: this.#registry.list().length - (this.#registry.get(MAIN_AGENT_ID) ? 1 : 0),
|
|
899
|
+
state: this.#rows.length === 0 && this.#loadingPersistedSubagents ? "loading" : "ready",
|
|
900
|
+
empty: "No agents in this session. Finished, parked and killed subagents stay with the session that created them.",
|
|
901
|
+
actions,
|
|
902
|
+
focus: "list",
|
|
903
|
+
};
|
|
904
|
+
}
|
|
905
|
+
|
|
906
|
+
#pickerAgentItem(ref: TRecord): TspPickerItem {
|
|
907
|
+
const observed = this.#observableFor(ref.id);
|
|
908
|
+
const metrics = this.#metricsFor(ref, observed);
|
|
909
|
+
const task = observed?.description ?? observed?.progress?.task ?? ref.activity;
|
|
910
|
+
const badges: { text: string; tone?: "warning" | "muted"; title?: string }[] = [];
|
|
911
|
+
const modelRole = observed?.progress?.modelRole ?? ref.history?.modelRole;
|
|
912
|
+
if (modelRole && this.#getRoleInfo) {
|
|
913
|
+
const info = this.#getRoleInfo(modelRole);
|
|
914
|
+
badges.push({ text: sanitizeDisplaySingleLine(info.tag ?? info.name ?? modelRole), title: "Model role" });
|
|
915
|
+
}
|
|
916
|
+
if (ref.kind === "advisor") badges.push({ text: "read-only", tone: "warning" });
|
|
917
|
+
const unread = this.#irc.unreadCount(ref.id);
|
|
918
|
+
if (unread > 0) badges.push({ text: `${unread} unread`, tone: "warning" });
|
|
919
|
+
if (this.#viewMode === "roster" && ref.parentId && ref.parentId !== MAIN_AGENT_ID) {
|
|
920
|
+
badges.push({ text: `↳ ${sanitizeDisplaySingleLine(ref.parentId)}`, tone: "muted", title: "Spawned by" });
|
|
921
|
+
}
|
|
922
|
+
const model = modelChip(ref, observed);
|
|
923
|
+
const item: TspPickerItem = {
|
|
924
|
+
id: ref.id,
|
|
925
|
+
label: sanitizeDisplaySingleLine(ref.id),
|
|
926
|
+
mono: true,
|
|
927
|
+
dot: statusDot(ref.status),
|
|
928
|
+
detail: task ? taskSummary(task) : undefined,
|
|
929
|
+
chips: model
|
|
930
|
+
? [{ text: model.fallback ? `fallback → ${model.text}` : model.text, dot: model.dot }]
|
|
931
|
+
: undefined,
|
|
932
|
+
badges: badges.length > 0 ? badges.slice(0, 3) : undefined,
|
|
933
|
+
};
|
|
934
|
+
if (this.#viewMode === "tree") {
|
|
935
|
+
const depth = this.#treeDepthById.get(ref.id) ?? 0;
|
|
936
|
+
return { ...item, depth, open: this.#childrenByParent.has(ref.id) ? true : undefined };
|
|
937
|
+
}
|
|
938
|
+
if (!metrics) return item;
|
|
939
|
+
const facts: Record<string, string | number> = {
|
|
940
|
+
cost: formatCost(metrics.cost),
|
|
941
|
+
// A running agent's active time ticks terminal-side; a settled one is frozen text.
|
|
942
|
+
time:
|
|
943
|
+
ref.status === "running" && metrics.durationMs > 0
|
|
944
|
+
? metrics.durationMs
|
|
945
|
+
: formatDuration(metrics.durationMs),
|
|
946
|
+
req: metrics.requests,
|
|
947
|
+
tools: metrics.tools,
|
|
948
|
+
tok: metrics.tokens,
|
|
949
|
+
};
|
|
950
|
+
let title: string | undefined;
|
|
951
|
+
if (metrics.contextTokens !== undefined && metrics.contextWindow) {
|
|
952
|
+
facts.ctx = Math.max(0, Math.min(1, metrics.contextTokens / metrics.contextWindow));
|
|
953
|
+
title = `Context ${formatNumber(metrics.contextTokens)} / ${formatNumber(metrics.contextWindow)}`;
|
|
954
|
+
}
|
|
955
|
+
return { ...item, facts, title };
|
|
956
|
+
}
|
|
957
|
+
|
|
958
|
+
/** Selected agent's preview: title with status, facts, context meter, recent activity. */
|
|
959
|
+
#describePreview(ref: TRecord | undefined, meter: boolean): NativeChild[] {
|
|
960
|
+
this.#recentByKey.clear();
|
|
961
|
+
if (!ref) return [];
|
|
962
|
+
const observed = this.#observableFor(ref.id);
|
|
963
|
+
const metrics = this.#metricsFor(ref, observed);
|
|
964
|
+
const out: NativeChild[] = [
|
|
965
|
+
node(
|
|
966
|
+
"row",
|
|
967
|
+
{ role: "omp.hub.title", gap: "sm", align: "center" },
|
|
968
|
+
[
|
|
969
|
+
text(sanitizeDisplaySingleLine(ref.displayName || ref.id), {
|
|
970
|
+
role: "omp.picker.title",
|
|
971
|
+
truncate: "end",
|
|
972
|
+
}),
|
|
973
|
+
node("badge", { text: ref.status, tone: statusDot(ref.status) }),
|
|
974
|
+
],
|
|
975
|
+
"title",
|
|
976
|
+
),
|
|
977
|
+
];
|
|
978
|
+
if (this.#notice) {
|
|
979
|
+
out.push(node("text", { text: this.#notice, tone: "error", wrap: "word" }, undefined, "notice"));
|
|
980
|
+
}
|
|
981
|
+
const facts = this.#detailFacts(ref, observed, metrics);
|
|
982
|
+
const chip = modelChip(ref, observed);
|
|
983
|
+
if (chip) {
|
|
984
|
+
const model: TspSpan[] = [];
|
|
985
|
+
if (chip.fallback) model.push(span("fallback → ", "warning"));
|
|
986
|
+
model.push(span(chip.text, "mono"));
|
|
987
|
+
if (chip.level && chip.dot) model.push(span(theme.sep.dot, "dim"), span(chip.level, chip.dot));
|
|
988
|
+
const modelRole = observed?.progress?.modelRole ?? ref.history?.modelRole;
|
|
989
|
+
if (modelRole && this.#getRoleInfo) {
|
|
990
|
+
model.push(span(theme.sep.dot, "dim"), roleBadgeSpan(modelRole, this.#getRoleInfo(modelRole)));
|
|
991
|
+
}
|
|
992
|
+
facts.splice(facts[0]?.k === "Task" ? 1 : 0, 0, { k: "Model", v: model });
|
|
993
|
+
}
|
|
994
|
+
out.push(node("kv", { items: facts, layout: "grid", role: "omp.hub.kv" }, undefined, "facts"));
|
|
995
|
+
if (metrics?.contextTokens !== undefined && metrics.contextWindow) {
|
|
996
|
+
const ratio = Math.max(0, Math.min(1, metrics.contextTokens / metrics.contextWindow));
|
|
997
|
+
const label = `${formatNumber(metrics.contextTokens)} / ${formatNumber(metrics.contextWindow)} · ${Math.round(ratio * 100)}%`;
|
|
998
|
+
out.push(
|
|
999
|
+
meter
|
|
1000
|
+
? node(
|
|
1001
|
+
"meter",
|
|
1002
|
+
{
|
|
1003
|
+
value: ratio,
|
|
1004
|
+
style: "bar",
|
|
1005
|
+
size: "md",
|
|
1006
|
+
label,
|
|
1007
|
+
tone: getContextUsageTone(getContextUsageLevel(ratio * 100, metrics.contextWindow)),
|
|
1008
|
+
},
|
|
1009
|
+
undefined,
|
|
1010
|
+
"context",
|
|
1011
|
+
)
|
|
1012
|
+
: node("progress", { value: ratio, label }, undefined, "context"),
|
|
1013
|
+
);
|
|
1014
|
+
}
|
|
1015
|
+
const recent = this.#activity.recent(ref.id, NATIVE_RECENT_ACTIVITY);
|
|
1016
|
+
const rows: NativeChild[] = recent.map(activity => {
|
|
1017
|
+
const key = stableKey(activity.id);
|
|
1018
|
+
this.#recentByKey.set(key, activity);
|
|
1019
|
+
const name = activity.kind === "tool" ? (activity.toolName ?? activity.title) : activity.title;
|
|
1020
|
+
return node(
|
|
1021
|
+
"row",
|
|
1022
|
+
{
|
|
1023
|
+
role: "omp.hub.activity.row",
|
|
1024
|
+
gap: "sm",
|
|
1025
|
+
align: "baseline",
|
|
1026
|
+
actions: { click: "transcript" },
|
|
1027
|
+
title: "Open in transcript",
|
|
1028
|
+
},
|
|
1029
|
+
[
|
|
1030
|
+
text([span(activityClock(activity.timestamp), "mono dim")]),
|
|
1031
|
+
text([activityGlyphSpan(activity)]),
|
|
1032
|
+
text([span(sanitizeDisplaySingleLine(name), "mono muted")]),
|
|
1033
|
+
text(sanitizeDisplaySingleLine(activity.summary), { truncate: "end", grow: 1 }),
|
|
1034
|
+
],
|
|
1035
|
+
key,
|
|
1036
|
+
);
|
|
1037
|
+
});
|
|
1038
|
+
if (rows.length === 0) rows.push(text([span("No response or tool activity yet", "dim")]));
|
|
1039
|
+
out.push(node("section", { head: "Recent activity", role: "omp.hub.activity" }, rows, "recentActivity"));
|
|
1040
|
+
return out;
|
|
1041
|
+
}
|
|
1042
|
+
|
|
1043
|
+
#activityPickerProps(): PickerBody {
|
|
1044
|
+
const agent = this.#rows[this.#selectedRow]?.id;
|
|
1045
|
+
const scope =
|
|
1046
|
+
this.#activityScope === "all"
|
|
1047
|
+
? "All agents"
|
|
1048
|
+
: this.#activityScope === "agent"
|
|
1049
|
+
? (agent ?? "Selected agent")
|
|
1050
|
+
: `${agent ?? "Selected"} subtree`;
|
|
1051
|
+
const selected = this.#activityRows[this.#selectedActivityRow];
|
|
1052
|
+
return {
|
|
1053
|
+
noun: "events",
|
|
1054
|
+
layout: "rows",
|
|
1055
|
+
preview: "none",
|
|
1056
|
+
...pickerQuery(this.#activitySearchEditing || this.#activitySearch.getValue() ? this.#activitySearch : null),
|
|
1057
|
+
placeholder: "Search activity…",
|
|
1058
|
+
scopes: ACTIVITY_FILTER_SCOPES,
|
|
1059
|
+
scope: this.#activityFilter,
|
|
1060
|
+
columns: ACTIVITY_COLUMNS,
|
|
1061
|
+
items: this.#activityRows.map(activity => this.#pickerActivityItem(activity)),
|
|
1062
|
+
selected: selected?.id ?? null,
|
|
1063
|
+
empty: "No agent activity recorded yet",
|
|
1064
|
+
actions: [
|
|
1065
|
+
{
|
|
1066
|
+
id: "open",
|
|
1067
|
+
label: "Open transcript",
|
|
1068
|
+
keys: ["enter"],
|
|
1069
|
+
primary: true,
|
|
1070
|
+
disabled: selected ? undefined : true,
|
|
1071
|
+
},
|
|
1072
|
+
{ id: "follow", label: "Follow", keys: ["space"], on: this.#activityFollow },
|
|
1073
|
+
{ id: "agents", label: sanitizeDisplaySingleLine(scope), keys: ["s"] },
|
|
1074
|
+
{ id: "filter", label: "Search", keys: ["/"] },
|
|
1075
|
+
CLOSE_ACTION,
|
|
1076
|
+
],
|
|
1077
|
+
focus: "list",
|
|
1078
|
+
};
|
|
1079
|
+
}
|
|
1080
|
+
|
|
1081
|
+
#pickerActivityItem(activity: AgentActivityRow): TspPickerItem {
|
|
1082
|
+
const title = activity.kind === "tool" ? (activity.toolName ?? activity.title) : activity.title;
|
|
1083
|
+
const tone = activity.status === "error" ? "error" : activity.status === "aborted" ? "warning" : undefined;
|
|
1084
|
+
return {
|
|
1085
|
+
id: activity.id,
|
|
1086
|
+
label: sanitizeDisplaySingleLine(title),
|
|
1087
|
+
mono: activity.kind === "tool" || undefined,
|
|
1088
|
+
detail: sanitizeDisplaySingleLine(activity.summary),
|
|
1089
|
+
icon: ACTIVITY_KIND_ICON[activity.kind],
|
|
1090
|
+
dot: activity.status === "pending" ? "pending" : undefined,
|
|
1091
|
+
tone,
|
|
1092
|
+
facts: {
|
|
1093
|
+
agent: [span(sanitizeDisplaySingleLine(activity.agentId), "mono")],
|
|
1094
|
+
at: activityClock(activity.timestamp),
|
|
1095
|
+
},
|
|
1096
|
+
title: formatLocalDateTimeWithOffset(new Date(activity.timestamp)),
|
|
1097
|
+
};
|
|
1098
|
+
}
|
|
1099
|
+
|
|
1100
|
+
#describeAgents(nativeTree: boolean): NativeChild[] {
|
|
1101
|
+
const head: NativeChild[] = [
|
|
1102
|
+
text([span("Roster", "strong")]),
|
|
1103
|
+
node("tabs", { items: VIEW_TABS, active: this.#viewMode }, undefined, "view"),
|
|
1104
|
+
];
|
|
1105
|
+
const counts: TspSpan[] = [];
|
|
1106
|
+
for (const status of ["running", "idle", "parked", "aborted"] as const) {
|
|
1107
|
+
const count = this.#statusCounts[status];
|
|
1108
|
+
if (count === 0) continue;
|
|
1109
|
+
if (counts.length > 0) counts.push(span(theme.sep.dot, "dim"));
|
|
1110
|
+
counts.push(statusGlyphSpan(status), span(` ${count} ${status}`, statusTone(status)));
|
|
1111
|
+
}
|
|
1112
|
+
if (counts.length > 0) head.push(text(counts));
|
|
1113
|
+
|
|
1114
|
+
const children: NativeChild[] = [
|
|
1115
|
+
node("row", { gap: "sm", wrap: true, align: "center" }, head, "head"),
|
|
1116
|
+
node("text", { spans: this.#usageSpans(), wrap: "word" }, undefined, "usage"),
|
|
1117
|
+
];
|
|
1118
|
+
if (this.#agentFilterEditing) {
|
|
1119
|
+
children.push(
|
|
1120
|
+
node(
|
|
1121
|
+
"input",
|
|
1122
|
+
{
|
|
1123
|
+
text: this.#agentFilter.getValue(),
|
|
1124
|
+
cursor: this.#agentFilter.getCursor(),
|
|
1125
|
+
prompt: [span("/", "muted")],
|
|
1126
|
+
},
|
|
1127
|
+
undefined,
|
|
1128
|
+
"filter",
|
|
1129
|
+
),
|
|
1130
|
+
);
|
|
1131
|
+
} else if (this.#agentFilter.getValue()) {
|
|
1132
|
+
children.push(
|
|
1133
|
+
node("text", { spans: [span(`/${this.#agentFilter.getValue()}`, "accent")] }, undefined, "filter"),
|
|
1134
|
+
);
|
|
1135
|
+
}
|
|
1136
|
+
children.push(
|
|
1137
|
+
node(
|
|
1138
|
+
"row",
|
|
1139
|
+
{ gap: "md", wrap: true, align: "start", grow: 1 },
|
|
1140
|
+
[this.#describeRoster(nativeTree), this.#describeDetail(this.#rows[this.#selectedRow])],
|
|
1141
|
+
"body",
|
|
1142
|
+
),
|
|
1143
|
+
);
|
|
1144
|
+
if (this.#notice) {
|
|
1145
|
+
children.push(
|
|
1146
|
+
node("text", { spans: [span(this.#notice, "error")], wrap: "word", tone: "error" }, undefined, "notice"),
|
|
1147
|
+
);
|
|
1148
|
+
}
|
|
1149
|
+
children.push(hintsRow(AGENT_HINTS[this.#viewMode]));
|
|
1150
|
+
return children;
|
|
1151
|
+
}
|
|
1152
|
+
|
|
1153
|
+
#usageSpans(): TspSpan[] {
|
|
1154
|
+
const metrics = this.#aggregate;
|
|
1155
|
+
const dot = theme.sep.dot;
|
|
1156
|
+
if (metrics.reportedAgents === 0) return [span(`Usage —${dot}0/${this.#rows.length} measured`, "dim")];
|
|
1157
|
+
const activeTime = formatMetricDuration(metrics);
|
|
1158
|
+
return [
|
|
1159
|
+
span(formatCost(metrics.cost), "statusLineCost"),
|
|
1160
|
+
span(
|
|
1161
|
+
[
|
|
1162
|
+
"",
|
|
1163
|
+
activeTime ? `${activeTime} agent time` : "agent time —",
|
|
1164
|
+
`${formatNumber(metrics.requests)} req`,
|
|
1165
|
+
`${formatNumber(metrics.tools)} tools`,
|
|
1166
|
+
`${formatNumber(metrics.tokens)} tok`,
|
|
1167
|
+
`${metrics.activeDurationAgents}/${metrics.reportedAgents} timed`,
|
|
1168
|
+
`${metrics.reportedAgents}/${this.#rows.length} measured`,
|
|
1169
|
+
].join(dot),
|
|
1170
|
+
"dim",
|
|
1171
|
+
),
|
|
1172
|
+
];
|
|
1173
|
+
}
|
|
1174
|
+
|
|
1175
|
+
#describeRoster(nativeTree: boolean): NativeNode {
|
|
1176
|
+
const layout = { grow: 3, min: { w: `${ROSTER_MIN_WIDTH}ch` } } as const;
|
|
1177
|
+
if (this.#rows.length === 0) {
|
|
1178
|
+
if (this.#loadingPersistedSubagents) {
|
|
1179
|
+
return node(
|
|
1180
|
+
"spinner",
|
|
1181
|
+
{ ...layout, label: [span("Loading saved agents…", "accent")] },
|
|
1182
|
+
undefined,
|
|
1183
|
+
"loading",
|
|
1184
|
+
);
|
|
1185
|
+
}
|
|
1186
|
+
return node(
|
|
1187
|
+
"col",
|
|
1188
|
+
layout,
|
|
1189
|
+
[
|
|
1190
|
+
text([span(`${theme.status.shadowed} `, "muted"), span("No agents in this session", "strong")]),
|
|
1191
|
+
text(
|
|
1192
|
+
[span("Finished, parked, and killed subagents remain with the session that created them.", "dim")],
|
|
1193
|
+
{ wrap: "word" },
|
|
1194
|
+
),
|
|
1195
|
+
text([span("Resume that session with omp-dev --continue, or spawn a task here.", "dim")], {
|
|
1196
|
+
wrap: "word",
|
|
1197
|
+
}),
|
|
1198
|
+
],
|
|
1199
|
+
"empty",
|
|
1200
|
+
);
|
|
1201
|
+
}
|
|
1202
|
+
if (this.#viewMode === "tree" && nativeTree) {
|
|
1203
|
+
return node("tree", { ...layout, nodes: this.#agentTreeNodes() }, undefined, "agents");
|
|
1204
|
+
}
|
|
1205
|
+
// Without native trees the parent view degrades to the parent-first list, naming each parent.
|
|
1206
|
+
const showParent = this.#viewMode === "roster" || !nativeTree;
|
|
1207
|
+
return node(
|
|
1208
|
+
"list",
|
|
1209
|
+
{ ...layout, selected: this.#rows[this.#selectedRow]?.id ?? null, virtual: true },
|
|
1210
|
+
this.#rows.map((ref, index) => this.#describeAgentItem(ref, index === this.#selectedRow, showParent)),
|
|
1211
|
+
"agents",
|
|
1212
|
+
);
|
|
1213
|
+
}
|
|
1214
|
+
|
|
1215
|
+
#agentLabel(ref: TRecord, selected: boolean, showParent: boolean): TspSpan[] {
|
|
1216
|
+
const label = [
|
|
1217
|
+
statusGlyphSpan(ref.status),
|
|
1218
|
+
span(" "),
|
|
1219
|
+
span(sanitizeDisplaySingleLine(ref.id), selected ? "accent strong" : "strong"),
|
|
1220
|
+
];
|
|
1221
|
+
if (showParent && ref.parentId && ref.parentId !== MAIN_AGENT_ID) {
|
|
1222
|
+
label.push(span(` ↳ ${sanitizeDisplaySingleLine(ref.parentId)}`, "dim"));
|
|
1223
|
+
}
|
|
1224
|
+
if (ref.kind === "advisor") label.push(span(" read-only", "warning"));
|
|
1225
|
+
const unread = this.#irc.unreadCount(ref.id);
|
|
1226
|
+
if (unread > 0) label.push(span(` ⧉ ${unread}`, "warning"));
|
|
1227
|
+
return label;
|
|
1228
|
+
}
|
|
1229
|
+
|
|
1230
|
+
#modelSpans(ref: TRecord, observed: ObservableSession | undefined): TspSpan[] | undefined {
|
|
1231
|
+
const spans: TspSpan[] = [];
|
|
1232
|
+
const modelRole = observed?.progress?.modelRole ?? ref.history?.modelRole;
|
|
1233
|
+
if (modelRole && this.#getRoleInfo) spans.push(roleBadgeSpan(modelRole, this.#getRoleInfo(modelRole)));
|
|
1234
|
+
const model = modelBadgeSpans(ref, observed);
|
|
1235
|
+
if (model) {
|
|
1236
|
+
if (spans.length > 0) spans.push(span(theme.sep.dot, "dim"));
|
|
1237
|
+
spans.push(...model);
|
|
1238
|
+
}
|
|
1239
|
+
return spans.length > 0 ? spans : undefined;
|
|
1240
|
+
}
|
|
1241
|
+
|
|
1242
|
+
#describeAgentItem(ref: TRecord, selected: boolean, showParent: boolean): NativeNode {
|
|
1243
|
+
const observed = this.#observableFor(ref.id);
|
|
1244
|
+
const metrics = this.#metricsFor(ref, observed);
|
|
1245
|
+
const age = formatAge(Math.max(1, Math.round((Date.now() - ref.lastActivity) / 1000)));
|
|
1246
|
+
const detail = [
|
|
1247
|
+
span(metrics ? `${metricsText(metrics)}${theme.sep.dot}${age}` : `usage${theme.sep.dot}${age}`, "dim"),
|
|
1248
|
+
];
|
|
1249
|
+
const task = observed?.description ?? observed?.progress?.task ?? ref.activity;
|
|
1250
|
+
if (task) detail.push(span(theme.sep.dot, "dim"), span(sanitizeDisplaySingleLine(task), "muted"));
|
|
1251
|
+
return node(
|
|
1252
|
+
"item",
|
|
1253
|
+
{ label: this.#agentLabel(ref, selected, showParent), detail, value: this.#modelSpans(ref, observed) },
|
|
1254
|
+
undefined,
|
|
1255
|
+
ref.id,
|
|
1256
|
+
);
|
|
1257
|
+
}
|
|
1258
|
+
|
|
1259
|
+
/** The parent projection as a disclosure tree; node ids are agent ids (the `item` of select events). */
|
|
1260
|
+
#agentTreeNodes(): AgentTreeEntry[] {
|
|
1261
|
+
const roots: AgentTreeEntry[] = [];
|
|
1262
|
+
const byId = new Map<string, AgentTreeEntry>();
|
|
1263
|
+
for (let index = 0; index < this.#rows.length; index++) {
|
|
1264
|
+
const ref = this.#rows[index]!;
|
|
1265
|
+
const observed = this.#observableFor(ref.id);
|
|
1266
|
+
const label = this.#agentLabel(ref, index === this.#selectedRow, false);
|
|
1267
|
+
const task = observed?.description ?? observed?.progress?.task ?? ref.activity;
|
|
1268
|
+
if (task) label.push(span(theme.sep.dot, "dim"), span(sanitizeDisplaySingleLine(task), "muted"));
|
|
1269
|
+
const model = this.#modelSpans(ref, observed);
|
|
1270
|
+
if (model) label.push(span(theme.sep.dot, "dim"), ...model);
|
|
1271
|
+
const entry: AgentTreeEntry = { id: ref.id, label, open: true };
|
|
1272
|
+
byId.set(ref.id, entry);
|
|
1273
|
+
const parentId = (this.#treeDepthById.get(ref.id) ?? 0) > 0 ? this.#treeParentById.get(ref.id) : undefined;
|
|
1274
|
+
const parent = parentId === undefined ? undefined : byId.get(parentId);
|
|
1275
|
+
if (!parent) roots.push(entry);
|
|
1276
|
+
else if (parent.children) parent.children.push(entry);
|
|
1277
|
+
else parent.children = [entry];
|
|
1278
|
+
}
|
|
1279
|
+
return roots;
|
|
1280
|
+
}
|
|
1281
|
+
|
|
1282
|
+
#describeDetail(ref: TRecord | undefined): NativeNode {
|
|
1283
|
+
const layout = {
|
|
1284
|
+
grow: 2,
|
|
1285
|
+
min: { w: `${DETAIL_MIN_WIDTH}ch` },
|
|
1286
|
+
gap: "sm",
|
|
1287
|
+
role: "omp.overlay.agentHub.detail",
|
|
1288
|
+
} as const;
|
|
1289
|
+
if (!ref) return node("col", layout, [text([span("Select an agent to inspect", "dim")])], "detail");
|
|
1290
|
+
const observed = this.#observableFor(ref.id);
|
|
1291
|
+
const metrics = this.#metricsFor(ref, observed);
|
|
1292
|
+
|
|
1293
|
+
const out: NativeChild[] = [
|
|
1294
|
+
text([
|
|
1295
|
+
statusGlyphSpan(ref.status),
|
|
1296
|
+
span(" "),
|
|
1297
|
+
span(sanitizeDisplaySingleLine(ref.displayName || ref.id), "strong"),
|
|
1298
|
+
]),
|
|
1299
|
+
];
|
|
1300
|
+
if (ref.displayName && ref.displayName !== ref.id)
|
|
1301
|
+
out.push(text([span(sanitizeDisplaySingleLine(ref.id), "dim")]));
|
|
1302
|
+
const state: NativeChild[] = [
|
|
1303
|
+
ref.status === "running"
|
|
1304
|
+
? node("spinner", { label: [span("running", "accent")] })
|
|
1305
|
+
: text([span(ref.status, statusTone(ref.status))]),
|
|
1306
|
+
];
|
|
1307
|
+
const duration = metrics ? formatMetricDuration(metrics) : undefined;
|
|
1308
|
+
if (duration) state.push(text([span(duration, "dim")]));
|
|
1309
|
+
state.push(
|
|
1310
|
+
text([span("last active", "dim")]),
|
|
1311
|
+
node("elapsed", { age: Math.max(0, Date.now() - ref.lastActivity), format: "short" }),
|
|
1312
|
+
text([span("ago", "dim")]),
|
|
1313
|
+
);
|
|
1314
|
+
out.push(node("row", { gap: "sm", wrap: true, align: "baseline" }, state, "state"));
|
|
1315
|
+
const model = this.#modelSpans(ref, observed);
|
|
1316
|
+
if (model) out.push(node("text", { spans: model }, undefined, "model"));
|
|
1317
|
+
out.push(node("kv", { items: this.#detailFacts(ref, observed, metrics), layout: "grid" }, undefined, "facts"));
|
|
1318
|
+
if (metrics?.contextTokens !== undefined && metrics.contextWindow) {
|
|
1319
|
+
const ratio = Math.max(0, Math.min(1, metrics.contextTokens / metrics.contextWindow));
|
|
1320
|
+
out.push(
|
|
1321
|
+
node(
|
|
1322
|
+
"progress",
|
|
1323
|
+
{
|
|
1324
|
+
value: ratio,
|
|
1325
|
+
label: `${formatNumber(metrics.contextTokens)}/${formatNumber(metrics.contextWindow)} ${Math.round(ratio * 100)}%`,
|
|
1326
|
+
},
|
|
1327
|
+
undefined,
|
|
1328
|
+
"context",
|
|
1329
|
+
),
|
|
1330
|
+
);
|
|
1331
|
+
}
|
|
1332
|
+
const recent = this.#activity.recent(ref.id, NATIVE_RECENT_ACTIVITY);
|
|
1333
|
+
out.push(
|
|
1334
|
+
node(
|
|
1335
|
+
"section",
|
|
1336
|
+
{ head: [span("Recent activity", "accent strong")] },
|
|
1337
|
+
[
|
|
1338
|
+
node(
|
|
1339
|
+
"list",
|
|
1340
|
+
{ empty: [span("No response or tool activity yet", "muted")] },
|
|
1341
|
+
recent.map(activity => this.#describeActivityItem(activity)),
|
|
1342
|
+
"recent",
|
|
1343
|
+
),
|
|
1344
|
+
],
|
|
1345
|
+
"recentActivity",
|
|
1346
|
+
),
|
|
1347
|
+
);
|
|
1348
|
+
return node("col", layout, out, "detail");
|
|
1349
|
+
}
|
|
1350
|
+
|
|
1351
|
+
/** Inspector facts shared by the fallback detail pane and the picker preview. */
|
|
1352
|
+
#detailFacts(
|
|
1353
|
+
ref: TRecord,
|
|
1354
|
+
observed: ObservableSession | undefined,
|
|
1355
|
+
metrics: AgentMetrics | undefined,
|
|
1356
|
+
): Array<{ k: string; v: TspSpan[] | string }> {
|
|
1357
|
+
const progress = observed?.progress;
|
|
1358
|
+
const children = this.#childrenByParent.get(ref.id) ?? [];
|
|
1359
|
+
const dot = theme.sep.dot;
|
|
1360
|
+
const facts: Array<{ k: string; v: TspSpan[] | string }> = [];
|
|
1361
|
+
const task = observed?.description ?? progress?.task ?? ref.activity;
|
|
1362
|
+
if (task) facts.push({ k: "Task", v: taskSummary(task) });
|
|
1363
|
+
const current = progress?.currentTool
|
|
1364
|
+
? `${progress.currentTool}${progress.currentToolArgs ? `${dot}${progress.currentToolArgs}` : ""}`
|
|
1365
|
+
: (progress?.lastIntent ?? ref.activity);
|
|
1366
|
+
if (current) {
|
|
1367
|
+
const value = [span(sanitizeDisplaySingleLine(current))];
|
|
1368
|
+
if (progress?.retryState) {
|
|
1369
|
+
value.push(
|
|
1370
|
+
span(`${dot}retry ${progress.retryState.attempt}/${progress.retryState.maxAttempts}`, "warning"),
|
|
1371
|
+
);
|
|
1372
|
+
}
|
|
1373
|
+
facts.push({ k: "Current", v: value });
|
|
1374
|
+
}
|
|
1375
|
+
facts.push({ k: "Usage", v: metrics ? metricsText(metrics) : [span("usage —", "dim")] });
|
|
1376
|
+
facts.push({
|
|
1377
|
+
k: "Lineage",
|
|
1378
|
+
v: `Spawned by ${sanitizeDisplaySingleLine(ref.parentId ?? MAIN_AGENT_ID)}${children.length > 0 ? `${dot}${children.length} ${children.length === 1 ? "child" : "children"}` : ""}`,
|
|
1379
|
+
});
|
|
1380
|
+
if (children.length > 0) {
|
|
1381
|
+
const named = children.slice(0, NATIVE_CHILD_IDS).map(child => sanitizeDisplaySingleLine(child.id));
|
|
1382
|
+
const more = children.length - named.length;
|
|
1383
|
+
facts.push({ k: "Children", v: [span(`${named.join(", ")}${more > 0 ? `, … +${more}` : ""}`, "dim")] });
|
|
1384
|
+
}
|
|
1385
|
+
facts.push({ k: "Registered", v: [span(formatLocalDateTimeWithOffset(new Date(ref.createdAt)), "dim")] });
|
|
1386
|
+
facts.push({
|
|
1387
|
+
k: "Changes",
|
|
1388
|
+
v: [
|
|
1389
|
+
span(
|
|
1390
|
+
ref.kind === "advisor" || ref.history?.readOnly
|
|
1391
|
+
? "Read-only · 0 LoC"
|
|
1392
|
+
: "Shared workspace · per-agent LoC not attributable",
|
|
1393
|
+
"dim",
|
|
1394
|
+
),
|
|
1395
|
+
],
|
|
1396
|
+
});
|
|
1397
|
+
const artifacts = ref.history;
|
|
1398
|
+
if (artifacts?.outputPath) {
|
|
1399
|
+
facts.push({
|
|
1400
|
+
k: "Output",
|
|
1401
|
+
v: [span(shortenPath(artifacts.outputPath), "path", { href: `file://${artifacts.outputPath}` })],
|
|
1402
|
+
});
|
|
1403
|
+
}
|
|
1404
|
+
if (artifacts?.patchPath) facts.push({ k: "Patch", v: [span(shortenPath(artifacts.patchPath), "path")] });
|
|
1405
|
+
for (const nestedPath of artifacts?.nestedPatchPaths ?? []) {
|
|
1406
|
+
facts.push({ k: "Nested patch", v: [span(shortenPath(nestedPath), "path")] });
|
|
1407
|
+
}
|
|
1408
|
+
if (artifacts?.branchName) facts.push({ k: "Worktree branch", v: [span(artifacts.branchName, "code")] });
|
|
1409
|
+
return facts;
|
|
1410
|
+
}
|
|
1411
|
+
|
|
1412
|
+
#describeActivityItem(activity: AgentActivityRow): NativeNode {
|
|
1413
|
+
const ref = this.#registry.get(activity.agentId);
|
|
1414
|
+
const observed = this.#observedById.get(activity.agentId);
|
|
1415
|
+
const role = observed?.progress?.modelRole ?? ref?.history?.modelRole;
|
|
1416
|
+
const label = [activityGlyphSpan(activity), span(" ")];
|
|
1417
|
+
if (role && this.#getRoleInfo) label.push(roleBadgeSpan(role, this.#getRoleInfo(role)), span(" "));
|
|
1418
|
+
const title = activity.kind === "tool" ? (activity.toolName ?? activity.title) : activity.title;
|
|
1419
|
+
label.push(
|
|
1420
|
+
span(sanitizeDisplaySingleLine(activity.agentId), "strong"),
|
|
1421
|
+
span(" "),
|
|
1422
|
+
span(sanitizeDisplaySingleLine(title), activity.kind === "response" ? "success" : "muted"),
|
|
1423
|
+
);
|
|
1424
|
+
return node(
|
|
1425
|
+
"item",
|
|
1426
|
+
{
|
|
1427
|
+
label,
|
|
1428
|
+
detail: sanitizeDisplaySingleLine(activity.summary),
|
|
1429
|
+
value: [span(activityClock(activity.timestamp), "dim")],
|
|
1430
|
+
},
|
|
1431
|
+
undefined,
|
|
1432
|
+
activity.id,
|
|
1433
|
+
);
|
|
1434
|
+
}
|
|
1435
|
+
|
|
1436
|
+
#describeActivity(): NativeChild[] {
|
|
1437
|
+
const selectedAgent = this.#rows[this.#selectedRow]?.id;
|
|
1438
|
+
const scope =
|
|
1439
|
+
this.#activityScope === "all"
|
|
1440
|
+
? "all agents"
|
|
1441
|
+
: this.#activityScope === "agent"
|
|
1442
|
+
? (selectedAgent ?? "selected agent")
|
|
1443
|
+
: `${selectedAgent ?? "selected"} subtree`;
|
|
1444
|
+
const dot = theme.sep.dot;
|
|
1445
|
+
const status: NativeChild[] = [
|
|
1446
|
+
text([
|
|
1447
|
+
span(
|
|
1448
|
+
`${sanitizeDisplaySingleLine(scope)}${dot}${this.#activityFilter}${dot}${this.#activityFollow ? "following" : "paused"}`,
|
|
1449
|
+
"dim",
|
|
1450
|
+
),
|
|
1451
|
+
]),
|
|
1452
|
+
this.#activitySearchEditing
|
|
1453
|
+
? node(
|
|
1454
|
+
"input",
|
|
1455
|
+
{
|
|
1456
|
+
text: this.#activitySearch.getValue(),
|
|
1457
|
+
cursor: this.#activitySearch.getCursor(),
|
|
1458
|
+
prompt: [span("search: ", "muted")],
|
|
1459
|
+
},
|
|
1460
|
+
undefined,
|
|
1461
|
+
"search",
|
|
1462
|
+
)
|
|
1463
|
+
: text([
|
|
1464
|
+
span(
|
|
1465
|
+
this.#activitySearch.getValue() ? `search: ${this.#activitySearch.getValue()}` : "search: —",
|
|
1466
|
+
"dim",
|
|
1467
|
+
),
|
|
1468
|
+
]),
|
|
1469
|
+
];
|
|
1470
|
+
const selected = this.#activityRows[this.#selectedActivityRow]?.id ?? null;
|
|
1471
|
+
return [
|
|
1472
|
+
node("row", { gap: "md", wrap: true, align: "baseline" }, status, "status"),
|
|
1473
|
+
node(
|
|
1474
|
+
"list",
|
|
1475
|
+
{
|
|
1476
|
+
selected,
|
|
1477
|
+
filter: this.#activitySearch.getValue() || undefined,
|
|
1478
|
+
virtual: true,
|
|
1479
|
+
grow: 1,
|
|
1480
|
+
empty: [
|
|
1481
|
+
span(
|
|
1482
|
+
this.#activitySearch.getValue() ? "No matching activity" : "No agent activity recorded yet",
|
|
1483
|
+
"muted",
|
|
1484
|
+
),
|
|
1485
|
+
],
|
|
1486
|
+
},
|
|
1487
|
+
this.#activityRows.map(activity => this.#describeActivityItem(activity)),
|
|
1488
|
+
"activity",
|
|
1489
|
+
),
|
|
1490
|
+
hintsRow(ACTIVITY_HINTS),
|
|
1491
|
+
];
|
|
1492
|
+
}
|
|
1493
|
+
|
|
532
1494
|
// ========================================================================
|
|
533
1495
|
// Live data plumbing
|
|
534
1496
|
// ========================================================================
|
|
@@ -582,7 +1544,7 @@ export class AgentHubOverlayComponent<TRecord extends AgentRecordLike = AgentRec
|
|
|
582
1544
|
if (!rowOrder.has(ref)) rowOrder.set(ref, this.#nextRowOrder++);
|
|
583
1545
|
}
|
|
584
1546
|
}
|
|
585
|
-
const query = this.#agentFilter.trim();
|
|
1547
|
+
const query = this.#agentFilter.getValue().trim();
|
|
586
1548
|
const rosterRows =
|
|
587
1549
|
query.length > 0
|
|
588
1550
|
? ordered.filter(ref => fuzzyMatch(query, `${ref.id} ${ref.displayName ?? ""}`).matches)
|
|
@@ -688,7 +1650,7 @@ export class AgentHubOverlayComponent<TRecord extends AgentRecordLike = AgentRec
|
|
|
688
1650
|
let rows = this.#activity.query({
|
|
689
1651
|
agentIds: this.#activityAgentIds(),
|
|
690
1652
|
kinds,
|
|
691
|
-
search: this.#activitySearch,
|
|
1653
|
+
search: this.#activitySearch.getValue(),
|
|
692
1654
|
limit: 2_000,
|
|
693
1655
|
});
|
|
694
1656
|
if (this.#activityFilter === "errors") rows = rows.filter(row => row.status === "error");
|
|
@@ -739,9 +1701,9 @@ export class AgentHubOverlayComponent<TRecord extends AgentRecordLike = AgentRec
|
|
|
739
1701
|
? (selectedAgent ?? "selected agent")
|
|
740
1702
|
: `${selectedAgent ?? "selected"} subtree`;
|
|
741
1703
|
const search = this.#activitySearchEditing
|
|
742
|
-
? theme.fg("accent", `search: ${this.#activitySearch}
|
|
743
|
-
: this.#activitySearch
|
|
744
|
-
? `search: ${this.#activitySearch}`
|
|
1704
|
+
? theme.fg("accent", `search: ${filterText(this.#activitySearch)}`)
|
|
1705
|
+
: this.#activitySearch.getValue()
|
|
1706
|
+
? `search: ${this.#activitySearch.getValue()}`
|
|
745
1707
|
: "search: —";
|
|
746
1708
|
body.push(
|
|
747
1709
|
theme.fg(
|
|
@@ -753,7 +1715,12 @@ export class AgentHubOverlayComponent<TRecord extends AgentRecordLike = AgentRec
|
|
|
753
1715
|
|
|
754
1716
|
const budget = Math.max(0, contentRows - body.length);
|
|
755
1717
|
if (this.#activityRows.length === 0 && budget > 0) {
|
|
756
|
-
body.push(
|
|
1718
|
+
body.push(
|
|
1719
|
+
theme.fg(
|
|
1720
|
+
"muted",
|
|
1721
|
+
this.#activitySearch.getValue() ? "No matching activity" : "No agent activity recorded yet",
|
|
1722
|
+
),
|
|
1723
|
+
);
|
|
757
1724
|
} else if (budget > 0) {
|
|
758
1725
|
const selected = Math.min(this.#selectedActivityRow, this.#activityRows.length - 1);
|
|
759
1726
|
const start = this.#activityFollow
|
|
@@ -839,8 +1806,11 @@ export class AgentHubOverlayComponent<TRecord extends AgentRecordLike = AgentRec
|
|
|
839
1806
|
|
|
840
1807
|
#footer(showingNarrowDetails: boolean, availableWidth: number): string {
|
|
841
1808
|
const nextView = this.#viewMode === "roster" ? "by parent" : "flat";
|
|
842
|
-
const filter =
|
|
843
|
-
|
|
1809
|
+
const filter = this.#agentFilterEditing
|
|
1810
|
+
? `/${filterText(this.#agentFilter)} · `
|
|
1811
|
+
: this.#agentFilter.getValue()
|
|
1812
|
+
? `/${this.#agentFilter.getValue()} · `
|
|
1813
|
+
: "";
|
|
844
1814
|
if (showingNarrowDetails) {
|
|
845
1815
|
return theme.fg(
|
|
846
1816
|
"dim",
|
|
@@ -1312,12 +2282,13 @@ export class AgentHubOverlayComponent<TRecord extends AgentRecordLike = AgentRec
|
|
|
1312
2282
|
#handleActivitySearchInput(keyData: string): void {
|
|
1313
2283
|
if (matchesKey(keyData, "escape") || matchesKey(keyData, "enter") || keyData === "\r" || keyData === "\n") {
|
|
1314
2284
|
this.#activitySearchEditing = false;
|
|
1315
|
-
} else if (matchesKey(keyData, "backspace")) {
|
|
1316
|
-
this.#activitySearch = this.#activitySearch.slice(0, -1);
|
|
1317
|
-
} else if (keyData.length === 1 && keyData >= " " && keyData !== "\u007f") {
|
|
1318
|
-
this.#activitySearch += keyData;
|
|
1319
2285
|
} else {
|
|
1320
|
-
|
|
2286
|
+
const before = this.#activitySearch.getValue();
|
|
2287
|
+
if (!this.#activitySearch.handleInput(keyData)) return;
|
|
2288
|
+
if (this.#activitySearch.getValue() === before) {
|
|
2289
|
+
this.#requestRender();
|
|
2290
|
+
return;
|
|
2291
|
+
}
|
|
1321
2292
|
}
|
|
1322
2293
|
this.#refreshActivityRows();
|
|
1323
2294
|
this.#requestRender();
|
|
@@ -1325,8 +2296,8 @@ export class AgentHubOverlayComponent<TRecord extends AgentRecordLike = AgentRec
|
|
|
1325
2296
|
|
|
1326
2297
|
#handleActivityInput(keyData: string): void {
|
|
1327
2298
|
if (matchesKey(keyData, "escape")) {
|
|
1328
|
-
if (this.#activitySearch) {
|
|
1329
|
-
this.#activitySearch
|
|
2299
|
+
if (this.#activitySearch.getValue()) {
|
|
2300
|
+
this.#activitySearch.setValue("");
|
|
1330
2301
|
this.#refreshActivityRows();
|
|
1331
2302
|
this.#requestRender();
|
|
1332
2303
|
} else {
|
|
@@ -1344,25 +2315,17 @@ export class AgentHubOverlayComponent<TRecord extends AgentRecordLike = AgentRec
|
|
|
1344
2315
|
return;
|
|
1345
2316
|
}
|
|
1346
2317
|
if (keyData === " ") {
|
|
1347
|
-
this.#
|
|
1348
|
-
if (this.#activityFollow && this.#activityRows.length > 0) {
|
|
1349
|
-
this.#selectedActivityRow = this.#activityRows.length - 1;
|
|
1350
|
-
}
|
|
1351
|
-
this.#requestRender();
|
|
2318
|
+
this.#toggleActivityFollow();
|
|
1352
2319
|
return;
|
|
1353
2320
|
}
|
|
1354
2321
|
if (keyData === "f") {
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
this.#requestRender();
|
|
2322
|
+
this.#setActivityFilter(
|
|
2323
|
+
ACTIVITY_FILTERS[(ACTIVITY_FILTERS.indexOf(this.#activityFilter) + 1) % ACTIVITY_FILTERS.length]!,
|
|
2324
|
+
);
|
|
1359
2325
|
return;
|
|
1360
2326
|
}
|
|
1361
2327
|
if (keyData === "s") {
|
|
1362
|
-
|
|
1363
|
-
this.#activityScope = scopes[(scopes.indexOf(this.#activityScope) + 1) % scopes.length]!;
|
|
1364
|
-
this.#refreshActivityRows();
|
|
1365
|
-
this.#requestRender();
|
|
2328
|
+
this.#cycleActivityScope();
|
|
1366
2329
|
return;
|
|
1367
2330
|
}
|
|
1368
2331
|
if (matchesKey(keyData, "j") || matchesSelectDown(keyData)) {
|
|
@@ -1387,25 +2350,58 @@ export class AgentHubOverlayComponent<TRecord extends AgentRecordLike = AgentRec
|
|
|
1387
2350
|
}
|
|
1388
2351
|
}
|
|
1389
2352
|
|
|
2353
|
+
/** Space on the activity log: follow the newest row, or hold the selection. */
|
|
2354
|
+
#toggleActivityFollow(): void {
|
|
2355
|
+
this.#activityFollow = !this.#activityFollow;
|
|
2356
|
+
if (this.#activityFollow && this.#activityRows.length > 0) {
|
|
2357
|
+
this.#selectedActivityRow = this.#activityRows.length - 1;
|
|
2358
|
+
}
|
|
2359
|
+
this.#requestRender();
|
|
2360
|
+
}
|
|
2361
|
+
|
|
2362
|
+
/** `f` cycles through these; the picker's filter scopes set one directly. */
|
|
2363
|
+
#setActivityFilter(filter: ActivityFilter): void {
|
|
2364
|
+
this.#activityFilter = filter;
|
|
2365
|
+
this.#refreshActivityRows();
|
|
2366
|
+
this.#requestRender();
|
|
2367
|
+
}
|
|
2368
|
+
|
|
2369
|
+
/** `s`: all agents → the selected agent → its subtree. */
|
|
2370
|
+
#cycleActivityScope(): void {
|
|
2371
|
+
const scopes: ActivityScope[] = ["all", "agent", "subtree"];
|
|
2372
|
+
this.#activityScope = scopes[(scopes.indexOf(this.#activityScope) + 1) % scopes.length]!;
|
|
2373
|
+
this.#refreshActivityRows();
|
|
2374
|
+
this.#requestRender();
|
|
2375
|
+
}
|
|
2376
|
+
|
|
2377
|
+
/** `t`: flat roster ⇄ grouped by parent. */
|
|
2378
|
+
#toggleViewMode(): void {
|
|
2379
|
+
this.#hoveredRow = null;
|
|
2380
|
+
this.#viewMode = this.#viewMode === "roster" ? "tree" : "roster";
|
|
2381
|
+
this.#refreshRows();
|
|
2382
|
+
this.#requestRender();
|
|
2383
|
+
}
|
|
2384
|
+
|
|
1390
2385
|
#handleTableInput(keyData: string): void {
|
|
1391
2386
|
if (this.#agentFilterEditing) {
|
|
1392
2387
|
if (matchesKey(keyData, "escape") || matchesKey(keyData, "enter") || keyData === "\r" || keyData === "\n") {
|
|
1393
2388
|
this.#agentFilterEditing = false;
|
|
1394
|
-
if (matchesKey(keyData, "escape")) this.#agentFilter
|
|
1395
|
-
} else if (matchesKey(keyData, "backspace")) {
|
|
1396
|
-
this.#agentFilter = this.#agentFilter.slice(0, -1);
|
|
1397
|
-
} else if (keyData.length === 1 && keyData >= " " && keyData !== "\u007f") {
|
|
1398
|
-
this.#agentFilter += keyData;
|
|
2389
|
+
if (matchesKey(keyData, "escape")) this.#agentFilter.setValue("");
|
|
1399
2390
|
} else {
|
|
1400
|
-
|
|
2391
|
+
const before = this.#agentFilter.getValue();
|
|
2392
|
+
if (!this.#agentFilter.handleInput(keyData)) return;
|
|
2393
|
+
if (this.#agentFilter.getValue() === before) {
|
|
2394
|
+
this.#requestRender();
|
|
2395
|
+
return;
|
|
2396
|
+
}
|
|
1401
2397
|
}
|
|
1402
2398
|
this.#refreshRows();
|
|
1403
2399
|
this.#requestRender();
|
|
1404
2400
|
return;
|
|
1405
2401
|
}
|
|
1406
2402
|
if (matchesKey(keyData, "escape")) {
|
|
1407
|
-
if (this.#agentFilter) {
|
|
1408
|
-
this.#agentFilter
|
|
2403
|
+
if (this.#agentFilter.getValue()) {
|
|
2404
|
+
this.#agentFilter.setValue("");
|
|
1409
2405
|
this.#refreshRows();
|
|
1410
2406
|
this.#requestRender();
|
|
1411
2407
|
} else if (this.#narrowDetailsOpen && this.#split.mode !== "split") {
|
|
@@ -1437,10 +2433,7 @@ export class AgentHubOverlayComponent<TRecord extends AgentRecordLike = AgentRec
|
|
|
1437
2433
|
}
|
|
1438
2434
|
}
|
|
1439
2435
|
if (keyData === "t") {
|
|
1440
|
-
this.#
|
|
1441
|
-
this.#viewMode = this.#viewMode === "roster" ? "tree" : "roster";
|
|
1442
|
-
this.#refreshRows();
|
|
1443
|
-
this.#requestRender();
|
|
2436
|
+
this.#toggleViewMode();
|
|
1444
2437
|
return;
|
|
1445
2438
|
}
|
|
1446
2439
|
if (matchesKey(keyData, "left")) {
|