@pellux/goodvibes-tui 2.0.21 → 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +58 -0
- package/README.md +16 -16
- package/docs/README.md +4 -4
- package/docs/foundation-artifacts/operator-contract.json +1 -1
- package/package.json +16 -6
- 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-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/verification-ledger.ts +18 -16
- 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/{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
|
@@ -4,41 +4,41 @@
|
|
|
4
4
|
* The canonical evaluator is the SDK version exported from @/runtime/index.ts
|
|
5
5
|
* (via operations.evaluateSessionMaintenance), which reads from configManager.
|
|
6
6
|
* Import the evaluator from there, this module provides only the shared
|
|
7
|
-
*
|
|
7
|
+
* View* type surface used across TUI views and tests; it intentionally does
|
|
8
8
|
* not re-implement the evaluator.
|
|
9
9
|
*/
|
|
10
10
|
import type { ConfigManager } from '@pellux/goodvibes-sdk/platform/config';
|
|
11
11
|
|
|
12
|
-
export type
|
|
13
|
-
export type
|
|
12
|
+
export type ViewGuidanceMode = 'off' | 'minimal' | 'guided';
|
|
13
|
+
export type ViewSessionMaintenanceLevel = 'stable' | 'watch' | 'suggest-compact' | 'compacting' | 'needs-repair' | 'unknown';
|
|
14
14
|
|
|
15
|
-
export interface
|
|
15
|
+
export interface ViewSessionMaintenanceLineageEntry {
|
|
16
16
|
readonly branchReason?: string;
|
|
17
17
|
}
|
|
18
18
|
|
|
19
|
-
export interface
|
|
20
|
-
readonly lineage?: readonly
|
|
19
|
+
export interface ViewSessionMaintenanceSession {
|
|
20
|
+
readonly lineage?: readonly ViewSessionMaintenanceLineageEntry[];
|
|
21
21
|
readonly lastCompactedAt?: number;
|
|
22
22
|
readonly compactionMessageCount?: number;
|
|
23
23
|
readonly compactionState?: string;
|
|
24
24
|
}
|
|
25
25
|
|
|
26
|
-
export interface
|
|
26
|
+
export interface ViewSessionMaintenanceInput {
|
|
27
27
|
/** ConfigManager used to read behavior.autoCompactThreshold and related keys. */
|
|
28
28
|
readonly configManager: Pick<ConfigManager, 'get'>;
|
|
29
29
|
readonly currentTokens: number;
|
|
30
30
|
readonly contextWindow: number;
|
|
31
31
|
readonly messageCount?: number;
|
|
32
32
|
readonly sessionMemoryCount?: number;
|
|
33
|
-
readonly session?:
|
|
33
|
+
readonly session?: ViewSessionMaintenanceSession | null;
|
|
34
34
|
}
|
|
35
35
|
|
|
36
|
-
export interface
|
|
37
|
-
readonly level:
|
|
36
|
+
export interface ViewSessionMaintenanceStatus {
|
|
37
|
+
readonly level: ViewSessionMaintenanceLevel;
|
|
38
38
|
readonly summary: string;
|
|
39
39
|
readonly reasons: readonly string[];
|
|
40
40
|
readonly nextSteps: readonly string[];
|
|
41
|
-
readonly guidanceMode:
|
|
41
|
+
readonly guidanceMode: ViewGuidanceMode;
|
|
42
42
|
readonly usagePct: number;
|
|
43
43
|
readonly remainingTokens: number;
|
|
44
44
|
/**
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* skills-discovery.ts, finding the skills on disk: project-local
|
|
3
|
+
* (.goodvibes/skills), global (~/.goodvibes/skills) and custom directories,
|
|
4
|
+
* each SKILL.md parsed for its frontmatter, marketplace-installed ones tagged
|
|
5
|
+
* with their provenance. Used by /skills, /local review and the Skills modal.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { promises as fsPromises } from 'node:fs';
|
|
9
|
+
import { join, sep } from 'node:path';
|
|
10
|
+
import { listInstalledEcosystemEntries, type EcosystemCatalogPathOptions, type ShellPathService } from '@/runtime/index.ts';
|
|
11
|
+
import { summarizeError } from '@pellux/goodvibes-sdk/platform/utils';
|
|
12
|
+
|
|
13
|
+
export type SkillOrigin = 'project-local' | 'global' | 'custom';
|
|
14
|
+
|
|
15
|
+
export interface SkillRecord {
|
|
16
|
+
name: string;
|
|
17
|
+
description: string;
|
|
18
|
+
path: string;
|
|
19
|
+
origin: SkillOrigin;
|
|
20
|
+
dependencies: string[];
|
|
21
|
+
includes: string[];
|
|
22
|
+
frontmatter: Record<string, string>;
|
|
23
|
+
/**
|
|
24
|
+
* Set when this skill's file lives under an installed ecosystem-marketplace
|
|
25
|
+
* receipt's targetPath, the receipt's provenance summary (e.g. curated
|
|
26
|
+
* source / signature info), so marketplace-installed skills read as such
|
|
27
|
+
* instead of looking identical to hand-authored ones.
|
|
28
|
+
*/
|
|
29
|
+
marketplaceProvenance?: string;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function parseFrontmatter(content: string): Record<string, string> {
|
|
33
|
+
const match = content.match(/^---\n([\s\S]*?)\n---/);
|
|
34
|
+
if (!match) return {};
|
|
35
|
+
const result: Record<string, string> = {};
|
|
36
|
+
for (const line of match[1].split('\n')) {
|
|
37
|
+
const [key, ...rest] = line.split(':');
|
|
38
|
+
if (key && rest.length > 0) {
|
|
39
|
+
result[key.trim()] = rest.join(':').trim();
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
return result;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function getSkillDirectories(cwd: string, homeDir: string): Array<{ root: string; origin: SkillOrigin }> {
|
|
46
|
+
return [
|
|
47
|
+
{ root: join(cwd, '.goodvibes', 'skills'), origin: 'project-local' },
|
|
48
|
+
{ root: join(cwd, '.goodvibes', 'tui', 'skills'), origin: 'project-local' },
|
|
49
|
+
{ root: join(homeDir, '.goodvibes', 'skills'), origin: 'global' },
|
|
50
|
+
{ root: join(homeDir, '.goodvibes', 'tui', 'skills'), origin: 'global' },
|
|
51
|
+
];
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
async function readSkillFile(path: string, origin: SkillOrigin): Promise<SkillRecord | null> {
|
|
55
|
+
let content = '';
|
|
56
|
+
try {
|
|
57
|
+
content = await fsPromises.readFile(path, 'utf-8');
|
|
58
|
+
} catch {
|
|
59
|
+
return null;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const frontmatter = parseFrontmatter(content);
|
|
63
|
+
const body = content.replace(/^---\n[\s\S]*?\n---\n?/, '');
|
|
64
|
+
const name = frontmatter.name ?? path.split(/[\\/]/).pop()?.replace(/\.md$/, '') ?? 'skill';
|
|
65
|
+
const description = frontmatter.description ?? frontmatter.summary ?? '';
|
|
66
|
+
const dependencies = frontmatter.depends_on
|
|
67
|
+
? frontmatter.depends_on.split(',').map((item) => item.trim()).filter(Boolean)
|
|
68
|
+
: [];
|
|
69
|
+
const includes: string[] = [];
|
|
70
|
+
const includeRegex = /^@([\w/-]+)/gm;
|
|
71
|
+
let match: RegExpExecArray | null;
|
|
72
|
+
while ((match = includeRegex.exec(body)) !== null) {
|
|
73
|
+
includes.push(match[1]);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
return {
|
|
77
|
+
name,
|
|
78
|
+
description,
|
|
79
|
+
path,
|
|
80
|
+
origin,
|
|
81
|
+
dependencies,
|
|
82
|
+
includes,
|
|
83
|
+
frontmatter,
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
async function scanSkillDirectory(root: string, origin: SkillOrigin): Promise<SkillRecord[]> {
|
|
88
|
+
let entries: string[] = [];
|
|
89
|
+
try {
|
|
90
|
+
entries = await fsPromises.readdir(root);
|
|
91
|
+
} catch {
|
|
92
|
+
return [];
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
const records: SkillRecord[] = [];
|
|
96
|
+
for (const entry of entries.sort((a, b) => a.localeCompare(b))) {
|
|
97
|
+
if (entry.endsWith('.md')) {
|
|
98
|
+
const record = await readSkillFile(join(root, entry), origin);
|
|
99
|
+
if (record) records.push(record);
|
|
100
|
+
continue;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
const markerPath = join(root, entry, 'SKILL.md');
|
|
104
|
+
const record = await readSkillFile(markerPath, origin);
|
|
105
|
+
if (record) records.push(record);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
return records;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Tags records whose file lives under an installed ecosystem-marketplace
|
|
113
|
+
* receipt's targetPath with that receipt's provenance summary. Best-effort:
|
|
114
|
+
* a lookup failure (e.g. no ecosystem catalogs configured) leaves records
|
|
115
|
+
* untagged rather than failing skill discovery.
|
|
116
|
+
*/
|
|
117
|
+
function applyMarketplaceProvenance(records: SkillRecord[], ecosystemPaths: EcosystemCatalogPathOptions): SkillRecord[] {
|
|
118
|
+
let receipts: ReturnType<typeof listInstalledEcosystemEntries>;
|
|
119
|
+
try {
|
|
120
|
+
receipts = listInstalledEcosystemEntries('skill', ecosystemPaths);
|
|
121
|
+
} catch {
|
|
122
|
+
return records;
|
|
123
|
+
}
|
|
124
|
+
if (receipts.length === 0) return records;
|
|
125
|
+
return records.map((record) => {
|
|
126
|
+
const receipt = receipts.find((candidate) => record.path === candidate.targetPath || record.path.startsWith(`${candidate.targetPath}${sep}`));
|
|
127
|
+
if (!receipt) return record;
|
|
128
|
+
return { ...record, marketplaceProvenance: receipt.provenanceSummary };
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
export async function discoverSkills(
|
|
133
|
+
shellPaths: Pick<ShellPathService, 'workingDirectory' | 'homeDirectory'>,
|
|
134
|
+
ecosystemPaths?: EcosystemCatalogPathOptions,
|
|
135
|
+
): Promise<SkillRecord[]> {
|
|
136
|
+
const cwd = shellPaths.workingDirectory;
|
|
137
|
+
const homeDir = shellPaths.homeDirectory;
|
|
138
|
+
const seen = new Set<string>();
|
|
139
|
+
const records: SkillRecord[] = [];
|
|
140
|
+
|
|
141
|
+
for (const { root, origin } of getSkillDirectories(cwd, homeDir)) {
|
|
142
|
+
for (const record of await scanSkillDirectory(root, origin)) {
|
|
143
|
+
if (seen.has(record.name.toLowerCase())) continue;
|
|
144
|
+
seen.add(record.name.toLowerCase());
|
|
145
|
+
records.push(record);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
const tagged = ecosystemPaths ? applyMarketplaceProvenance(records, ecosystemPaths) : records;
|
|
150
|
+
|
|
151
|
+
return tagged.sort((a, b) => {
|
|
152
|
+
const originRank = a.origin === b.origin
|
|
153
|
+
? 0
|
|
154
|
+
: a.origin === 'project-local'
|
|
155
|
+
? -1
|
|
156
|
+
: 1;
|
|
157
|
+
return originRank || a.name.localeCompare(b.name);
|
|
158
|
+
});
|
|
159
|
+
}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* view-deps.ts, what the built-in modals and views are built from.
|
|
3
|
+
*
|
|
4
|
+
* Composed once at startup (runtime/bootstrap-shell.ts) and handed to
|
|
5
|
+
* createShellViews (builtin-views.ts), which builds the always-on read models
|
|
6
|
+
* (usage, fleet acts and spawn) and registers every config-modal surface.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import type { ConfigManager, ServiceRegistry, SubscriptionManager } from '@pellux/goodvibes-sdk/platform/config';
|
|
10
|
+
import type { ToolRegistry } from '@pellux/goodvibes-sdk/platform/tools';
|
|
11
|
+
import type { ProviderRegistry } from '@pellux/goodvibes-sdk/platform/providers';
|
|
12
|
+
import type { Orchestrator, SessionMemoryStore } from '@pellux/goodvibes-sdk/platform/core';
|
|
13
|
+
import type { MemoryAccess } from '@pellux/goodvibes-sdk/platform/runtime/memory-spine';
|
|
14
|
+
import type { HookActivityTracker, HookDispatcher, HookWorkbench } from '@pellux/goodvibes-sdk/platform/hooks';
|
|
15
|
+
import type { UserAuthManager } from '@pellux/goodvibes-sdk/platform/security';
|
|
16
|
+
import type { ProjectPlanningService, KnowledgeApi } from '@pellux/goodvibes-sdk/platform/knowledge';
|
|
17
|
+
import type { SessionChangeTracker } from '@pellux/goodvibes-sdk/platform/sessions';
|
|
18
|
+
import type { WorkPlanStore } from '@pellux/goodvibes-sdk/platform/workflow';
|
|
19
|
+
import type { PolicyRuntimeState, SandboxSessionRegistry } from '@/runtime/index.ts';
|
|
20
|
+
import type { UiRuntimeServices } from '../runtime/ui-services.ts';
|
|
21
|
+
import type { PluginsModalManager } from './modals/plugins-modal.ts';
|
|
22
|
+
|
|
23
|
+
export interface BuiltinViewDeps {
|
|
24
|
+
readonly providerRegistry: ProviderRegistry;
|
|
25
|
+
readonly uiServices: UiRuntimeServices;
|
|
26
|
+
readonly toolRegistry?: ToolRegistry;
|
|
27
|
+
/** The main orchestrator (session usage and context size). */
|
|
28
|
+
readonly orchestrator?: Orchestrator;
|
|
29
|
+
/** The current model's context window in tokens. */
|
|
30
|
+
readonly getCtxWindow?: () => number;
|
|
31
|
+
readonly requestRender?: () => void;
|
|
32
|
+
readonly sandboxSessionRegistry: SandboxSessionRegistry;
|
|
33
|
+
/** Memory access for the Memory modal (the spine client, which routes to an adopted daemon). */
|
|
34
|
+
readonly memoryRegistry?: MemoryAccess;
|
|
35
|
+
readonly pluginManager?: PluginsModalManager;
|
|
36
|
+
readonly hookDispatcher?: Pick<HookDispatcher, 'listHooks' | 'getChains'>;
|
|
37
|
+
readonly hookWorkbench?: HookWorkbench;
|
|
38
|
+
readonly hookActivityTracker?: Pick<HookActivityTracker, 'listRecent'>;
|
|
39
|
+
readonly knowledgeApi?: KnowledgeApi;
|
|
40
|
+
/** The files this session edited (the Changes modal's default view). */
|
|
41
|
+
readonly sessionChangeTracker?: Pick<SessionChangeTracker, 'getChangedFiles'>;
|
|
42
|
+
/** Report a fleet act's result (late-bound to the command context's print). */
|
|
43
|
+
readonly fleetActsNotify?: (message: string) => void;
|
|
44
|
+
/** Hand a spawned session id to the one-key jump affordance (late-bound). */
|
|
45
|
+
readonly armFixSessionAttach?: (sessionId: string) => void;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/** The deps with everything the modals require resolved from the UI services. */
|
|
49
|
+
export type ResolvedBuiltinViewDeps = BuiltinViewDeps & {
|
|
50
|
+
readonly configManager: ConfigManager;
|
|
51
|
+
readonly localUserAuthManager: UserAuthManager;
|
|
52
|
+
readonly subscriptionManager: SubscriptionManager;
|
|
53
|
+
readonly serviceRegistry: ServiceRegistry;
|
|
54
|
+
readonly sessionMemoryStore: SessionMemoryStore;
|
|
55
|
+
readonly projectPlanningService: ProjectPlanningService;
|
|
56
|
+
readonly projectPlanningProjectId: string;
|
|
57
|
+
readonly workPlanStore: WorkPlanStore;
|
|
58
|
+
readonly policyRuntimeState: PolicyRuntimeState;
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
function required<T>(value: T | undefined, what: string): T {
|
|
62
|
+
if (value === undefined) throw new Error(`${what} must be wired at bootstrap for the built-in modals.`);
|
|
63
|
+
return value;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export function resolveBuiltinViewDeps(deps: BuiltinViewDeps): ResolvedBuiltinViewDeps {
|
|
67
|
+
const ui = deps.uiServices;
|
|
68
|
+
return {
|
|
69
|
+
...deps,
|
|
70
|
+
configManager: required(ui.platform.configManager, 'Config manager'),
|
|
71
|
+
localUserAuthManager: required(ui.platform.localUserAuthManager, 'Local auth manager'),
|
|
72
|
+
subscriptionManager: required(ui.platform.subscriptionManager, 'Subscription manager'),
|
|
73
|
+
serviceRegistry: required(ui.platform.serviceRegistry, 'Service registry'),
|
|
74
|
+
sessionMemoryStore: required(ui.sessions.sessionMemoryStore, 'Session memory store'),
|
|
75
|
+
projectPlanningService: required(ui.planning.projectPlanningService, 'Project planning service'),
|
|
76
|
+
projectPlanningProjectId: required(ui.planning.projectPlanningProjectId, 'Project planning project id'),
|
|
77
|
+
workPlanStore: required(ui.planning.workPlanStore, 'Work plan store'),
|
|
78
|
+
policyRuntimeState: required(ui.platform.policyRuntimeState, 'Policy runtime state'),
|
|
79
|
+
};
|
|
80
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* view-panel-adapter.ts, the operator API's panels.list / panels.open, answered
|
|
3
|
+
* by the modal views.
|
|
4
|
+
*
|
|
5
|
+
* THE ONLY PLACE PANE VOCABULARY IS KEPT IN THE TUI. The terminal has no panes:
|
|
6
|
+
* every view opens as a modal. The names here (ViewPanelAdapter, PanelManagerLike,
|
|
7
|
+
* getTopPane/getBottomPane, NO_PANES, the `panels` field) are kept on purpose
|
|
8
|
+
* because they are the SDK's contract, not TUI design:
|
|
9
|
+
* - the SDK's IntegrationHelperService serves the operator API verbs
|
|
10
|
+
* panels.list and panels.open (and the /api/panels/* HTTP routes) from the
|
|
11
|
+
* `panelManager` option, typed PanelManagerLike;
|
|
12
|
+
* - remote clients, the web UI and the daemon already speak those verb names.
|
|
13
|
+
* Renaming them is an SDK and wire change, so the TUI keeps one adapter that
|
|
14
|
+
* satisfies the contract and translates it: panels.list lists the views a
|
|
15
|
+
* remote client can open (Agents, Usage, Changes, Notifications, Sessions),
|
|
16
|
+
* panels.open opens one as its modal, and both pane lists are always empty
|
|
17
|
+
* because nothing is ever open in a pane. The opener is set by the shell once
|
|
18
|
+
* the modal host exists. See docs/architecture.md ("The operator API bridge").
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
import type { IntegrationHelperService } from '@/runtime/index.ts';
|
|
22
|
+
|
|
23
|
+
/** The SDK's panel-manager contract, as IntegrationHelperService takes it. */
|
|
24
|
+
type PanelManagerLike = ConstructorParameters<typeof IntegrationHelperService>[0]['panelManager'];
|
|
25
|
+
type PanelPaneLike = ReturnType<PanelManagerLike['getTopPane']>;
|
|
26
|
+
type PanelRegistrationLike = ReturnType<PanelManagerLike['getRegisteredTypes']>[number];
|
|
27
|
+
|
|
28
|
+
const VIEWS: readonly PanelRegistrationLike[] = [
|
|
29
|
+
{ id: 'agents', name: 'Agents', category: 'views', description: 'Running and finished agents, chains, workflows and hosted sessions; steer, stop, open a transcript' },
|
|
30
|
+
{ id: 'usage', name: 'Usage', category: 'views', description: 'Context pressure, session tokens and cost, per-turn history, per-agent costs, budget alert' },
|
|
31
|
+
{ id: 'changes', name: 'Changes', category: 'views', description: 'Changed files with a tinted diff, semantic summary, hunk staging, review comments and recent commits' },
|
|
32
|
+
{ id: 'notifications', name: 'Notifications', category: 'views', description: 'Everything the notification feed collected, newest first' },
|
|
33
|
+
{ id: 'sessions', name: 'Sessions', category: 'views', description: 'Saved, hosted and cross-surface sessions' },
|
|
34
|
+
];
|
|
35
|
+
|
|
36
|
+
const NO_PANES: PanelPaneLike = { panels: [], activeIndex: 0 };
|
|
37
|
+
|
|
38
|
+
export interface ViewPanelAdapter extends PanelManagerLike {
|
|
39
|
+
/** Set how a view name is opened (returns false when nothing holds it). */
|
|
40
|
+
setOpener(open: (id: string) => boolean): void;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export function createViewPanelAdapter(): ViewPanelAdapter {
|
|
44
|
+
let opener: ((id: string) => boolean) | null = null;
|
|
45
|
+
return {
|
|
46
|
+
getTopPane: () => NO_PANES,
|
|
47
|
+
getBottomPane: () => NO_PANES,
|
|
48
|
+
getRegisteredTypes: () => VIEWS,
|
|
49
|
+
open: (id: string) => opener?.(id) ?? false,
|
|
50
|
+
show: () => {},
|
|
51
|
+
setOpener: (open) => { opener = open; },
|
|
52
|
+
};
|
|
53
|
+
}
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
// blockedReason, orphaned, stalled, agentId), edges (from → to dependency
|
|
8
8
|
// links), and the elastic-pool state (ready/running/atCap/maxSize). This module
|
|
9
9
|
// is a pure string renderer; every line is fit to the given width (no clipping,
|
|
10
|
-
// no horizontal overflow), so the same output drives a
|
|
10
|
+
// no horizontal overflow), so the same output drives a view or a command.
|
|
11
11
|
// ---------------------------------------------------------------------------
|
|
12
12
|
|
|
13
13
|
import type { OperatorMethodOutput } from '@pellux/goodvibes-sdk';
|
|
@@ -1,101 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* conversation-fold.ts, which transcript rows render FOLDED, and what spacing
|
|
3
|
-
* that earns them.
|
|
4
|
-
*
|
|
5
|
-
* A folded tool result is exactly ONE row: its header, with the preview riding
|
|
6
|
-
* on the same line after the `▸ N lines` badge. It used to be four rows, a
|
|
7
|
-
* `▄▄▄` cap, an interior preview line carrying a second `[▸ N hidden]` count, a
|
|
8
|
-
* `▀▀▀` cap, and a blank after all of it, for one line of text the header had
|
|
9
|
-
* already sized.
|
|
10
|
-
*
|
|
11
|
-
* These predicates live in their own leaf module because TWO callers must agree
|
|
12
|
-
* on them and neither may depend on the other: the row renderer
|
|
13
|
-
* (conversation-rendering.ts) decides how to draw the row, and the line cache
|
|
14
|
-
* (conversation-line-cache.ts) decides the blank separator that follows it. If
|
|
15
|
-
* those two ever disagreed about which rows are folded, a warm cache and a cold
|
|
16
|
-
* rebuild would space the transcript differently, the exact class of bug the
|
|
17
|
-
* cache's byte-identical contract exists to rule out.
|
|
18
|
-
*
|
|
19
|
-
* Everything here is PURE: collapse state is read, never written, so a row can
|
|
20
|
-
* be asked about before it has rendered.
|
|
21
|
-
*/
|
|
22
|
-
|
|
23
|
-
import { foldedToolResult, trailingBlankAfterRow } from '@pellux/goodvibes-terminal-shell';
|
|
24
|
-
import { summarizeToolResult } from '../renderer/tool-result-summary.ts';
|
|
25
|
-
import { isTurnCollapsed, type ConversationRenderContext } from './conversation-render-context.ts';
|
|
26
|
-
import { collapseKeyForNode, type RenderNode } from './conversation-turn-structure.ts';
|
|
27
|
-
import type { ConversationMessageSnapshot } from '@pellux/goodvibes-sdk/platform/core';
|
|
28
|
-
|
|
29
|
-
/** The subset of a render context these predicates read. */
|
|
30
|
-
type FoldContext = Pick<ConversationRenderContext, 'assistantTurns' | 'collapseState'>;
|
|
31
|
-
|
|
32
|
-
/**
|
|
33
|
-
* Does a tool-result row render FOLDED, one compact header row with its preview
|
|
34
|
-
* on it, rather than header-plus-expanded-body?
|
|
35
|
-
*
|
|
36
|
-
* An unset collapse key answers the same as the default the row itself will
|
|
37
|
-
* store (collapsed), so asking before and asking after the row renders give the
|
|
38
|
-
* same answer.
|
|
39
|
-
*/
|
|
40
|
-
export function isToolResultFolded(
|
|
41
|
-
message: Extract<ConversationMessageSnapshot, { role: 'tool' }>,
|
|
42
|
-
collapseState: ReadonlyMap<string, boolean>,
|
|
43
|
-
collapseKey: string,
|
|
44
|
-
): boolean {
|
|
45
|
-
// Threading only, the decision, the short-content threshold and the
|
|
46
|
-
// unset-key default all belong to foldedToolResult(). summarizeToolResult is
|
|
47
|
-
// this product's own renderer concern, so resolving it stays here: a
|
|
48
|
-
// summarizable result stays folded even when short, because the one-line
|
|
49
|
-
// summary is the better row and the raw payload belongs behind the toggle.
|
|
50
|
-
return foldedToolResult({
|
|
51
|
-
contentLength: message.content.length,
|
|
52
|
-
hasSummary: summarizeToolResult(message.toolName, message.content) !== null,
|
|
53
|
-
storedCollapsed: collapseState.get(collapseKey),
|
|
54
|
-
});
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
/** A row that belongs to a tool run: the call itself, or the result under it. */
|
|
58
|
-
function isToolRunNode(node: RenderNode): boolean {
|
|
59
|
-
return node.kind === 'toolcall' || node.message.role === 'tool';
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
/**
|
|
63
|
-
* Does this planned row emit exactly one folded tool-result line?
|
|
64
|
-
*
|
|
65
|
-
* A collapsed turn hides the row entirely, so there is no folded row to butt up
|
|
66
|
-
* against the next one.
|
|
67
|
-
*/
|
|
68
|
-
function rendersFoldedToolRow(node: RenderNode, context: FoldContext): boolean {
|
|
69
|
-
if (node.kind === 'toolcall') return false;
|
|
70
|
-
const message = node.message;
|
|
71
|
-
if (message.role !== 'tool') return false;
|
|
72
|
-
if (isTurnCollapsed(context.assistantTurns?.get(node.absIdx), context.collapseState)) return false;
|
|
73
|
-
return isToolResultFolded(message, context.collapseState, collapseKeyForNode(node));
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
/**
|
|
77
|
-
* The blank separator that follows a planned row.
|
|
78
|
-
*
|
|
79
|
-
* Branch rows sit tight under their parent, so the blank lands only after the
|
|
80
|
-
* last row of a top-level unit, that is what keeps a turn's whole subtree
|
|
81
|
-
* reading as one block. On top of that, a folded row followed by more tool
|
|
82
|
-
* machinery gets NO blank at all: N consecutive folded results stack as N
|
|
83
|
-
* adjacent single rows. A folded row followed by anything that is not tool
|
|
84
|
-
* machinery keeps its blank, so the run still separates from the prose around
|
|
85
|
-
* it.
|
|
86
|
-
*/
|
|
87
|
-
export function trailingBlankAfter(
|
|
88
|
-
node: RenderNode,
|
|
89
|
-
next: RenderNode | undefined,
|
|
90
|
-
context: FoldContext,
|
|
91
|
-
): boolean {
|
|
92
|
-
// Adaptation only, RenderNode is this product's shape, so reading depth and
|
|
93
|
-
// node kind off it stays here; the rule those facts feed belongs to
|
|
94
|
-
// trailingBlankAfterRow(). No `next` at all means end of transcript: neither
|
|
95
|
-
// a branch row nor tool machinery follows, which is the plain-blank case.
|
|
96
|
-
return trailingBlankAfterRow({
|
|
97
|
-
nextIsBranchRow: next !== undefined && next.depth !== 0,
|
|
98
|
-
nextIsToolMachinery: next !== undefined && isToolRunNode(next),
|
|
99
|
-
rowRendersFolded: rendersFoldedToolRow(node, context),
|
|
100
|
-
});
|
|
101
|
-
}
|