@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
|
@@ -14,16 +14,30 @@
|
|
|
14
14
|
* wrapped promise settles) so it reflects "a prompt just appeared", not
|
|
15
15
|
* "a prompt was just resolved".
|
|
16
16
|
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
17
|
+
* Text (owner ruling 2026-09-29, SDK runtime/turn-notification.ts): the
|
|
18
|
+
* notice names what is waiting, the command for exec, the file for write and
|
|
19
|
+
* edit, the URL for fetch, and the turn that asked for it. When
|
|
20
|
+
* behavior.notificationsMetadataOnly is on (default off) every channel here
|
|
21
|
+
* (OSC 9, desktop, webhook) sends the tool name and permission category only,
|
|
22
|
+
* never a command, a path or the turn's name.
|
|
20
23
|
*/
|
|
21
24
|
import { notifyCompletion } from '@pellux/goodvibes-sdk/platform/utils';
|
|
22
25
|
import { logger } from '@pellux/goodvibes-sdk/platform/utils';
|
|
23
26
|
import type { WebhookNotifier } from '@pellux/goodvibes-sdk/platform/integrations';
|
|
24
27
|
import type { PermissionRequestHandler, PermissionPromptRequest } from '@pellux/goodvibes-sdk/platform/permissions';
|
|
25
28
|
import type { FocusTracker } from '@pellux/goodvibes-sdk/platform/runtime/operations';
|
|
26
|
-
import {
|
|
29
|
+
import {
|
|
30
|
+
shouldFireAlert,
|
|
31
|
+
FORCE_NOTIFY_DURATION_MS,
|
|
32
|
+
NOTIFICATION_TEXT_LIMITS,
|
|
33
|
+
buildApprovalNotification,
|
|
34
|
+
describeToolTarget,
|
|
35
|
+
formatWebhookText,
|
|
36
|
+
joinNotificationLine,
|
|
37
|
+
readNotificationsMetadataOnly,
|
|
38
|
+
resolveTurnName,
|
|
39
|
+
type ConfigGet,
|
|
40
|
+
} from '@pellux/goodvibes-sdk/platform/runtime/operations';
|
|
27
41
|
import type { TerminalNotifier } from './terminal-notifier.ts';
|
|
28
42
|
|
|
29
43
|
export interface ApprovalAlertDeps {
|
|
@@ -37,6 +51,21 @@ export interface ApprovalAlertDeps {
|
|
|
37
51
|
* desktop-alert gate below). Absent in tests/headless.
|
|
38
52
|
*/
|
|
39
53
|
readonly terminalNotifier?: TerminalNotifier | null;
|
|
54
|
+
/**
|
|
55
|
+
* The live conversation, read for the name of the turn that is asking: a
|
|
56
|
+
* title the user set, else the message that started this turn (the last
|
|
57
|
+
* user message while a turn runs). Absent means the notice names no turn.
|
|
58
|
+
*/
|
|
59
|
+
readonly conversation?: ApprovalTurnSource | null;
|
|
60
|
+
/** Desktop delivery; defaults to the SDK notifyCompletion. Tests pass a spy to read the text. */
|
|
61
|
+
readonly notifyDesktop?: typeof notifyCompletion;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/** The conversation surface the approval notice names the turn from. */
|
|
65
|
+
export interface ApprovalTurnSource {
|
|
66
|
+
readonly title: string;
|
|
67
|
+
getTitleSource(): string;
|
|
68
|
+
getLastUserMessage(): string | null;
|
|
40
69
|
}
|
|
41
70
|
|
|
42
71
|
/**
|
|
@@ -56,19 +85,28 @@ export function wrapRequestPermissionWithAlert(
|
|
|
56
85
|
}
|
|
57
86
|
|
|
58
87
|
function fireApprovalAlert(request: PermissionPromptRequest, deps: ApprovalAlertDeps): void {
|
|
59
|
-
|
|
60
|
-
const
|
|
88
|
+
const metadataOnly = readNotificationsMetadataOnly(deps.configGet);
|
|
89
|
+
const conversation = deps.conversation;
|
|
90
|
+
const titleSource = conversation?.getTitleSource();
|
|
91
|
+
const notice = buildApprovalNotification({
|
|
92
|
+
tool: request.tool,
|
|
93
|
+
category: request.category,
|
|
94
|
+
target: metadataOnly ? null : describeToolTarget(request.args, request.analysis?.target),
|
|
95
|
+
turnName: metadataOnly || !conversation ? null : resolveTurnName({
|
|
96
|
+
title: conversation.title,
|
|
97
|
+
titleSource: titleSource === 'user' || titleSource === 'system' ? titleSource : null,
|
|
98
|
+
turnText: conversation.getLastUserMessage(),
|
|
99
|
+
}),
|
|
100
|
+
}, { metadataOnly });
|
|
61
101
|
|
|
62
102
|
// In-terminal (OSC 9) notification fires on its OWN gating (independent of the
|
|
63
103
|
// desktop-alert gate below), so it is emitted before the early return.
|
|
64
|
-
deps.terminalNotifier?.notify('approval-wait',
|
|
104
|
+
deps.terminalNotifier?.notify('approval-wait', joinNotificationLine(notice, NOTIFICATION_TEXT_LIMITS.terminal));
|
|
65
105
|
|
|
66
106
|
if (!shouldFireAlert(deps.focusTracker, deps.configGet, 'behavior.notifyOnApprovalPending')) return;
|
|
67
107
|
|
|
68
|
-
const title = 'GoodVibes: approval needed';
|
|
69
|
-
|
|
70
108
|
try {
|
|
71
|
-
notifyCompletion(title,
|
|
109
|
+
(deps.notifyDesktop ?? notifyCompletion)(notice.title, notice.body, FORCE_NOTIFY_DURATION_MS);
|
|
72
110
|
} catch (err) {
|
|
73
111
|
logger.debug('approval-alert: desktop notify error', { error: String(err) });
|
|
74
112
|
}
|
|
@@ -77,7 +115,7 @@ function fireApprovalAlert(request: PermissionPromptRequest, deps: ApprovalAlert
|
|
|
77
115
|
if (webhookNotifier) {
|
|
78
116
|
const urls = webhookNotifier.getUrls();
|
|
79
117
|
if (urls.length > 0) {
|
|
80
|
-
webhookNotifier.send(
|
|
118
|
+
webhookNotifier.send(formatWebhookText(notice)).catch((err: unknown) => {
|
|
81
119
|
logger.debug('approval-alert: webhook send error', { error: String(err) });
|
|
82
120
|
});
|
|
83
121
|
}
|
|
@@ -25,7 +25,13 @@
|
|
|
25
25
|
|
|
26
26
|
import type { ConversationManager } from './conversation.ts';
|
|
27
27
|
|
|
28
|
-
|
|
28
|
+
/**
|
|
29
|
+
* `msg_<idx>` (a message block), or `bead_c:<idx>:<call>` (a work-tree bead of
|
|
30
|
+
* the main transcript: its call's message index). A bead inside a folded turn
|
|
31
|
+
* or lane has no block of its own; its message line is the row that stands
|
|
32
|
+
* for it (the folded turn's header, or the ◉ of its folded lane's spawn).
|
|
33
|
+
*/
|
|
34
|
+
const MESSAGE_BOOKMARK_KEY = /^(?:msg_|bead_c:)(\d+)(?::\d+)?$/;
|
|
29
35
|
|
|
30
36
|
/**
|
|
31
37
|
* Resolve a bookmark key that missed a direct BlockMeta lookup. Returns the
|
|
@@ -13,18 +13,28 @@
|
|
|
13
13
|
* (notifyCompletion) + outbound webhook (WebhookNotifier), both gated by
|
|
14
14
|
* the shared focus/config gating in alert-gating.ts. Skips evaluation
|
|
15
15
|
* entirely when the session model is unpriced, a $0 placeholder cost must
|
|
16
|
-
* never be reported as a real breach (mirrors
|
|
16
|
+
* never be reported as a real breach (mirrors the usage modal's "unpriced"
|
|
17
17
|
* display convention).
|
|
18
18
|
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
19
|
+
* Text (owner ruling 2026-09-29, SDK runtime/turn-notification.ts): the
|
|
20
|
+
* notice names the budget that tripped (cost against the configured
|
|
21
|
+
* threshold) and the turn during which it tripped. When
|
|
22
|
+
* behavior.notificationsMetadataOnly is on (default off) the desktop and
|
|
23
|
+
* webhook text carries the numbers and the session id prefix only.
|
|
21
24
|
*/
|
|
22
25
|
import { notifyCompletion } from '@pellux/goodvibes-sdk/platform/utils';
|
|
23
26
|
import { logger } from '@pellux/goodvibes-sdk/platform/utils';
|
|
24
27
|
import type { WebhookNotifier } from '@pellux/goodvibes-sdk/platform/integrations';
|
|
25
28
|
import { calcSessionCost, computeBudgetBreach, isModelPriced } from '@pellux/goodvibes-sdk/platform/providers';
|
|
26
29
|
import type { FocusTracker } from '@pellux/goodvibes-sdk/platform/runtime/operations';
|
|
27
|
-
import {
|
|
30
|
+
import {
|
|
31
|
+
shouldFireAlert,
|
|
32
|
+
FORCE_NOTIFY_DURATION_MS,
|
|
33
|
+
buildBudgetNotification,
|
|
34
|
+
formatWebhookText,
|
|
35
|
+
readNotificationsMetadataOnly,
|
|
36
|
+
type ConfigGet,
|
|
37
|
+
} from '@pellux/goodvibes-sdk/platform/runtime/operations';
|
|
28
38
|
|
|
29
39
|
export interface BudgetBreachUsageSnapshot {
|
|
30
40
|
readonly input: number;
|
|
@@ -38,6 +48,10 @@ export interface BudgetBreachNotifierDeps {
|
|
|
38
48
|
readonly configGet: ConfigGet;
|
|
39
49
|
readonly webhookNotifier?: WebhookNotifier | null;
|
|
40
50
|
readonly sessionId: string;
|
|
51
|
+
/** The name of the turn that just ended (the one during which the budget tripped). */
|
|
52
|
+
readonly getTurnName?: (() => string | null) | undefined;
|
|
53
|
+
/** Desktop delivery; defaults to the SDK notifyCompletion. Tests pass a spy to read the text. */
|
|
54
|
+
readonly notifyDesktop?: typeof notifyCompletion | undefined;
|
|
41
55
|
}
|
|
42
56
|
|
|
43
57
|
/** Stateful edge-trigger checker, construct once per session, call on every TURN_COMPLETED. */
|
|
@@ -85,11 +99,16 @@ export function createBudgetBreachNotifier(deps: BudgetBreachNotifierDeps): Budg
|
|
|
85
99
|
function fireBudgetBreachAlert(deps: BudgetBreachNotifierDeps, sessionCost: number, budgetThresholdUsd: number): void {
|
|
86
100
|
if (!shouldFireAlert(deps.focusTracker, deps.configGet, 'behavior.notifyOnBudgetBreach')) return;
|
|
87
101
|
|
|
88
|
-
const
|
|
89
|
-
const
|
|
102
|
+
const metadataOnly = readNotificationsMetadataOnly(deps.configGet);
|
|
103
|
+
const notice = buildBudgetNotification({
|
|
104
|
+
sessionCostUsd: sessionCost,
|
|
105
|
+
budgetUsd: budgetThresholdUsd,
|
|
106
|
+
sessionId: deps.sessionId,
|
|
107
|
+
turnName: metadataOnly ? null : deps.getTurnName?.() ?? null,
|
|
108
|
+
}, { metadataOnly });
|
|
90
109
|
|
|
91
110
|
try {
|
|
92
|
-
notifyCompletion(title,
|
|
111
|
+
(deps.notifyDesktop ?? notifyCompletion)(notice.title, notice.body, FORCE_NOTIFY_DURATION_MS);
|
|
93
112
|
} catch (err) {
|
|
94
113
|
logger.debug('budget-breach-notifier: desktop notify error', { error: String(err) });
|
|
95
114
|
}
|
|
@@ -98,7 +117,7 @@ function fireBudgetBreachAlert(deps: BudgetBreachNotifierDeps, sessionCost: numb
|
|
|
98
117
|
if (webhookNotifier) {
|
|
99
118
|
const urls = webhookNotifier.getUrls();
|
|
100
119
|
if (urls.length > 0) {
|
|
101
|
-
webhookNotifier.send(
|
|
120
|
+
webhookNotifier.send(formatWebhookText(notice)).catch((err: unknown) => {
|
|
102
121
|
logger.debug('budget-breach-notifier: webhook send error', { error: String(err) });
|
|
103
122
|
});
|
|
104
123
|
}
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
*
|
|
16
16
|
* The `[Compaction]` prefix is one of the FORCE_CONVERSATION_PREFIXES the
|
|
17
17
|
* system-message router always surfaces inline (see system-message-router.ts),
|
|
18
|
-
* so the receipt cannot be routed away
|
|
18
|
+
* so the receipt cannot be routed away from the transcript and vanish.
|
|
19
19
|
*/
|
|
20
20
|
|
|
21
21
|
/** The shape of the SDK's COMPACTION_RECEIPT event payload we render from. */
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Shared context-window usage computation.
|
|
3
3
|
*
|
|
4
|
-
* Centralises the tokens/contextWindow ratio so all
|
|
4
|
+
* Centralises the tokens/contextWindow ratio so all views, auto-compact,
|
|
5
5
|
* and session-maintenance read from one formula rather than six diverging
|
|
6
6
|
* hand-rolled copies.
|
|
7
7
|
*
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* architecture line-count gate; behaviour is unchanged.
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
|
-
import { activeTheme } from '../renderer/theme.ts';
|
|
10
|
+
import { activeTheme, activeTokens } from '../renderer/theme.ts';
|
|
11
11
|
import { renderMarkdownTracked } from '../renderer/markdown.ts';
|
|
12
12
|
import { renderConversationEventLine, renderConversationFoldedRow } from '../renderer/conversation-surface.ts';
|
|
13
13
|
import { GLYPHS } from '../renderer/ui-primitives.ts';
|
|
@@ -54,10 +54,10 @@ export function renderCompactionContinuationMessage(
|
|
|
54
54
|
markerFg: T.toolAccent,
|
|
55
55
|
label: 'compaction handoff',
|
|
56
56
|
labelFg: T.toolAccent,
|
|
57
|
-
detailFg:
|
|
57
|
+
detailFg: activeTokens().textMuted,
|
|
58
58
|
};
|
|
59
59
|
const details = [
|
|
60
|
-
{ text: ` ${isCollapsed ? GLYPHS.navigation.collapsed : GLYPHS.navigation.expanded} ${lineCount} line${lineCount === 1 ? '' : 's'} `, fg:
|
|
60
|
+
{ text: ` ${isCollapsed ? GLYPHS.navigation.collapsed : GLYPHS.navigation.expanded} ${lineCount} line${lineCount === 1 ? '' : 's'} `, fg: activeTokens().textFaint },
|
|
61
61
|
];
|
|
62
62
|
|
|
63
63
|
if (isCollapsed) {
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* conversation-display-only.ts, the display-only output a conversation keeps.
|
|
3
|
+
*
|
|
4
|
+
* A command's printed text or a tool result row is drawn into the transcript
|
|
5
|
+
* but is not a message, so a rebuild (a resize, a theme change, the splash
|
|
6
|
+
* leaving) would drop it. ConversationManager keeps each such draw here, with
|
|
7
|
+
* the number of messages that existed when it was drawn, and draws it again
|
|
8
|
+
* in the same place on every rebuild. While a reply streams, draws made during
|
|
9
|
+
* the turn sit below the streamed text and are drawn again after each delta.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
/** Display-only draws kept for redraw; the oldest leave first. */
|
|
13
|
+
const MAX_DISPLAY_ONLY_ENTRIES = 500;
|
|
14
|
+
|
|
15
|
+
interface DisplayOnlyEntry {
|
|
16
|
+
readonly seq: number;
|
|
17
|
+
/** Messages that existed when it was drawn: it goes right after the unit holding message `anchor - 1`. */
|
|
18
|
+
readonly anchor: number;
|
|
19
|
+
readonly draw: (width: number) => void;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/** One rebuild's walk over the kept draws, in order. */
|
|
23
|
+
export interface DisplayOnlyCursor {
|
|
24
|
+
/** Draw every kept entry printed while messages up to `lastIndex` existed. */
|
|
25
|
+
through(lastIndex: number): void;
|
|
26
|
+
/** During a streaming rebuild: the entries not yet drawn go below the streamed text. */
|
|
27
|
+
trailStreamed(lineCount: () => number): void;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export class DisplayOnlyOutput {
|
|
31
|
+
private entries: DisplayOnlyEntry[] = [];
|
|
32
|
+
private seq = 0;
|
|
33
|
+
/** While a reply streams, draws from this seq on sit below the streamed text. */
|
|
34
|
+
private trailingFromSeq = Number.POSITIVE_INFINITY;
|
|
35
|
+
/** Lines those trailing draws take, below the streamed text. */
|
|
36
|
+
trailingLineCount = 0;
|
|
37
|
+
|
|
38
|
+
get count(): number { return this.entries.length; }
|
|
39
|
+
|
|
40
|
+
drop(): void {
|
|
41
|
+
this.entries = [];
|
|
42
|
+
this.resetTrailing();
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
resetTrailing(): void {
|
|
46
|
+
this.trailingFromSeq = Number.POSITIVE_INFINITY;
|
|
47
|
+
this.trailingLineCount = 0;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/** Keep a draw; `anchor` is the message count when it was printed. */
|
|
51
|
+
keep(anchor: number, draw: (width: number) => void): number {
|
|
52
|
+
const seq = ++this.seq;
|
|
53
|
+
this.entries.push({ seq, anchor, draw });
|
|
54
|
+
if (this.entries.length > MAX_DISPLAY_ONLY_ENTRIES) this.entries.splice(0, this.entries.length - MAX_DISPLAY_ONLY_ENTRIES);
|
|
55
|
+
return seq;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/** A kept draw was just drawn at the end of the buffer; while streaming it trails the streamed text. */
|
|
59
|
+
noteDrawn(seq: number, lines: number, streaming: boolean): void {
|
|
60
|
+
if (!streaming) return;
|
|
61
|
+
if (this.trailingFromSeq === Number.POSITIVE_INFINITY) this.trailingFromSeq = seq;
|
|
62
|
+
this.trailingLineCount += lines;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/** Draw the entries kept below the streamed text again, and count their lines. */
|
|
66
|
+
drawTrailing(width: number, lineCount: () => number): void {
|
|
67
|
+
const before = lineCount();
|
|
68
|
+
for (const entry of this.entries) if (entry.seq >= this.trailingFromSeq) entry.draw(width);
|
|
69
|
+
this.trailingLineCount = lineCount() - before;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
cursor(width: number): DisplayOnlyCursor {
|
|
73
|
+
const kept = this.entries;
|
|
74
|
+
let next = 0;
|
|
75
|
+
return {
|
|
76
|
+
through: (lastIndex) => {
|
|
77
|
+
while (next < kept.length && kept[next]!.anchor <= lastIndex + 1) kept[next++]!.draw(width);
|
|
78
|
+
},
|
|
79
|
+
trailStreamed: (lineCount) => {
|
|
80
|
+
if (next >= kept.length) return;
|
|
81
|
+
this.trailingFromSeq = kept[next]!.seq;
|
|
82
|
+
this.drawTrailing(width, lineCount);
|
|
83
|
+
},
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
}
|
|
@@ -40,3 +40,30 @@ export function resolveTranscriptEventLine(
|
|
|
40
40
|
}
|
|
41
41
|
return [...lines].reverse().find((line) => line < currentLine) ?? lines[lines.length - 1]!;
|
|
42
42
|
}
|
|
43
|
+
|
|
44
|
+
/** The next (or previous) line in a sorted list after (or before) `currentLine`, wrapping around; -1 when the list is empty. */
|
|
45
|
+
export function wrapToLine(lines: readonly number[], currentLine: number, direction: 'next' | 'prev'): number {
|
|
46
|
+
if (lines.length === 0) return -1;
|
|
47
|
+
if (direction === 'next') return lines.find((line) => line > currentLine) ?? lines[0]!;
|
|
48
|
+
return [...lines].reverse().find((line) => line < currentLine) ?? lines[lines.length - 1]!;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/** The block containing `lineIndex`, else the one starting nearest it (optionally only blocks of `typeFilter`). */
|
|
52
|
+
export function nearestBlock<B extends { readonly type: string; readonly startLine: number; readonly lineCount: number }>(
|
|
53
|
+
blocks: readonly B[],
|
|
54
|
+
lineIndex: number,
|
|
55
|
+
typeFilter?: string,
|
|
56
|
+
): B | null {
|
|
57
|
+
let nearest: B | null = null;
|
|
58
|
+
let nearestDist = Infinity;
|
|
59
|
+
for (const block of blocks) {
|
|
60
|
+
if (typeFilter !== undefined && block.type !== typeFilter) continue;
|
|
61
|
+
if (lineIndex >= block.startLine && lineIndex < block.startLine + block.lineCount) return block;
|
|
62
|
+
const dist = Math.abs(block.startLine - lineIndex);
|
|
63
|
+
if (dist < nearestDist) {
|
|
64
|
+
nearestDist = dist;
|
|
65
|
+
nearest = block;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
return nearest;
|
|
69
|
+
}
|