@pellux/goodvibes-tui 2.0.21 → 2.1.1
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 +65 -1
- package/README.md +30 -29
- package/docs/README.md +8 -5
- package/docs/foundation-artifacts/operator-contract.json +1 -1
- package/package.json +28 -19
- package/src/cli/management.ts +5 -2
- package/src/cli/tui-startup.ts +34 -39
- package/src/core/agent-view-render.ts +157 -0
- package/src/core/approval-alert.ts +49 -11
- package/src/core/bookmark-navigation.ts +7 -1
- package/src/core/budget-breach-notifier.ts +27 -8
- package/src/core/compaction-receipt.ts +1 -1
- package/src/core/context-usage.ts +1 -1
- package/src/core/conversation-compaction-render.ts +3 -3
- package/src/core/conversation-display-only.ts +86 -0
- package/src/core/conversation-event-navigation.ts +27 -0
- package/src/core/conversation-line-cache.ts +216 -447
- package/src/core/conversation-render-context.ts +20 -27
- package/src/core/conversation-rendering.ts +157 -575
- package/src/core/conversation-splash-state.ts +6 -1
- package/src/core/conversation-types.ts +35 -0
- package/src/core/conversation-usage.ts +12 -0
- package/src/core/conversation.ts +130 -131
- package/src/core/long-task-notifier.ts +100 -31
- package/src/core/notices.ts +75 -0
- package/src/core/pairing-tailscale-gateway.ts +1 -1
- package/src/core/process-output-log.ts +143 -0
- package/src/core/rewind-receipt.ts +1 -1
- package/src/core/session-focus.ts +190 -0
- package/src/core/session-resume-core.ts +23 -81
- package/src/core/stream-event-wiring.ts +31 -5
- package/src/core/system-message-noise.ts +2 -2
- package/src/core/system-message-router.ts +16 -18
- package/src/core/turn-event-wiring.ts +272 -33
- package/src/core/work-tree-focus.ts +290 -0
- package/src/core/work-tree-fold-store.ts +143 -0
- package/src/core/work-tree-model.ts +799 -0
- package/src/core/work-tree-render.ts +384 -0
- package/src/core/work-tree-sources.ts +184 -0
- package/src/core/work-tree-wiring.ts +176 -0
- package/src/core/workstream-notification.ts +36 -6
- package/src/core/wrfc-notice-titles.ts +37 -0
- package/src/input/agents-modal-text.ts +119 -0
- package/src/input/agents-modal.ts +699 -0
- package/src/input/bookmark-modal.ts +33 -9
- package/src/input/changes-git.ts +251 -0
- package/src/input/changes-modal.ts +629 -0
- package/src/input/changes-model.ts +125 -0
- package/src/input/command-palette-types.ts +41 -0
- package/src/input/command-palette.ts +272 -0
- package/src/input/command-reference.ts +2 -2
- package/src/input/command-registry.ts +77 -35
- package/src/input/commands/acp-runtime.ts +19 -9
- package/src/input/commands/channel-runtime.ts +2 -2
- package/src/input/commands/ci-runtime.ts +1 -1
- package/src/input/commands/codebase-runtime.ts +1 -1
- package/src/input/commands/config.ts +7 -1
- package/src/input/commands/context-window.ts +25 -19
- package/src/input/commands/control-room-runtime.ts +18 -18
- package/src/input/commands/conversation-runtime.ts +0 -1
- package/src/input/commands/cost-runtime.ts +29 -20
- package/src/input/commands/diff-runtime.ts +35 -181
- package/src/input/commands/eval.ts +1 -1
- package/src/input/commands/experience-runtime.ts +3 -7
- package/src/input/commands/fresh-conversation.ts +86 -0
- package/src/input/commands/git-runtime.ts +5 -22
- package/src/input/commands/graph-runtime.ts +1 -1
- package/src/input/commands/guidance-runtime.ts +1 -1
- package/src/input/commands/health-runtime.ts +8 -9
- package/src/input/commands/hooks-runtime.ts +2 -2
- package/src/input/commands/hosted-runtime.ts +3 -3
- package/src/input/commands/incident-runtime.ts +3 -3
- package/src/input/commands/intelligence-runtime.ts +6 -9
- package/src/input/commands/knowledge.ts +2 -2
- package/src/input/commands/legacy-panel-command.ts +57 -0
- package/src/input/commands/local-auth-runtime.ts +8 -9
- package/src/input/commands/local-provider-runtime.ts +7 -4
- package/src/input/commands/local-runtime.ts +12 -15
- package/src/input/commands/local-setup-review.ts +1 -1
- package/src/input/commands/local-setup.ts +13 -19
- package/src/input/commands/memory.ts +1 -1
- package/src/input/commands/notify-runtime.ts +9 -0
- package/src/input/commands/operator-runtime.ts +10 -12
- package/src/input/commands/permissions-provenance.ts +3 -3
- package/src/input/commands/planning-runtime.ts +7 -7
- package/src/input/commands/platform-sandbox-runtime.ts +2 -2
- package/src/input/commands/platform-services-runtime.ts +1 -1
- package/src/input/commands/policy-dispatch.ts +2 -2
- package/src/input/commands/policy.ts +3 -3
- package/src/input/commands/provider.ts +1 -1
- package/src/input/commands/remote-runtime.ts +1 -1
- package/src/input/commands/review-runtime.ts +39 -94
- package/src/input/commands/rewind-runtime.ts +81 -108
- package/src/input/commands/runtime-services.ts +15 -17
- package/src/input/commands/services-runtime.ts +2 -2
- package/src/input/commands/session-content.ts +21 -5
- package/src/input/commands/session-workflow.ts +13 -38
- package/src/input/commands/settings-sync-runtime.ts +2 -2
- package/src/input/commands/shell-core.ts +19 -4
- package/src/input/commands/skills-runtime.ts +2 -2
- package/src/input/commands/status-runtime.ts +13 -0
- package/src/input/commands/subscription-runtime.ts +1 -1
- package/src/input/commands/tasks-runtime.ts +2 -8
- package/src/input/commands/work-plan-runtime.ts +4 -4
- package/src/input/commands/workstream-attempts.ts +24 -47
- package/src/input/commands/workstream-runtime.ts +2 -2
- package/src/input/commands/worktree-runtime.ts +3 -3
- package/src/input/commands.ts +3 -1
- package/src/input/composer-editor.ts +44 -0
- package/src/input/concealed-input.ts +1 -1
- package/src/input/config-modal-types.ts +20 -8
- package/src/input/config-modal.ts +106 -105
- package/src/input/confirm-dialog-types.ts +28 -0
- package/src/input/confirm-dialog.ts +70 -0
- package/src/input/delete-key-policy.ts +3 -3
- package/src/input/feed-context-factory.ts +15 -16
- package/src/input/handler-command-route.ts +7 -42
- package/src/input/handler-content-actions.ts +16 -15
- package/src/input/handler-feed-routes.ts +26 -211
- package/src/input/handler-feed.ts +68 -95
- package/src/input/handler-interactions.ts +5 -3
- package/src/input/handler-modal-routes.ts +215 -216
- package/src/input/handler-modal-stack.ts +81 -11
- package/src/input/handler-modal-token-routes.ts +4 -0
- package/src/input/handler-onboarding.ts +0 -1
- package/src/input/handler-picker-routes.ts +65 -107
- package/src/input/handler-session-view-route.ts +149 -0
- package/src/input/handler-shortcuts.ts +16 -154
- package/src/input/handler-types.ts +4 -2
- package/src/input/handler-ui-state.ts +45 -15
- package/src/input/handler-work-tree-route.ts +123 -0
- package/src/input/handler.ts +25 -70
- package/src/input/keybindings.ts +16 -71
- package/src/input/masked-entry-modal.ts +117 -0
- package/src/input/mcp-workspace.ts +48 -17
- package/src/input/model-picker-filter.ts +8 -0
- package/src/input/model-picker-open.ts +99 -0
- package/src/input/model-picker-types.ts +1 -1
- package/src/input/model-picker.ts +92 -9
- package/src/input/notifications-modal.ts +123 -0
- package/src/input/overlay-filter.ts +26 -0
- package/src/input/profile-picker-modal.ts +37 -8
- package/src/input/search.ts +3 -3
- package/src/input/selection-modal-queue.ts +2 -0
- package/src/input/selection-modal.ts +28 -16
- package/src/input/session-picker-modal.ts +33 -8
- package/src/input/settings-modal-activation.ts +7 -2
- package/src/input/settings-modal-data.ts +11 -6
- package/src/input/settings-modal-types.ts +1 -1
- package/src/input/settings-modal.ts +20 -20
- package/src/input/submission-intent.ts +1 -1
- package/src/input/submission-router.ts +4 -9
- package/src/input/surface-modal-host.ts +110 -0
- package/src/input/theme-settings-actions.ts +82 -0
- package/src/input/usage-modal.ts +183 -0
- package/src/input/views.ts +97 -0
- package/src/main.ts +175 -165
- package/src/permissions/hunk-selection.ts +1 -1
- package/src/permissions/prompt-card.ts +511 -0
- package/src/permissions/prompt.ts +59 -622
- package/src/renderer/agents-modal.ts +310 -0
- package/src/renderer/autocomplete-overlay.ts +42 -220
- package/src/renderer/back-to-bottom.ts +80 -0
- package/src/renderer/block-actions-overlay.ts +30 -48
- package/src/renderer/bookmark-modal.ts +48 -79
- package/src/renderer/changes-modal.ts +325 -0
- package/src/renderer/code-block.ts +184 -106
- package/src/renderer/command-palette.ts +93 -0
- package/src/renderer/composer-fragments.ts +4 -4
- package/src/renderer/composer.ts +146 -0
- package/src/renderer/compositor.ts +26 -160
- package/src/renderer/config-modal.ts +185 -81
- package/src/renderer/confirm-dialog.ts +50 -0
- package/src/renderer/context-inspector.ts +116 -154
- package/src/renderer/context-status-hint.ts +4 -4
- package/src/renderer/conversation-layout.ts +20 -0
- package/src/renderer/conversation-overlays.ts +94 -84
- package/src/renderer/conversation-surface.ts +19 -38
- package/src/renderer/diff-tint.ts +98 -0
- package/src/renderer/file-picker-overlay.ts +63 -90
- package/src/renderer/footer-targets.ts +38 -0
- package/src/renderer/git-status.ts +3 -4
- package/src/renderer/header-line.ts +140 -117
- package/src/renderer/help-overlay.ts +133 -219
- package/src/renderer/history-search-overlay.ts +15 -12
- package/src/renderer/lane-graph/bead.ts +800 -0
- package/src/renderer/lane-graph/glyphs.ts +134 -0
- package/src/renderer/lane-graph/layout.ts +248 -0
- package/src/renderer/lane-graph/paint.ts +537 -0
- package/src/renderer/lane-graph/semantic-memo.ts +77 -0
- package/src/renderer/layout-engine.ts +2 -57
- package/src/renderer/layout.ts +7 -13
- package/src/renderer/markdown-inline.ts +2 -1
- package/src/renderer/markdown-table.ts +17 -12
- package/src/renderer/markdown.ts +92 -34
- package/src/renderer/masked-entry-modal.ts +78 -0
- package/src/renderer/mcp-workspace.ts +166 -203
- package/src/renderer/model-workspace.ts +381 -436
- package/src/renderer/notifications-modal.ts +116 -0
- package/src/renderer/onboarding/onboarding-wizard.ts +178 -588
- package/src/renderer/process-view.ts +192 -0
- package/src/renderer/profile-picker-modal.ts +58 -110
- package/src/renderer/search-overlay.ts +6 -3
- package/src/renderer/selection-modal-overlay.ts +87 -327
- package/src/renderer/semantic-diff.ts +2 -2
- package/src/renderer/session-chips.ts +102 -0
- package/src/renderer/session-picker-modal.ts +177 -297
- package/src/renderer/settings-modal-connections.ts +14 -23
- package/src/renderer/settings-modal-context.ts +367 -0
- package/src/renderer/settings-modal-helpers.ts +6 -11
- package/src/renderer/settings-modal.ts +322 -726
- package/src/renderer/shell-surface.ts +277 -175
- package/src/renderer/shortcuts-overlay.ts +217 -0
- package/src/renderer/status-line.ts +475 -0
- package/src/renderer/surface-compose.ts +174 -0
- package/src/renderer/surface-kit-extra.ts +303 -0
- package/src/renderer/surface-kit-list.ts +366 -0
- package/src/renderer/surface-kit-parts.ts +360 -0
- package/src/renderer/surface-kit.ts +568 -0
- package/src/renderer/syntax-highlighter.ts +280 -197
- package/src/renderer/syntax-theme.ts +51 -0
- package/src/renderer/system-message.ts +0 -1
- package/src/renderer/term-caps.ts +42 -1
- package/src/renderer/terminal-bg-probe.ts +116 -19
- package/src/renderer/terminal-palette-probe.ts +322 -0
- package/src/renderer/terminal-palette-reader.ts +183 -0
- package/src/renderer/terminal-palette.ts +58 -0
- package/src/renderer/theme-mode-config.ts +16 -1
- package/src/renderer/theme.ts +234 -207
- package/src/renderer/thinking.ts +0 -1
- package/src/renderer/throbber.ts +188 -0
- package/src/renderer/toast-center.ts +119 -0
- package/src/renderer/ui-factory.ts +39 -602
- package/src/renderer/ui-primitives.ts +8 -25
- package/src/renderer/usage-modal.ts +353 -0
- package/src/renderer/user-message.ts +58 -0
- package/src/renderer/voice-capture-chip.ts +53 -0
- package/src/runtime/bootstrap-command-context.ts +0 -5
- package/src/runtime/bootstrap-command-parts.ts +38 -89
- package/src/runtime/bootstrap-core.ts +13 -15
- package/src/runtime/bootstrap-hook-bridge.ts +12 -21
- package/src/runtime/bootstrap-notifier-sync.ts +17 -1
- package/src/runtime/bootstrap-shell.ts +16 -50
- package/src/runtime/bootstrap.ts +14 -14
- package/src/runtime/client/fleet-union.ts +5 -5
- package/src/runtime/client/hosted-exit.ts +2 -2
- package/src/runtime/client/hosted-sessions.ts +1 -1
- package/src/runtime/client/spine-adoption.ts +1 -1
- package/src/runtime/context-accounting-source.ts +5 -4
- package/src/runtime/index.ts +1 -0
- package/src/runtime/notification-dispatch.ts +102 -29
- package/src/runtime/recovery-offer-wiring.ts +4 -1
- package/src/runtime/recovery-prompt.ts +31 -15
- package/src/runtime/runtime-services-types.ts +10 -6
- package/src/runtime/services.ts +17 -12
- package/src/runtime/session-continuity-hints.ts +2 -5
- package/src/runtime/session-inbound-inputs.ts +1 -1
- package/src/runtime/session-resume-liveness-confirm.ts +14 -2
- package/src/runtime/startup-typeahead-gate.ts +103 -0
- package/src/runtime/store/domains/domain-read-matrix.ts +0 -1
- package/src/runtime/store/domains/index.ts +0 -8
- package/src/runtime/store/selectors/index.ts +0 -28
- package/src/runtime/store/state.ts +0 -8
- package/src/runtime/ui-services.ts +4 -6
- package/src/runtime/usage-tracker.ts +348 -0
- package/src/runtime/wrfc-persistence.ts +4 -4
- package/src/shell/blocking-input.ts +92 -2
- package/src/shell/recovery-input-helpers.ts +1 -1
- package/src/shell/session-views.ts +539 -0
- package/src/shell/status-report.ts +86 -0
- package/src/shell/transcript-scroll.ts +68 -0
- package/src/shell/ui-openers.ts +84 -172
- package/src/shell/view-openers.ts +205 -0
- package/src/tools/wrfc-agent-guard.ts +22 -2
- package/src/utils/format-duration.ts +4 -8
- package/src/utils/splash-lines.ts +9 -1
- package/src/utils/terminal-width.ts +56 -0
- package/src/verification/live-verifier.ts +0 -10
- package/src/version.ts +1 -1
- package/src/{panels → views}/agent-inspector-shared.ts +2 -2
- package/src/{panels → views}/builtin-modals.ts +22 -21
- package/src/views/builtin-views.ts +135 -0
- package/src/{panels → views}/confirm-state.ts +9 -9
- package/src/{panels → views}/diff-review-model.ts +10 -3
- package/src/{panels → views}/eval-registry.ts +1 -1
- package/src/{panels → views}/fleet-acts.ts +49 -85
- package/src/{panels/fleet-panel-format.ts → views/fleet-format.ts} +40 -40
- package/src/{panels → views}/fleet-gateway.ts +5 -5
- package/src/{panels → views}/fleet-observed-render.ts +16 -16
- package/src/{panels → views}/fleet-read-model.ts +9 -9
- package/src/{panels/fleet-tabs.ts → views/fleet-session-tabs.ts} +13 -13
- package/src/{panels → views}/fleet-spawn.ts +33 -49
- package/src/{panels → views}/fleet-steer.ts +12 -12
- package/src/{panels → views}/fleet-stop.ts +4 -4
- package/src/{panels → views}/fleet-transcript.ts +26 -27
- package/src/{panels/fleet-panel-worktree-detail.ts → views/fleet-worktree-detail.ts} +7 -7
- package/src/{panels → views}/hosted-session-feed.ts +6 -6
- package/src/views/modal-surface-registry.ts +41 -0
- package/src/{panels → views}/modals/devices-modal.ts +2 -2
- package/src/{panels → views}/modals/hooks-modal.ts +13 -13
- package/src/{panels → views}/modals/keybindings-modal.ts +19 -27
- package/src/{panels → views}/modals/knowledge-modal.ts +6 -5
- package/src/{panels → views}/modals/local-auth-modal.ts +22 -21
- package/src/{panels → views}/modals/marketplace-modal.ts +11 -11
- package/src/{panels → views}/modals/memory-modal.ts +8 -8
- package/src/{panels → views}/modals/modal-surface-helpers.ts +15 -5
- package/src/{panels → views}/modals/pairing-modal.ts +9 -9
- package/src/{panels → views}/modals/planning-modal.ts +15 -20
- package/src/{panels → views}/modals/plugins-modal.ts +6 -6
- package/src/{panels → views}/modals/policy-modal.ts +13 -5
- package/src/{panels → views}/modals/provider-health-modal.ts +4 -4
- package/src/{panels → views}/modals/remote-modal.ts +3 -3
- package/src/{panels → views}/modals/sandbox-modal.ts +4 -4
- package/src/{panels → views}/modals/security-modal.ts +22 -16
- package/src/{panels → views}/modals/services-modal.ts +3 -3
- package/src/{panels → views}/modals/settings-sync-modal.ts +4 -4
- package/src/{panels → views}/modals/skills-modal.ts +9 -9
- package/src/{panels → views}/modals/subscription-modal.ts +3 -3
- package/src/{panels → views}/modals/work-plan-modal.ts +10 -13
- package/src/views/notifications-feed.ts +314 -0
- package/src/{panels → views}/polish-core.ts +38 -36
- package/src/{panels → views}/polish-tables.ts +35 -26
- package/src/{panels → views}/polish.ts +101 -95
- package/src/{panels → views}/project-planning-answer-actions.ts +3 -3
- package/src/{panels → views}/provider-health-routes.ts +14 -14
- package/src/{panels → views}/search-focus.ts +8 -8
- package/src/{panels → views}/session-maintenance.ts +11 -11
- package/src/views/skills-discovery.ts +159 -0
- package/src/views/view-deps.ts +80 -0
- package/src/views/view-panel-adapter.ts +53 -0
- package/src/{panels → views}/workstream-graph-render.ts +1 -1
- package/src/core/conversation-fold.ts +0 -101
- package/src/core/conversation-turn-structure.ts +0 -464
- package/src/core/conversation-user-receipts.ts +0 -42
- package/src/input/commands/operator-panel-runtime.ts +0 -186
- package/src/input/panel-integration-actions.ts +0 -33
- package/src/input/panel-mouse-geometry.ts +0 -97
- package/src/input/panel-paste-flood-guard.ts +0 -86
- package/src/panels/base-panel.ts +0 -301
- package/src/panels/builtin/agent.ts +0 -27
- package/src/panels/builtin/development.ts +0 -92
- package/src/panels/builtin/knowledge.ts +0 -12
- package/src/panels/builtin/operations.ts +0 -177
- package/src/panels/builtin/session.ts +0 -81
- package/src/panels/builtin/shared.ts +0 -357
- package/src/panels/builtin-panels.ts +0 -28
- package/src/panels/cost-tracker-panel.ts +0 -746
- package/src/panels/diff-panel.ts +0 -797
- package/src/panels/diff-review-panel.ts +0 -395
- package/src/panels/expandable-list-panel.ts +0 -189
- package/src/panels/fleet-deep-link.ts +0 -31
- package/src/panels/fleet-panel.ts +0 -799
- package/src/panels/git-panel.ts +0 -800
- package/src/panels/hosted-session-panel.ts +0 -221
- package/src/panels/index.ts +0 -21
- package/src/panels/local-auth-panel.ts +0 -478
- package/src/panels/notifications-feed.ts +0 -127
- package/src/panels/notifications-panel.ts +0 -160
- package/src/panels/panel-confirm-overlay.ts +0 -72
- package/src/panels/panel-manager.ts +0 -791
- package/src/panels/plugins-panel.ts +0 -371
- package/src/panels/scrollable-list-panel.ts +0 -498
- package/src/panels/skills-panel.ts +0 -454
- package/src/panels/token-budget-panel.ts +0 -688
- package/src/panels/types.ts +0 -191
- package/src/panels/wrfc-panel-format.ts +0 -133
- package/src/renderer/diff-view.ts +0 -134
- package/src/renderer/fleet-tab-strip.ts +0 -56
- package/src/renderer/footer-tips.ts +0 -53
- package/src/renderer/fullscreen-primitives.ts +0 -146
- package/src/renderer/fullscreen-workspace.ts +0 -194
- package/src/renderer/hint-grammar.ts +0 -52
- package/src/renderer/modal-factory.ts +0 -480
- package/src/renderer/overlay-box.ts +0 -154
- package/src/renderer/overlay-viewport.ts +0 -104
- package/src/renderer/panel-composite.ts +0 -145
- package/src/renderer/panel-workspace-bar.ts +0 -54
- package/src/renderer/process-indicator.ts +0 -163
- package/src/renderer/tab-strip.ts +0 -222
- package/src/renderer/text-layout.ts +0 -31
- package/src/renderer/tool-call.ts +0 -276
- package/src/renderer/tool-result-expanded-lines.ts +0 -48
- package/src/renderer/tool-result-summary.ts +0 -219
- package/src/runtime/diagnostics/panels/index.ts +0 -21
- package/src/runtime/diagnostics/panels/ops.ts +0 -156
- package/src/runtime/perf/panel-contracts.ts +0 -32
- package/src/runtime/store/domains/panels.ts +0 -117
- package/src/verification/verification-ledger.ts +0 -707
- /package/src/{panels → views}/memory-consolidation-gateway.ts +0 -0
- /package/src/{panels → views}/modals/modal-theme.ts +0 -0
- /package/src/{panels → views}/provider-health-domains.ts +0 -0
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
import { getDisplayWidth, truncateDisplay, wrapText } from '../utils/terminal-width.ts';
|
|
2
|
-
|
|
3
|
-
export function wrapWithHangingIndent(
|
|
4
|
-
text: string,
|
|
5
|
-
width: number,
|
|
6
|
-
indent: string,
|
|
7
|
-
maxLines?: number,
|
|
8
|
-
): string[] {
|
|
9
|
-
const safeWidth = Math.max(1, width);
|
|
10
|
-
const indentWidth = getDisplayWidth(indent);
|
|
11
|
-
const wrapped = wrapText(text, safeWidth);
|
|
12
|
-
const lines = wrapped.map((line, index) => {
|
|
13
|
-
if (index === 0) return truncateDisplay(line, safeWidth);
|
|
14
|
-
return `${indent}${truncateDisplay(line, Math.max(1, safeWidth - indentWidth))}`;
|
|
15
|
-
});
|
|
16
|
-
return typeof maxLines === 'number' ? lines.slice(0, maxLines) : lines;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
export function fitLabelDetailColumns(
|
|
20
|
-
label: string,
|
|
21
|
-
detail: string,
|
|
22
|
-
totalWidth: number,
|
|
23
|
-
labelRatio = 0.55,
|
|
24
|
-
): { labelWidth: number; detailWidth: number } {
|
|
25
|
-
const safeWidth = Math.max(8, totalWidth);
|
|
26
|
-
const labelWidth = Math.max(10, Math.min(safeWidth - 2, Math.floor(safeWidth * labelRatio)));
|
|
27
|
-
return {
|
|
28
|
-
labelWidth,
|
|
29
|
-
detailWidth: Math.max(0, safeWidth - labelWidth - 2),
|
|
30
|
-
};
|
|
31
|
-
}
|
|
@@ -1,276 +0,0 @@
|
|
|
1
|
-
import { type Line, createStyledCell, createEmptyLine } from '@pellux/goodvibes-sdk/platform/types';
|
|
2
|
-
import { LAYOUT, TOOL_STATUS } from './layout.ts';
|
|
3
|
-
import { getDisplayWidth, truncateDisplay } from '../utils/terminal-width.ts';
|
|
4
|
-
import type { ToolCall } from '@pellux/goodvibes-sdk/platform/types';
|
|
5
|
-
import { stripDangerousAnsi } from './ansi-sanitize.ts';
|
|
6
|
-
import { formatElapsed } from '../utils/format-elapsed.ts';
|
|
7
|
-
import { GLYPHS } from './ui-primitives.ts';
|
|
8
|
-
import { activeUiTones } from './theme.ts';
|
|
9
|
-
import { treeContentCol, treeTextCol, writeTreeStatusMarker } from '@pellux/goodvibes-terminal-shell';
|
|
10
|
-
|
|
11
|
-
const TOOL_NAME_MIN_WIDTH = 8;
|
|
12
|
-
const TOOL_NAME_MAX_WIDTH = 20;
|
|
13
|
-
|
|
14
|
-
/** Tree placement for a tool-call row rendered as a branch of its turn. */
|
|
15
|
-
export interface ToolCallTreeOptions {
|
|
16
|
-
/** Effective indent in columns, already clamped by treeIndentCols(). */
|
|
17
|
-
readonly indentCols?: number;
|
|
18
|
-
readonly branchFg?: string;
|
|
19
|
-
/** Suppress the tool label because the turn header already carries it. */
|
|
20
|
-
readonly omitToolName?: boolean;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
function writeStyledText(
|
|
24
|
-
line: Line,
|
|
25
|
-
startCol: number,
|
|
26
|
-
endColExclusive: number,
|
|
27
|
-
text: string,
|
|
28
|
-
style: { readonly fg: string; readonly bold?: boolean; readonly dim?: boolean },
|
|
29
|
-
): number {
|
|
30
|
-
let col = startCol;
|
|
31
|
-
for (const ch of text) {
|
|
32
|
-
const width = getDisplayWidth(ch);
|
|
33
|
-
if (width <= 0) continue;
|
|
34
|
-
if (col + width > endColExclusive) break;
|
|
35
|
-
line[col] = createStyledCell(ch, {
|
|
36
|
-
fg: style.fg,
|
|
37
|
-
bold: style.bold ?? false,
|
|
38
|
-
dim: style.dim ?? false,
|
|
39
|
-
});
|
|
40
|
-
if (width > 1 && col + 1 < endColExclusive) {
|
|
41
|
-
line[col + 1] = createStyledCell('', {
|
|
42
|
-
fg: style.fg,
|
|
43
|
-
bold: style.bold ?? false,
|
|
44
|
-
dim: style.dim ?? false,
|
|
45
|
-
});
|
|
46
|
-
}
|
|
47
|
-
col += width;
|
|
48
|
-
}
|
|
49
|
-
return col;
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
function buildLeftSegments(
|
|
53
|
-
toolName: string,
|
|
54
|
-
keyArg: string,
|
|
55
|
-
suffixText: string,
|
|
56
|
-
leftBudget: number,
|
|
57
|
-
omitToolName = false,
|
|
58
|
-
): Array<{ text: string; fg: string; bold?: boolean; dim?: boolean }> {
|
|
59
|
-
if (leftBudget <= 0) return [];
|
|
60
|
-
|
|
61
|
-
// When every call in the turn shares one label, the label rides on the turn
|
|
62
|
-
// header and each row leads with the argument that actually distinguishes it
|
|
63
|
-
// (`config.get`, `services.restart`). Falls back to showing the name when
|
|
64
|
-
// there is no key argument, so a row can never render empty.
|
|
65
|
-
if (omitToolName && keyArg) {
|
|
66
|
-
return buildLeftSegments(keyArg, '', suffixText, leftBudget, false);
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
const t = activeUiTones();
|
|
70
|
-
const segments: Array<{ text: string; fg: string; bold?: boolean; dim?: boolean }> = [];
|
|
71
|
-
const suffixBudget = suffixText ? Math.min(Math.max(12, Math.floor(leftBudget * 0.3)), 20) : 0;
|
|
72
|
-
const suffixDisplay = suffixBudget > 0 ? truncateDisplay(suffixText, suffixBudget) : '';
|
|
73
|
-
const suffixWidth = getDisplayWidth(suffixDisplay);
|
|
74
|
-
const separatorBeforeSuffix = suffixDisplay ? 2 : 0;
|
|
75
|
-
const mainBudget = Math.max(1, leftBudget - suffixWidth - separatorBeforeSuffix);
|
|
76
|
-
|
|
77
|
-
let toolNameDisplay = '';
|
|
78
|
-
let keyArgDisplay = '';
|
|
79
|
-
if (keyArg) {
|
|
80
|
-
const rawToolWidth = getDisplayWidth(toolName);
|
|
81
|
-
const preferredToolWidth = Math.min(
|
|
82
|
-
Math.max(rawToolWidth, TOOL_NAME_MIN_WIDTH),
|
|
83
|
-
TOOL_NAME_MAX_WIDTH,
|
|
84
|
-
Math.max(TOOL_NAME_MIN_WIDTH, Math.floor(mainBudget * 0.4)),
|
|
85
|
-
);
|
|
86
|
-
const toolNameWidth = Math.min(preferredToolWidth, Math.max(1, mainBudget - 3));
|
|
87
|
-
const keyArgWidth = Math.max(0, mainBudget - toolNameWidth - 2);
|
|
88
|
-
toolNameDisplay = truncateDisplay(toolName, toolNameWidth);
|
|
89
|
-
keyArgDisplay = truncateDisplay(keyArg, keyArgWidth);
|
|
90
|
-
} else {
|
|
91
|
-
toolNameDisplay = truncateDisplay(toolName, mainBudget);
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
if (toolNameDisplay) {
|
|
95
|
-
segments.push({ text: toolNameDisplay, fg: '#00ffcc', bold: true });
|
|
96
|
-
}
|
|
97
|
-
if (keyArgDisplay) {
|
|
98
|
-
segments.push({ text: ' ', fg: t.fg.primary });
|
|
99
|
-
segments.push({ text: keyArgDisplay, fg: '252' });
|
|
100
|
-
}
|
|
101
|
-
if (suffixDisplay) {
|
|
102
|
-
segments.push({ text: ' ', fg: t.fg.primary });
|
|
103
|
-
segments.push({
|
|
104
|
-
text: suffixDisplay,
|
|
105
|
-
fg: suffixText.startsWith('- ') ? t.chrome.bad : '244',
|
|
106
|
-
dim: true,
|
|
107
|
-
});
|
|
108
|
-
}
|
|
109
|
-
return segments;
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
/**
|
|
113
|
-
* Extract the most meaningful argument from a tool call for display.
|
|
114
|
-
*/
|
|
115
|
-
function extractKeyArg(toolCall: ToolCall): string {
|
|
116
|
-
const args = toolCall.arguments;
|
|
117
|
-
// Path-based tools
|
|
118
|
-
if (typeof args.path === 'string') return args.path;
|
|
119
|
-
if (typeof args.file === 'string') return args.file;
|
|
120
|
-
if (typeof args.query === 'string') return args.query;
|
|
121
|
-
if (typeof args.title === 'string') return args.title;
|
|
122
|
-
// Array-based (read/write)
|
|
123
|
-
if (Array.isArray(args.files) && args.files.length > 0) {
|
|
124
|
-
const first = args.files[0];
|
|
125
|
-
if (typeof first === 'string') return first;
|
|
126
|
-
if (first && typeof first === 'object' && typeof (first as Record<string, unknown>).path === 'string')
|
|
127
|
-
return (first as Record<string, unknown>).path as string;
|
|
128
|
-
}
|
|
129
|
-
// Exec
|
|
130
|
-
if (typeof args.command === 'string') return args.command;
|
|
131
|
-
if (typeof args.cmd === 'string') return args.cmd;
|
|
132
|
-
if (Array.isArray(args.commands) && args.commands.length > 0) {
|
|
133
|
-
const first = args.commands[0];
|
|
134
|
-
if (first && typeof first === 'object' && typeof (first as Record<string, unknown>).cmd === 'string')
|
|
135
|
-
return (first as Record<string, unknown>).cmd as string;
|
|
136
|
-
}
|
|
137
|
-
// Find/grep
|
|
138
|
-
if (typeof args.pattern === 'string') return args.pattern;
|
|
139
|
-
if (Array.isArray(args.queries) && args.queries.length > 0) {
|
|
140
|
-
const first = args.queries[0];
|
|
141
|
-
if (first && typeof first === 'object') {
|
|
142
|
-
const firstRecord = first as Record<string, unknown>;
|
|
143
|
-
if (typeof firstRecord.query === 'string') return firstRecord.query;
|
|
144
|
-
if (typeof firstRecord.pattern === 'string') return firstRecord.pattern;
|
|
145
|
-
}
|
|
146
|
-
}
|
|
147
|
-
// Fetch
|
|
148
|
-
if (Array.isArray(args.urls) && args.urls.length > 0) {
|
|
149
|
-
const first = args.urls[0];
|
|
150
|
-
if (first && typeof first === 'object' && typeof (first as Record<string, unknown>).url === 'string')
|
|
151
|
-
return (first as Record<string, unknown>).url as string;
|
|
152
|
-
}
|
|
153
|
-
// Agent
|
|
154
|
-
if (typeof args.task === 'string') return args.task.slice(0, 40);
|
|
155
|
-
if (typeof args.mode === 'string') return args.mode;
|
|
156
|
-
// Fallback: first string value
|
|
157
|
-
for (const val of Object.values(args)) {
|
|
158
|
-
if (typeof val === 'string' && val.length > 0) return val.slice(0, 40);
|
|
159
|
-
}
|
|
160
|
-
return '';
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
/**
|
|
164
|
-
* Render a tool call as a single collapsed line.
|
|
165
|
-
*
|
|
166
|
-
* Layout: [margin] [icon] [space] [tool name padded] [key arg] [summary] [duration]
|
|
167
|
-
*
|
|
168
|
-
* @param toolCall - The tool call being executed
|
|
169
|
-
* @param status - 'executing' | 'done' | 'error' | 'pending' | 'cancelled'
|
|
170
|
-
* @param resultSummary - Optional brief summary (e.g., "3 files", "exit 0")
|
|
171
|
-
* @param width - Terminal width
|
|
172
|
-
* @param durationMs - Optional duration in milliseconds
|
|
173
|
-
* @param errorMsg - Optional error message for failed calls
|
|
174
|
-
* @param frameIndex - Spinner frame index for animated icon
|
|
175
|
-
* @param startedAtMs - Wall-clock ms when execution started; enables live elapsed timer
|
|
176
|
-
*/
|
|
177
|
-
export function renderToolCallBlock(
|
|
178
|
-
toolCall: ToolCall,
|
|
179
|
-
status: 'executing' | 'done' | 'error' | 'pending' | 'cancelled',
|
|
180
|
-
resultSummary: string | undefined,
|
|
181
|
-
width: number,
|
|
182
|
-
durationMs?: number,
|
|
183
|
-
errorMsg?: string,
|
|
184
|
-
frameIndex?: number,
|
|
185
|
-
startedAtMs?: number,
|
|
186
|
-
tree?: ToolCallTreeOptions,
|
|
187
|
-
): Line[] {
|
|
188
|
-
const line = createEmptyLine(width);
|
|
189
|
-
const t = activeUiTones();
|
|
190
|
-
const indent = Math.max(0, tree?.indentCols ?? 0);
|
|
191
|
-
const inTree = indent > 0;
|
|
192
|
-
// In tree mode the row's columns come from the shared grid: the branch glyph
|
|
193
|
-
// where the parent's content began, the status marker in the bullet column,
|
|
194
|
-
// and the row's text at this depth's text column.
|
|
195
|
-
const margin = inTree ? treeContentCol(indent) : LAYOUT.LEFT_MARGIN;
|
|
196
|
-
const rightMargin = LAYOUT.RIGHT_MARGIN;
|
|
197
|
-
const contentEnd = width - rightMargin;
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
// Status icon. 'pending' means the tool is still awaiting a decision
|
|
201
|
-
// (e.g. an approval prompt) and has NOT run yet, it uses the hollow idle
|
|
202
|
-
// glyph so a not-yet-run tool never shows the completed green ✓ (2c).
|
|
203
|
-
// 'cancelled' means the user stopped THIS call mid-flight, the blocked glyph
|
|
204
|
-
// in the warn tone, distinct from both success and a real error.
|
|
205
|
-
const icon = status === 'done' ? TOOL_STATUS.SUCCESS_ICON
|
|
206
|
-
: status === 'error' ? TOOL_STATUS.FAIL_ICON
|
|
207
|
-
: status === 'cancelled' ? GLYPHS.status.blocked
|
|
208
|
-
: status === 'pending' ? GLYPHS.status.idle
|
|
209
|
-
: TOOL_STATUS.SPINNER_FRAMES[(frameIndex ?? 0) % TOOL_STATUS.SPINNER_FRAMES.length];
|
|
210
|
-
const iconColor = status === 'done' ? t.chrome.good
|
|
211
|
-
: status === 'error' ? t.chrome.bad
|
|
212
|
-
: status === 'cancelled' ? t.chrome.warn
|
|
213
|
-
: '244';
|
|
214
|
-
const rightText = (() => {
|
|
215
|
-
if (durationMs !== undefined && status === 'done') {
|
|
216
|
-
return formatElapsed(durationMs);
|
|
217
|
-
}
|
|
218
|
-
if (status === 'executing') {
|
|
219
|
-
if (startedAtMs !== undefined) {
|
|
220
|
-
return formatElapsed(Date.now() - startedAtMs);
|
|
221
|
-
}
|
|
222
|
-
return '...';
|
|
223
|
-
}
|
|
224
|
-
return '';
|
|
225
|
-
})();
|
|
226
|
-
const rightWidth = getDisplayWidth(rightText);
|
|
227
|
-
const rightStart = rightText
|
|
228
|
-
? Math.max(margin + 2, contentEnd - rightWidth)
|
|
229
|
-
: contentEnd;
|
|
230
|
-
const leftStart = inTree ? treeTextCol(indent) : margin;
|
|
231
|
-
const leftEndExclusive = rightText
|
|
232
|
-
? Math.max(leftStart, rightStart - 1)
|
|
233
|
-
: contentEnd;
|
|
234
|
-
let col: number = leftStart;
|
|
235
|
-
|
|
236
|
-
// In tree mode the status glyph goes in the bullet column, the same column
|
|
237
|
-
// as the `●` of the `● assistant` header this row hangs under, so a turn's
|
|
238
|
-
// markers read as that bullet's column continuing down the turn. The row's
|
|
239
|
-
// own text then starts at its depth's text column with no inline icon slot.
|
|
240
|
-
if (inTree) {
|
|
241
|
-
writeTreeStatusMarker(line, icon, iconColor, width);
|
|
242
|
-
} else {
|
|
243
|
-
if (col < leftEndExclusive) {
|
|
244
|
-
line[col] = createStyledCell(icon, { fg: iconColor, bold: status === 'done' || status === 'error' || status === 'cancelled' });
|
|
245
|
-
}
|
|
246
|
-
col += 2; // icon + space
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
// Tool name, extract short name for long MCP tool names, but keep a readable dynamic width.
|
|
250
|
-
const rawName = toolCall.name.includes('__')
|
|
251
|
-
? toolCall.name.split('__').pop()!
|
|
252
|
-
: toolCall.name;
|
|
253
|
-
const keyArg = stripDangerousAnsi(extractKeyArg(toolCall));
|
|
254
|
-
const suffixText = status === 'error' && errorMsg
|
|
255
|
-
? `- ${stripDangerousAnsi(errorMsg).slice(0, 40)}`
|
|
256
|
-
: status === 'cancelled'
|
|
257
|
-
? '- cancelled'
|
|
258
|
-
: status === 'done' && resultSummary
|
|
259
|
-
? `(${stripDangerousAnsi(resultSummary)})`
|
|
260
|
-
: '';
|
|
261
|
-
const leftBudget = Math.max(0, leftEndExclusive - col);
|
|
262
|
-
const leftSegments = buildLeftSegments(rawName, keyArg, suffixText, leftBudget, tree?.omitToolName ?? false);
|
|
263
|
-
for (const segment of leftSegments) {
|
|
264
|
-
col = writeStyledText(line, col, leftEndExclusive, segment.text, {
|
|
265
|
-
fg: segment.fg,
|
|
266
|
-
bold: segment.bold ?? false,
|
|
267
|
-
dim: segment.dim ?? false,
|
|
268
|
-
});
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
if (rightText) {
|
|
272
|
-
writeStyledText(line, rightStart, contentEnd, rightText, { fg: '238', dim: true });
|
|
273
|
-
}
|
|
274
|
-
|
|
275
|
-
return [line];
|
|
276
|
-
}
|
|
@@ -1,48 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* tool-result-expanded-lines.ts, the single source of truth for how many
|
|
3
|
-
* screen lines a tool-result message's EXPANDED form actually renders to.
|
|
4
|
-
*
|
|
5
|
-
* Tool-result content gets pretty-printed (JSON.stringify(parsed, null, 2))
|
|
6
|
-
* or diff-rendered before display, so the raw message's `content.split('\n')`
|
|
7
|
-
* length is not what the user sees once expanded, a one-line JSON blob can
|
|
8
|
-
* pretty-print to 50 lines. Both the per-block "N lines" badge
|
|
9
|
-
* (conversation-rendering.ts) and the folded-group total (conversation-tool-
|
|
10
|
-
* groups.ts) must count the SAME post-render lines, or the two disagree with
|
|
11
|
-
* each other and both can disagree with what Tab actually reveals.
|
|
12
|
-
*/
|
|
13
|
-
|
|
14
|
-
import type { Line } from '@pellux/goodvibes-sdk/platform/types';
|
|
15
|
-
import { renderMarkdownTracked } from './markdown.ts';
|
|
16
|
-
import { renderDiffView } from './diff-view.ts';
|
|
17
|
-
|
|
18
|
-
/** True when `content` looks like a unified diff (matches the detection used
|
|
19
|
-
* by renderConversationToolMessage to pick the diff block type). */
|
|
20
|
-
export function isDiffContent(content: string): boolean {
|
|
21
|
-
const contentLines = content.split('\n');
|
|
22
|
-
const hasDiffHeader = contentLines.some((l) => l.startsWith('--- ')) && contentLines.some((l) => l.startsWith('+++ '));
|
|
23
|
-
const hasHunk = contentLines.some((l) => l.startsWith('@@ '));
|
|
24
|
-
return hasDiffHeader && hasHunk;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
/**
|
|
28
|
-
* Render a tool result's content exactly as its EXPANDED form would appear in
|
|
29
|
-
* the transcript (diff view for diffs; pretty-printed JSON, when parseable,
|
|
30
|
-
* for everything else). The caller decides whether to actually display these
|
|
31
|
-
* lines or just count them.
|
|
32
|
-
*/
|
|
33
|
-
export function renderExpandedToolResultLines(content: string, width: number): Line[] {
|
|
34
|
-
if (isDiffContent(content)) {
|
|
35
|
-
return renderDiffView(content, width);
|
|
36
|
-
}
|
|
37
|
-
let contentToRender = content;
|
|
38
|
-
const trimmed = contentToRender.trimStart();
|
|
39
|
-
if ((trimmed.startsWith('{') || trimmed.startsWith('[')) && contentToRender.length < 100_000) {
|
|
40
|
-
try {
|
|
41
|
-
const parsed = JSON.parse(contentToRender);
|
|
42
|
-
contentToRender = `\`\`\`json\n${JSON.stringify(parsed, null, 2)}\n\`\`\``;
|
|
43
|
-
} catch {
|
|
44
|
-
// Leave invalid JSON as-is, falls through to the plain markdown render below.
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
return renderMarkdownTracked(contentToRender, width).lines;
|
|
48
|
-
}
|
|
@@ -1,219 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* summarizeToolResult, one-line human summaries for tool RESULTS, so the
|
|
3
|
-
* transcript (and the fleet attach view, which shares the same rendering seam
|
|
4
|
-
* in conversation-rendering.ts) shows "wrote haiku.txt (532 B)" instead of a
|
|
5
|
-
* raw `{"files_written":1,"bytes_written":532,...}` JSON blob. The full payload
|
|
6
|
-
* stays reachable behind the existing collapse/expand toggle, this only
|
|
7
|
-
* changes the collapsed preview line, never destroys data. (item 3.)
|
|
8
|
-
*
|
|
9
|
-
* Returning `null` means "no honest summary for this shape", the caller then
|
|
10
|
-
* falls back to the previous raw-first-line preview, so an unrecognised tool or
|
|
11
|
-
* an unexpected payload is never mis-summarised.
|
|
12
|
-
*/
|
|
13
|
-
|
|
14
|
-
/** Compact byte count: "87 B", "1.2 KB", "3.4 MB". */
|
|
15
|
-
function formatBytes(n: number): string {
|
|
16
|
-
if (!Number.isFinite(n) || n < 0) return '? B';
|
|
17
|
-
if (n < 1024) return `${n} B`;
|
|
18
|
-
if (n < 1024 * 1024) return `${(n / 1024).toFixed(1)} KB`;
|
|
19
|
-
return `${(n / (1024 * 1024)).toFixed(1)} MB`;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
/** Compact duration: "820ms", "1.2s", "1m3s". */
|
|
23
|
-
function formatDuration(ms: number): string {
|
|
24
|
-
if (!Number.isFinite(ms) || ms < 0) return '?';
|
|
25
|
-
if (ms < 1000) return `${Math.round(ms)}ms`;
|
|
26
|
-
if (ms < 60_000) return `${(ms / 1000).toFixed(1)}s`;
|
|
27
|
-
const s = Math.round(ms / 1000);
|
|
28
|
-
return `${Math.floor(s / 60)}m${s % 60}s`;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
/** Trailing filename for compact display (paths can be long/absolute). */
|
|
32
|
-
function baseName(p: string): string {
|
|
33
|
-
const cleaned = p.replace(/[/\\]+$/, '');
|
|
34
|
-
const idx = Math.max(cleaned.lastIndexOf('/'), cleaned.lastIndexOf('\\'));
|
|
35
|
-
return idx >= 0 ? cleaned.slice(idx + 1) : cleaned;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
function asNumber(v: unknown): number | undefined {
|
|
39
|
-
return typeof v === 'number' && Number.isFinite(v) ? v : undefined;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
function firstPath(files: unknown): string | undefined {
|
|
43
|
-
if (!Array.isArray(files) || files.length === 0) return undefined;
|
|
44
|
-
const first = files[0];
|
|
45
|
-
if (typeof first === 'string') return first;
|
|
46
|
-
if (first && typeof first === 'object') {
|
|
47
|
-
const rec = first as Record<string, unknown>;
|
|
48
|
-
if (typeof rec.path === 'string') return rec.path;
|
|
49
|
-
if (typeof rec.resolved_path === 'string') return rec.resolved_path;
|
|
50
|
-
}
|
|
51
|
-
return undefined;
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
/** write: { files_written, bytes_written, files?: [{path, bytes_written}], dry_run? } */
|
|
55
|
-
function summarizeWrite(obj: Record<string, unknown>): string | null {
|
|
56
|
-
const count = asNumber(obj.files_written);
|
|
57
|
-
const bytes = asNumber(obj.bytes_written);
|
|
58
|
-
if (count === undefined) return null;
|
|
59
|
-
const verb = obj.dry_run === true ? 'would write' : 'wrote';
|
|
60
|
-
const size = bytes !== undefined ? ` (${formatBytes(bytes)})` : '';
|
|
61
|
-
if (count === 1) {
|
|
62
|
-
const path = firstPath(obj.files);
|
|
63
|
-
return path ? `${verb} ${baseName(path)}${size}` : `${verb} 1 file${size}`;
|
|
64
|
-
}
|
|
65
|
-
return `${verb} ${count} files${size}`;
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
/** read: { summary: { files_read, total_lines }, files?: [{path, lineCount}] } */
|
|
69
|
-
function summarizeRead(obj: Record<string, unknown>): string | null {
|
|
70
|
-
const summary = (obj.summary && typeof obj.summary === 'object')
|
|
71
|
-
? obj.summary as Record<string, unknown>
|
|
72
|
-
: obj;
|
|
73
|
-
const count = asNumber(summary.files_read);
|
|
74
|
-
const lines = asNumber(summary.total_lines);
|
|
75
|
-
if (count === undefined && lines === undefined) return null;
|
|
76
|
-
const lineText = lines !== undefined ? ` (${lines} line${lines === 1 ? '' : 's'})` : '';
|
|
77
|
-
if (count === 1) {
|
|
78
|
-
const path = firstPath(obj.files);
|
|
79
|
-
return path ? `read ${baseName(path)}${lineText}` : `read 1 file${lineText}`;
|
|
80
|
-
}
|
|
81
|
-
return `read ${count ?? '?'} files${lineText}`;
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
/** edit: { applied, failed, dry_run } or richer with a path. */
|
|
85
|
-
function summarizeEdit(obj: Record<string, unknown>): string | null {
|
|
86
|
-
const applied = asNumber(obj.applied);
|
|
87
|
-
if (applied === undefined) return null;
|
|
88
|
-
const failed = asNumber(obj.failed) ?? 0;
|
|
89
|
-
const verb = obj.dry_run === true ? 'would apply' : 'applied';
|
|
90
|
-
const path = typeof obj.path === 'string' ? obj.path : firstPath(obj.files);
|
|
91
|
-
const target = path ? ` to ${baseName(path)}` : '';
|
|
92
|
-
const failText = failed > 0 ? `, ${failed} failed` : '';
|
|
93
|
-
return `${verb} ${applied} edit${applied === 1 ? '' : 's'}${target}${failText}`;
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
// The credential scrub's withheld_env report stays OUT of the collapsed line
|
|
97
|
-
// entirely. It withholds the same standing set of names on every spawn, so any
|
|
98
|
-
// per-result rendering repeats identical env-var noise all session long, the
|
|
99
|
-
// dominant transcript spam for anyone with several keys exported. The report
|
|
100
|
-
// only matters when a command fails because a needed credential was scrubbed,
|
|
101
|
-
// and for that case the full name list is one expand away in the raw payload.
|
|
102
|
-
|
|
103
|
-
/**
|
|
104
|
-
* Compact per-command exec-sandbox suffix: `sandboxed` fields are present
|
|
105
|
-
* only when the sandbox was active for the run (SDK's attachSandboxMeta stays
|
|
106
|
-
* quiet otherwise), so absence here means "ran unsandboxed" and the suffix is
|
|
107
|
-
* empty, never a fabricated claim either way.
|
|
108
|
-
*
|
|
109
|
-
* sandboxed, network disabled -> " · sandboxed (net off)"
|
|
110
|
-
* sandboxed, network enabled, escalation -> " · sandboxed (net on, +1 escalation)"
|
|
111
|
-
* requested but host couldn't provide it -> " · sandbox unavailable (<reason>)"
|
|
112
|
-
*/
|
|
113
|
-
function sandboxSuffix(obj: Record<string, unknown>): string {
|
|
114
|
-
const sandboxed = obj.sandboxed;
|
|
115
|
-
if (sandboxed === undefined) return '';
|
|
116
|
-
if (sandboxed === false) {
|
|
117
|
-
const reason = typeof obj.sandbox_boundary === 'string' ? obj.sandbox_boundary : undefined;
|
|
118
|
-
return reason ? ` · sandbox unavailable (${reason})` : ' · sandbox unavailable';
|
|
119
|
-
}
|
|
120
|
-
const network = typeof obj.sandbox_network === 'string' ? obj.sandbox_network : undefined;
|
|
121
|
-
const escalations = Array.isArray(obj.sandbox_escalations)
|
|
122
|
-
? obj.sandbox_escalations.filter((e): e is string => typeof e === 'string')
|
|
123
|
-
: [];
|
|
124
|
-
const netText = network ? `net ${network}` : undefined;
|
|
125
|
-
const escText = escalations.length > 0 ? `+${escalations.length} escalation${escalations.length === 1 ? '' : 's'}` : undefined;
|
|
126
|
-
const detail = [netText, escText].filter((s): s is string => s !== undefined).join(', ');
|
|
127
|
-
return detail ? ` · sandboxed (${detail})` : ' · sandboxed';
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
/** A single exec command result. */
|
|
131
|
-
function summarizeExecOne(obj: Record<string, unknown>): string | null {
|
|
132
|
-
const exit = obj.exit_code;
|
|
133
|
-
const dur = asNumber(obj.duration_ms);
|
|
134
|
-
const durText = dur !== undefined ? ` · ${formatDuration(dur)}` : '';
|
|
135
|
-
const sandboxText = sandboxSuffix(obj);
|
|
136
|
-
if (obj.timed_out === true) return `timed out${durText}${sandboxText}`;
|
|
137
|
-
if (obj.cancelled === true) return `cancelled${durText}${sandboxText}`;
|
|
138
|
-
const stdout = typeof obj.stdout === 'string' ? obj.stdout : '';
|
|
139
|
-
const lineCount = stdout.length === 0
|
|
140
|
-
? 0
|
|
141
|
-
: stdout.replace(/\n$/, '').split('\n').length;
|
|
142
|
-
const lineText = lineCount > 0 ? ` · ${lineCount} line${lineCount === 1 ? '' : 's'}` : '';
|
|
143
|
-
const code = typeof exit === 'number' ? String(exit) : '?';
|
|
144
|
-
return `exit ${code}${durText}${lineText}${sandboxText}`;
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
/** exec: single { cmd, exit_code, ... } or multi { commands: [...], total }. */
|
|
148
|
-
function summarizeExec(obj: Record<string, unknown>): string | null {
|
|
149
|
-
if (Array.isArray(obj.commands)) {
|
|
150
|
-
const total = asNumber(obj.total) ?? obj.commands.length;
|
|
151
|
-
const failed = obj.commands.filter(
|
|
152
|
-
(c) => c && typeof c === 'object' && (c as Record<string, unknown>).success === false,
|
|
153
|
-
).length;
|
|
154
|
-
// Count sandboxed commands across the batch, a compact "N sandboxed" beats
|
|
155
|
-
// repeating each command's full boundary detail on the collapsed line.
|
|
156
|
-
const sandboxedCount = obj.commands.filter(
|
|
157
|
-
(c) => c && typeof c === 'object' && (c as Record<string, unknown>).sandboxed === true,
|
|
158
|
-
).length;
|
|
159
|
-
const sandboxText = sandboxedCount > 0 ? ` · ${sandboxedCount} sandboxed` : '';
|
|
160
|
-
return failed > 0
|
|
161
|
-
? `${total} commands · ${failed} failed${sandboxText}`
|
|
162
|
-
: `${total} commands · all ok${sandboxText}`;
|
|
163
|
-
}
|
|
164
|
-
if ('exit_code' in obj || 'timed_out' in obj || 'cancelled' in obj) {
|
|
165
|
-
return summarizeExecOne(obj);
|
|
166
|
-
}
|
|
167
|
-
return null;
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
/**
|
|
171
|
-
* agent status/get/wait: an optional `failure` field (ChildFailureEnvelope,
|
|
172
|
-
* see the SDK's tools/agent/child-failure-envelope.ts) is present once the
|
|
173
|
-
* child agent terminated abnormally (max_turns, circuit_breaker,
|
|
174
|
-
* watchdog_timeout, budget_exhausted, claim_unverified, api_error, killed,
|
|
175
|
-
* interrupted, error). Renders it compactly on the collapsed preview line so
|
|
176
|
-
* a failed child is visible without expanding the full result; absent
|
|
177
|
-
* `failure` (the common, successful case) falls through to the raw preview.
|
|
178
|
-
*/
|
|
179
|
-
function summarizeAgentFailure(obj: Record<string, unknown>): string | null {
|
|
180
|
-
const failure = obj.failure;
|
|
181
|
-
if (!failure || typeof failure !== 'object') return null;
|
|
182
|
-
const f = failure as Record<string, unknown>;
|
|
183
|
-
const agentId = typeof f.agentId === 'string' ? f.agentId : '?';
|
|
184
|
-
const phase = typeof f.phase === 'string' ? f.phase : undefined;
|
|
185
|
-
const reason = f.reason && typeof f.reason === 'object' ? f.reason as Record<string, unknown> : undefined;
|
|
186
|
-
const code = reason && typeof reason.code === 'string' ? reason.code : 'error';
|
|
187
|
-
const message = reason && typeof reason.message === 'string' ? reason.message : '';
|
|
188
|
-
const partialOutputs = f.partialOutputs && typeof f.partialOutputs === 'object' ? f.partialOutputs as Record<string, unknown> : undefined;
|
|
189
|
-
const turnsCompleted = asNumber(partialOutputs?.turnsCompleted);
|
|
190
|
-
|
|
191
|
-
const MAX_MSG_LEN = 80;
|
|
192
|
-
const truncatedMessage = message.length > MAX_MSG_LEN ? `${message.slice(0, MAX_MSG_LEN - 3)}...` : message;
|
|
193
|
-
const phaseText = phase ? ` at ${phase}` : '';
|
|
194
|
-
const turnsText = turnsCompleted !== undefined ? ` (${turnsCompleted} turn${turnsCompleted === 1 ? '' : 's'} completed)` : '';
|
|
195
|
-
const msgText = truncatedMessage.length > 0 ? `: ${truncatedMessage}` : '';
|
|
196
|
-
return `agent ${agentId} failed [${code}]${phaseText}${msgText}${turnsText}`;
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
export function summarizeToolResult(toolName: string | undefined, content: string): string | null {
|
|
200
|
-
if (!toolName) return null;
|
|
201
|
-
const family = toolName.includes('__') ? toolName.split('__').pop()! : toolName;
|
|
202
|
-
let data: unknown;
|
|
203
|
-
try {
|
|
204
|
-
data = JSON.parse(content);
|
|
205
|
-
} catch {
|
|
206
|
-
return null;
|
|
207
|
-
}
|
|
208
|
-
if (data === null || typeof data !== 'object' || Array.isArray(data)) return null;
|
|
209
|
-
const obj = data as Record<string, unknown>;
|
|
210
|
-
|
|
211
|
-
switch (family) {
|
|
212
|
-
case 'write': return summarizeWrite(obj);
|
|
213
|
-
case 'read': return summarizeRead(obj);
|
|
214
|
-
case 'edit': return summarizeEdit(obj);
|
|
215
|
-
case 'exec': return summarizeExec(obj);
|
|
216
|
-
case 'agent': return summarizeAgentFailure(obj);
|
|
217
|
-
default: return null;
|
|
218
|
-
}
|
|
219
|
-
}
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Diagnostics panels barrel, re-exports all panel data provider classes.
|
|
3
|
-
*
|
|
4
|
-
* Import from this module to access the individual diagnostic panel providers.
|
|
5
|
-
*/
|
|
6
|
-
export { ToolCallsPanel } from '@/runtime/index.ts';
|
|
7
|
-
export { AgentsPanel } from '@/runtime/index.ts';
|
|
8
|
-
export { TasksPanel } from '@/runtime/index.ts';
|
|
9
|
-
export { EventsPanel } from '@/runtime/index.ts';
|
|
10
|
-
export { StateInspectorPanel } from '@/runtime/index.ts';
|
|
11
|
-
export type { InspectableDomain } from '@/runtime/index.ts';
|
|
12
|
-
export { HealthPanel } from '@/runtime/index.ts';
|
|
13
|
-
export { DivergencePanel } from '@/runtime/index.ts';
|
|
14
|
-
export { ReplayPanel } from '@/runtime/index.ts';
|
|
15
|
-
export { ToolContractsPanel } from '@/runtime/index.ts';
|
|
16
|
-
export { TransportPanel } from '@/runtime/index.ts';
|
|
17
|
-
export type { TransportPanelSnapshot } from '@/runtime/index.ts';
|
|
18
|
-
export { OpsPanel } from './ops.ts';
|
|
19
|
-
export type { OpsAuditEntry } from './ops.ts';
|
|
20
|
-
export { SecurityPanel } from '@/runtime/index.ts';
|
|
21
|
-
export type { SecurityPanelSnapshot } from '@/runtime/index.ts';
|