@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
|
@@ -11,17 +11,33 @@ import type { SessionSurface } from '@/runtime/index.ts';
|
|
|
11
11
|
import { journalPathFor, openTranscriptJournal, type TranscriptJournal } from '@pellux/goodvibes-sdk/platform/runtime/operations';
|
|
12
12
|
import type { WebhookNotifier } from '@pellux/goodvibes-sdk/platform/integrations';
|
|
13
13
|
import { notifyCompletion } from '@pellux/goodvibes-sdk/platform/utils';
|
|
14
|
-
import { maybeNotifyLongTask, readNotifyAfterSeconds
|
|
14
|
+
import { maybeNotifyLongTask, readNotifyAfterSeconds } from './long-task-notifier.ts';
|
|
15
15
|
import type { FocusTracker } from '@pellux/goodvibes-sdk/platform/runtime/operations';
|
|
16
|
-
import {
|
|
16
|
+
import {
|
|
17
|
+
shouldFireAlert,
|
|
18
|
+
FORCE_NOTIFY_DURATION_MS,
|
|
19
|
+
NOTIFICATION_TEXT_LIMITS,
|
|
20
|
+
TurnActivityTally,
|
|
21
|
+
buildTurnNotificationLine,
|
|
22
|
+
readNotificationsMetadataOnly,
|
|
23
|
+
resolveTurnName,
|
|
24
|
+
trimAtWordBoundary,
|
|
25
|
+
type TurnOutcome,
|
|
26
|
+
} from '@pellux/goodvibes-sdk/platform/runtime/operations';
|
|
17
27
|
import { createBudgetBreachNotifier, type BudgetBreachNotifier } from './budget-breach-notifier.ts';
|
|
18
28
|
import { readBudgetAlertUsd } from '@pellux/goodvibes-sdk/platform/providers';
|
|
19
29
|
|
|
20
30
|
/** Minimal orchestrator surface required by turn-event wiring. */
|
|
21
31
|
interface TurnOrchestrator {
|
|
22
32
|
readonly lastInputTokens: number;
|
|
23
|
-
/** Cumulative session usage, same object
|
|
33
|
+
/** Cumulative session usage, same object the usage modal reads, used here for budget-breach checks. */
|
|
24
34
|
readonly usage: { readonly input: number; readonly output: number; readonly cacheRead: number; readonly cacheWrite: number };
|
|
35
|
+
/**
|
|
36
|
+
* The SDK Orchestrator's own end-of-turn popup; handOff() silences it while
|
|
37
|
+
* this wiring's long-task notice owns the popup and returns the release.
|
|
38
|
+
* Optional so bare test doubles still satisfy the type.
|
|
39
|
+
*/
|
|
40
|
+
readonly turnEndNotice?: { handOff(): () => void };
|
|
25
41
|
}
|
|
26
42
|
|
|
27
43
|
/** Minimal provider registry surface required by turn-event wiring. */
|
|
@@ -99,6 +115,22 @@ export interface WireTurnEventHandlersOptions {
|
|
|
99
115
|
* Absent in tests/headless.
|
|
100
116
|
*/
|
|
101
117
|
readonly terminalNotifier?: import('./terminal-notifier.ts').TerminalNotifier | null;
|
|
118
|
+
/**
|
|
119
|
+
* Desktop delivery for the turn, budget, agent and workstream notices wired
|
|
120
|
+
* here; defaults to the SDK notifyCompletion. Tests pass a spy to read the text.
|
|
121
|
+
*/
|
|
122
|
+
readonly notifyDesktop?: typeof notifyCompletion;
|
|
123
|
+
/**
|
|
124
|
+
* The terminal bell for a turn over 5s that did not reach the desktop popup
|
|
125
|
+
* (the popup rings it itself); defaults to writing BEL to stdout, which is
|
|
126
|
+
* what the SDK Orchestrator's own popup did before this wiring took it over.
|
|
127
|
+
*/
|
|
128
|
+
readonly ringBell?: () => void;
|
|
129
|
+
/**
|
|
130
|
+
* How long a failover retry may take to start before the failed turn it
|
|
131
|
+
* replaces is told as failed after all. @internal, tests only.
|
|
132
|
+
*/
|
|
133
|
+
readonly _failoverRetryGraceMs?: number;
|
|
102
134
|
/**
|
|
103
135
|
* Minimal test seam: injectable clock for controlling Date.now() in tests.
|
|
104
136
|
* Defaults to the real Date.now when absent.
|
|
@@ -133,6 +165,35 @@ function wrapJournalWithSessionRebind(
|
|
|
133
165
|
};
|
|
134
166
|
}
|
|
135
167
|
|
|
168
|
+
/**
|
|
169
|
+
* How long a failover retry may take to reach TURN_SUBMITTED (it waits on the
|
|
170
|
+
* memory-recall refresh first) before the failure it replaced is told anyway.
|
|
171
|
+
*/
|
|
172
|
+
const FAILOVER_RETRY_GRACE_MS = 30_000;
|
|
173
|
+
|
|
174
|
+
/** The Orchestrator's own popup rang the bell for a turn over this long. */
|
|
175
|
+
const TURN_BELL_AFTER_MS = 5_000;
|
|
176
|
+
|
|
177
|
+
/** The Orchestrator's own popup came for a turn over this long (SDK notifyCompletion). */
|
|
178
|
+
const TURN_DESKTOP_AFTER_MS = 30_000;
|
|
179
|
+
|
|
180
|
+
/** Most agent/workstream names kept at once; a lost terminal event cannot grow the maps unbounded. */
|
|
181
|
+
const MAX_REMEMBERED_NAMES = 256;
|
|
182
|
+
|
|
183
|
+
function rememberBounded<V>(map: Map<string, V>, id: string, value: V): void {
|
|
184
|
+
map.delete(id);
|
|
185
|
+
map.set(id, value);
|
|
186
|
+
while (map.size > MAX_REMEMBERED_NAMES) {
|
|
187
|
+
const oldest = map.keys().next().value;
|
|
188
|
+
if (oldest === undefined) break;
|
|
189
|
+
map.delete(oldest);
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
function normalizeTitleSource(source: unknown): 'user' | 'system' | null {
|
|
194
|
+
return source === 'user' || source === 'system' ? source : null;
|
|
195
|
+
}
|
|
196
|
+
|
|
136
197
|
export interface WireTurnEventHandlersResult {
|
|
137
198
|
/** Trigger a git status refresh; may be called from external code after tool execution. */
|
|
138
199
|
readonly refreshGit: () => void;
|
|
@@ -140,6 +201,14 @@ export interface WireTurnEventHandlersResult {
|
|
|
140
201
|
readonly unsubs: ReadonlyArray<() => void>;
|
|
141
202
|
/** The per-session transcript journal; call appendRecord() for user-submitted events. */
|
|
142
203
|
readonly transcriptJournal: TranscriptJournal;
|
|
204
|
+
/**
|
|
205
|
+
* The failover path re-submits the failed turn on another provider (see
|
|
206
|
+
* stream-event-wiring.ts onFailoverRetry). Call it synchronously from that
|
|
207
|
+
* TURN_ERROR: the failure notice for the turn is withheld, and the retry's
|
|
208
|
+
* TURN_SUBMITTED continues the same user turn, so the user gets one notice
|
|
209
|
+
* for how it finally ended.
|
|
210
|
+
*/
|
|
211
|
+
readonly continueTurnAfterFailover: () => void;
|
|
143
212
|
}
|
|
144
213
|
|
|
145
214
|
/**
|
|
@@ -166,12 +235,23 @@ export function wireTurnEventHandlers(
|
|
|
166
235
|
surface, gitStatusProvider,
|
|
167
236
|
lastGitInfoRef, buildSessionContinuityHints, render, webhookNotifier, focusTracker,
|
|
168
237
|
terminalNotifier, runtimeBus, systemMessageRouter,
|
|
238
|
+
notifyDesktop = notifyCompletion,
|
|
239
|
+
ringBell = () => { process.stdout.write('\x07'); },
|
|
240
|
+
_failoverRetryGraceMs = FAILOVER_RETRY_GRACE_MS,
|
|
169
241
|
_clock = Date.now,
|
|
170
242
|
} = options;
|
|
171
243
|
|
|
172
244
|
const unsubs: Array<() => void> = [];
|
|
173
245
|
const configGet = (k: string): unknown => configManager.get(k as Parameters<typeof configManager.get>[0]);
|
|
174
246
|
|
|
247
|
+
// ONE POPUP PER TURN. The long-task notice below owns the turn's desktop
|
|
248
|
+
// popup: it names the turn, counts what it did, honors
|
|
249
|
+
// behavior.notifyAfterSeconds and the unfocused-only gate, and reaches the
|
|
250
|
+
// webhook. The SDK Orchestrator pops its own end-of-turn notice too, so it
|
|
251
|
+
// is handed off here for as long as this wiring lives.
|
|
252
|
+
const releaseTurnEndNotice = orchestrator.turnEndNotice?.handOff();
|
|
253
|
+
if (releaseTurnEndNotice) unsubs.push(releaseTurnEndNotice);
|
|
254
|
+
|
|
175
255
|
// Create the per-session transcript journal. Path mirrors recovery-file
|
|
176
256
|
// convention (homeDirectory-scoped). Created lazily on first append.
|
|
177
257
|
//
|
|
@@ -192,47 +272,162 @@ export function wireTurnEventHandlers(
|
|
|
192
272
|
// Track turn start time for long-task notification threshold.
|
|
193
273
|
let turnStartTime: number | null = null;
|
|
194
274
|
|
|
275
|
+
// What names the turn and what it did, for the end-of-turn notices
|
|
276
|
+
// (SDK runtime/turn-notification.ts): the submitted text, and a tally of
|
|
277
|
+
// tool calls, changed files, agents started and the latest review score.
|
|
278
|
+
let turnText: string | null = null;
|
|
279
|
+
let lastTurnName: string | null = null;
|
|
280
|
+
let lastEndedTurnId: string | null = null;
|
|
281
|
+
const tally = new TurnActivityTally();
|
|
282
|
+
const nameCurrentTurn = (): string | null => resolveTurnName({
|
|
283
|
+
title: conversation.title,
|
|
284
|
+
titleSource: normalizeTitleSource(conversation.getTitleSource()),
|
|
285
|
+
turnText,
|
|
286
|
+
});
|
|
287
|
+
|
|
288
|
+
// Task text for agents and workstreams, from their opening events; the
|
|
289
|
+
// terminal events carry only the id. Bounded, dropped at the terminal event.
|
|
290
|
+
const agentTasks = new Map<string, string>();
|
|
291
|
+
const workstreams = new Map<string, { task: string; reviewScore: number | null }>();
|
|
292
|
+
|
|
195
293
|
// Budget-breach edge-trigger checker, one instance per session,
|
|
196
294
|
// piggybacking on the same TURN_COMPLETED handler as the long-task
|
|
197
295
|
// notification below rather than adding a second TURN_COMPLETED subscription.
|
|
198
296
|
const budgetBreachNotifier: BudgetBreachNotifier | null = focusTracker
|
|
199
|
-
? createBudgetBreachNotifier({
|
|
297
|
+
? createBudgetBreachNotifier({
|
|
298
|
+
focusTracker, configGet, webhookNotifier: webhookNotifier ?? null, sessionId: runtime.sessionId,
|
|
299
|
+
getTurnName: () => lastTurnName, notifyDesktop,
|
|
300
|
+
})
|
|
200
301
|
: null;
|
|
201
302
|
|
|
303
|
+
// Provider failover (stream-event-wiring.ts) re-submits a failed turn on
|
|
304
|
+
// another provider. The user asked once, so the user turn spans every
|
|
305
|
+
// attempt: its failure is withheld while the retry is on its way, and the
|
|
306
|
+
// retry's TURN_SUBMITTED continues it (same start time, tally and name).
|
|
307
|
+
let failoverRetryPending = false;
|
|
308
|
+
let withheldFailure: { turnId: string; reason: string | null; timer: ReturnType<typeof setTimeout> } | null = null;
|
|
309
|
+
const clearWithheldFailure = (): void => {
|
|
310
|
+
if (withheldFailure) clearTimeout(withheldFailure.timer);
|
|
311
|
+
withheldFailure = null;
|
|
312
|
+
};
|
|
313
|
+
const continueTurnAfterFailover = (): void => {
|
|
314
|
+
failoverRetryPending = true;
|
|
315
|
+
};
|
|
316
|
+
|
|
317
|
+
/**
|
|
318
|
+
* The end of a turn, however it ended: the in-terminal (OSC 9) notice on
|
|
319
|
+
* its own per-signal config + focus gate, then the long-task desktop and
|
|
320
|
+
* webhook notice past behavior.notifyAfterSeconds. Once per turnId.
|
|
321
|
+
*/
|
|
322
|
+
const notifyTurnEnd = (turnId: string, outcome: TurnOutcome, reason: string | null): void => {
|
|
323
|
+
if (turnId === lastEndedTurnId) return;
|
|
324
|
+
lastEndedTurnId = turnId;
|
|
325
|
+
clearWithheldFailure();
|
|
326
|
+
failoverRetryPending = false;
|
|
327
|
+
const turnElapsedMs = turnStartTime !== null ? _clock() - turnStartTime : 0;
|
|
328
|
+
turnStartTime = null;
|
|
329
|
+
const name = nameCurrentTurn();
|
|
330
|
+
lastTurnName = name;
|
|
331
|
+
const activity = tally.snapshot();
|
|
332
|
+
const metadataOnly = readNotificationsMetadataOnly(configGet);
|
|
333
|
+
terminalNotifier?.notify('turn-end', buildTurnNotificationLine({
|
|
334
|
+
outcome, elapsedMs: turnElapsedMs, name, reason, sessionId: runtime.sessionId, ...activity,
|
|
335
|
+
}, { metadataOnly }, NOTIFICATION_TEXT_LIMITS.terminal));
|
|
336
|
+
const notifyOnComplete = configGet('behavior.notifyOnComplete') !== false;
|
|
337
|
+
const popped = maybeNotifyLongTask({
|
|
338
|
+
elapsedMs: turnElapsedMs,
|
|
339
|
+
status: outcome === 'completed' ? 'ok' : 'fail',
|
|
340
|
+
outcome,
|
|
341
|
+
name,
|
|
342
|
+
reason,
|
|
343
|
+
activity,
|
|
344
|
+
metadataOnly,
|
|
345
|
+
kind: 'turn',
|
|
346
|
+
sessionId: runtime.sessionId,
|
|
347
|
+
thresholdSeconds: readNotifyAfterSeconds(configGet),
|
|
348
|
+
webhookNotifier: webhookNotifier ?? null,
|
|
349
|
+
focusTracker,
|
|
350
|
+
configGet,
|
|
351
|
+
notifyDesktop,
|
|
352
|
+
desktop: notifyOnComplete,
|
|
353
|
+
// The SDK popup this replaces came past 30s whatever notifyAfterSeconds
|
|
354
|
+
// said (default 60): the one popup keeps that, the webhook keeps the threshold.
|
|
355
|
+
desktopAfterMs: TURN_DESKTOP_AFTER_MS,
|
|
356
|
+
});
|
|
357
|
+
// The popup rings the bell itself; a shorter turn still gets the bell the
|
|
358
|
+
// Orchestrator's own popup gave it (behavior.notifyOnComplete).
|
|
359
|
+
if (!popped && notifyOnComplete && turnElapsedMs > TURN_BELL_AFTER_MS) {
|
|
360
|
+
try { ringBell(); } catch { /* stdout may already be torn down */ }
|
|
361
|
+
}
|
|
362
|
+
};
|
|
363
|
+
|
|
364
|
+
/**
|
|
365
|
+
* A TURN_ERROR. The failover path decides synchronously, in its own
|
|
366
|
+
* TURN_ERROR handler, whether to re-submit the turn, and this handler runs
|
|
367
|
+
* first (main.ts wires it first), so the notice waits a microtask for that
|
|
368
|
+
* decision. Withheld while a retry is on its way; told anyway if the retry
|
|
369
|
+
* has not started within the grace period.
|
|
370
|
+
*/
|
|
371
|
+
const onTurnError = (turnId: string, reason: string | null): void => {
|
|
372
|
+
queueMicrotask(() => {
|
|
373
|
+
if (!failoverRetryPending) {
|
|
374
|
+
notifyTurnEnd(turnId, 'failed', reason);
|
|
375
|
+
return;
|
|
376
|
+
}
|
|
377
|
+
clearWithheldFailure();
|
|
378
|
+
const timer = setTimeout(() => {
|
|
379
|
+
if (withheldFailure?.turnId !== turnId) return;
|
|
380
|
+
notifyTurnEnd(turnId, 'failed', reason);
|
|
381
|
+
}, _failoverRetryGraceMs);
|
|
382
|
+
(timer as { unref?: () => void }).unref?.();
|
|
383
|
+
withheldFailure = { turnId, reason, timer };
|
|
384
|
+
});
|
|
385
|
+
};
|
|
386
|
+
unsubs.push(clearWithheldFailure);
|
|
387
|
+
|
|
202
388
|
const refreshGit = (): void => {
|
|
203
389
|
gitStatusProvider.refresh().then((info) => { lastGitInfoRef.value = info; render(); }).catch(() => { /* non-fatal */ });
|
|
204
390
|
};
|
|
205
391
|
|
|
206
392
|
// Journal user message immediately on TURN_SUBMITTED so a SIGKILL during
|
|
207
393
|
// the subsequent stream loses at most the in-flight token chunk.
|
|
208
|
-
unsubs.push(events.turns.on('TURN_SUBMITTED', () => {
|
|
209
|
-
|
|
394
|
+
unsubs.push(events.turns.on('TURN_SUBMITTED', (evt) => {
|
|
395
|
+
if (failoverRetryPending && withheldFailure !== null) {
|
|
396
|
+
// The failover's re-submission: the same user turn goes on.
|
|
397
|
+
clearWithheldFailure();
|
|
398
|
+
failoverRetryPending = false;
|
|
399
|
+
} else {
|
|
400
|
+
clearWithheldFailure();
|
|
401
|
+
failoverRetryPending = false;
|
|
402
|
+
turnStartTime = _clock();
|
|
403
|
+
turnText = typeof evt?.prompt === 'string' ? evt.prompt : null;
|
|
404
|
+
tally.reset();
|
|
405
|
+
}
|
|
210
406
|
try {
|
|
211
407
|
const snap = conversation.toJSON() as { messages: Array<import('./conversation.ts').ConversationMessageSnapshot> };
|
|
212
408
|
transcriptJournal.appendRecord('user_message', snap.messages);
|
|
213
409
|
} catch { /* best-effort */ }
|
|
214
410
|
}));
|
|
215
411
|
|
|
412
|
+
// A turn that failed or was cancelled ends without TURN_COMPLETED; it is
|
|
413
|
+
// named and told the same way.
|
|
414
|
+
unsubs.push(events.turns.on('TURN_ERROR', (evt) => {
|
|
415
|
+
onTurnError(evt.turnId, evt.error);
|
|
416
|
+
}));
|
|
417
|
+
unsubs.push(events.turns.on('PREFLIGHT_FAIL', (evt) => {
|
|
418
|
+
notifyTurnEnd(evt.turnId, 'failed', evt.reason);
|
|
419
|
+
}));
|
|
420
|
+
unsubs.push(events.turns.on('TURN_CANCEL', (evt) => {
|
|
421
|
+
notifyTurnEnd(evt.turnId, 'cancelled', evt.reason ?? null);
|
|
422
|
+
}));
|
|
423
|
+
|
|
216
424
|
unsubs.push(events.turns.on('TURN_COMPLETED', (evt) => {
|
|
217
|
-
// Long-task push notification: fires when the turn exceeded the threshold.
|
|
218
|
-
const turnElapsedMs = turnStartTime !== null ? _clock() - turnStartTime : 0;
|
|
219
|
-
turnStartTime = null;
|
|
220
|
-
const notifyThreshold = readNotifyAfterSeconds((k) => configManager.get(k as Parameters<typeof configManager.get>[0]));
|
|
221
425
|
// stopReason 'empty_response' signals a non-successful completion.
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
elapsedMs: turnElapsedMs,
|
|
228
|
-
status: taskStatus,
|
|
229
|
-
kind: 'turn',
|
|
230
|
-
sessionId: runtime.sessionId,
|
|
231
|
-
thresholdSeconds: notifyThreshold,
|
|
232
|
-
webhookNotifier: webhookNotifier ?? null,
|
|
233
|
-
focusTracker,
|
|
234
|
-
configGet,
|
|
235
|
-
});
|
|
426
|
+
notifyTurnEnd(
|
|
427
|
+
evt.turnId,
|
|
428
|
+
evt.stopReason === 'completed' ? 'completed' : 'failed',
|
|
429
|
+
evt.stopReason === 'completed' ? null : 'The model returned an empty response',
|
|
430
|
+
);
|
|
236
431
|
// Budget-breach alert: edge-triggered, piggybacks on this same
|
|
237
432
|
// TURN_COMPLETED handler rather than a second subscription.
|
|
238
433
|
if (budgetBreachNotifier) {
|
|
@@ -291,20 +486,48 @@ export function wireTurnEventHandlers(
|
|
|
291
486
|
refreshGit();
|
|
292
487
|
}));
|
|
293
488
|
|
|
489
|
+
// Activity for the end-of-turn notice, counted only while a turn runs.
|
|
490
|
+
unsubs.push(events.tools.on('TOOL_RECEIVED', (payload) => {
|
|
491
|
+
if (turnStartTime !== null) tally.noteToolReceived(payload.callId, payload.tool, payload.args);
|
|
492
|
+
}));
|
|
493
|
+
unsubs.push(events.tools.on('TOOL_CANCELLED', (payload) => {
|
|
494
|
+
if (turnStartTime !== null) tally.noteToolFailed(payload.callId);
|
|
495
|
+
}));
|
|
496
|
+
unsubs.push(events.agents.on('AGENT_SPAWNING', (payload) => {
|
|
497
|
+
if (turnStartTime !== null) tally.noteAgentStarted();
|
|
498
|
+
rememberBounded(agentTasks, payload.agentId, payload.task);
|
|
499
|
+
}));
|
|
500
|
+
unsubs.push(events.agents.on('AGENT_COMPLETED', (payload) => { agentTasks.delete(payload.agentId); }));
|
|
501
|
+
unsubs.push(events.agents.on('AGENT_CANCELLED', (payload) => { agentTasks.delete(payload.agentId); }));
|
|
502
|
+
unsubs.push(events.workflows.on('WORKFLOW_CHAIN_CREATED', (payload) => {
|
|
503
|
+
rememberBounded(workstreams, payload.chainId, { task: payload.task, reviewScore: null });
|
|
504
|
+
}));
|
|
505
|
+
unsubs.push(events.workflows.on('WORKFLOW_REVIEW_COMPLETED', (payload) => {
|
|
506
|
+
if (turnStartTime !== null) tally.noteReviewScore(payload.score);
|
|
507
|
+
const entry = workstreams.get(payload.chainId);
|
|
508
|
+
if (entry) entry.reviewScore = payload.score;
|
|
509
|
+
}));
|
|
510
|
+
unsubs.push(events.workflows.on('WORKFLOW_CHAIN_PASSED', (payload) => { workstreams.delete(payload.chainId); }));
|
|
511
|
+
|
|
294
512
|
// In-terminal (OSC 9) agent-blocked notification: a delegated agent parked
|
|
295
513
|
// waiting for a human message (AGENT_AWAITING_MESSAGE) is "blocked on you".
|
|
296
|
-
// Gated by the notifier's own per-signal config + focus rule.
|
|
297
|
-
//
|
|
514
|
+
// Gated by the notifier's own per-signal config + focus rule. Names the
|
|
515
|
+
// agent's task unless behavior.notificationsMetadataOnly is on.
|
|
298
516
|
if (terminalNotifier) {
|
|
299
517
|
unsubs.push(events.agents.on('AGENT_AWAITING_MESSAGE', (payload) => {
|
|
300
|
-
|
|
518
|
+
const task = readNotificationsMetadataOnly(configGet) ? null : agentTasks.get(payload.agentId);
|
|
519
|
+
terminalNotifier.notify('agent-blocked', task
|
|
520
|
+
? trimAtWordBoundary(`Agent waiting for your input: ${task}`, NOTIFICATION_TEXT_LIMITS.terminal)
|
|
521
|
+
: `agent ${payload.agentId.slice(0, 8)} is waiting for your input`);
|
|
301
522
|
}));
|
|
302
523
|
}
|
|
303
524
|
|
|
304
|
-
unsubs.push(events.tools.on('TOOL_SUCCEEDED', () => {
|
|
525
|
+
unsubs.push(events.tools.on('TOOL_SUCCEEDED', (payload) => {
|
|
526
|
+
if (turnStartTime !== null) tally.noteToolSucceeded(payload.callId);
|
|
305
527
|
refreshGit();
|
|
306
528
|
}));
|
|
307
|
-
unsubs.push(events.tools.on('TOOL_FAILED', () => {
|
|
529
|
+
unsubs.push(events.tools.on('TOOL_FAILED', (payload) => {
|
|
530
|
+
if (turnStartTime !== null) tally.noteToolFailed(payload.callId);
|
|
308
531
|
refreshGit();
|
|
309
532
|
}));
|
|
310
533
|
|
|
@@ -318,22 +541,38 @@ export function wireTurnEventHandlers(
|
|
|
318
541
|
// a webhook that's already covered.
|
|
319
542
|
if (focusTracker) {
|
|
320
543
|
unsubs.push(events.agents.on('AGENT_FAILED', (payload) => {
|
|
544
|
+
const task = agentTasks.get(payload.agentId);
|
|
545
|
+
agentTasks.delete(payload.agentId);
|
|
321
546
|
if (!shouldFireAlert(focusTracker, configGet, 'behavior.notifyOnAgentFailure')) return;
|
|
322
547
|
try {
|
|
323
|
-
|
|
548
|
+
const metadataOnly = readNotificationsMetadataOnly(configGet);
|
|
549
|
+
const name = metadataOnly ? '' : trimAtWordBoundary(task ?? '', NOTIFICATION_TEXT_LIMITS.desktopTitle - 'Agent failed: '.length);
|
|
550
|
+
notifyDesktop(
|
|
551
|
+
name ? `Agent failed: ${name}` : 'GoodVibes: agent failed',
|
|
552
|
+
metadataOnly
|
|
553
|
+
? `agent ${payload.agentId.slice(0, 8)} failed`
|
|
554
|
+
: trimAtWordBoundary(`agent ${payload.agentId.slice(0, 8)} failed: ${payload.error}`, NOTIFICATION_TEXT_LIMITS.desktopBody),
|
|
555
|
+
FORCE_NOTIFY_DURATION_MS,
|
|
556
|
+
);
|
|
324
557
|
} catch (err) {
|
|
325
558
|
logger.debug('turn-event-wiring: agent-failure notify error', { error: String(err) });
|
|
326
559
|
}
|
|
327
560
|
}));
|
|
328
561
|
unsubs.push(events.workflows.on('WORKFLOW_CHAIN_FAILED', (payload) => {
|
|
562
|
+
const workstream = workstreams.get(payload.chainId);
|
|
563
|
+
workstreams.delete(payload.chainId);
|
|
329
564
|
if (!shouldFireAlert(focusTracker, configGet, 'behavior.notifyOnChainFailure')) return;
|
|
330
565
|
try {
|
|
331
566
|
// Title and body come from workstream-notification.ts, which is where
|
|
332
567
|
// the three branches (cancelled / turn budget / failed) are narrated and
|
|
333
568
|
// tested. A notification is a message to a person, so it carries neither
|
|
334
569
|
// the internal name for the machinery nor the chain id.
|
|
335
|
-
const notice = workstreamFailureNotification(payload
|
|
336
|
-
|
|
570
|
+
const notice = workstreamFailureNotification(payload, {
|
|
571
|
+
task: workstream?.task,
|
|
572
|
+
reviewScore: workstream?.reviewScore,
|
|
573
|
+
metadataOnly: readNotificationsMetadataOnly(configGet),
|
|
574
|
+
});
|
|
575
|
+
notifyDesktop(notice.title, notice.body, FORCE_NOTIFY_DURATION_MS);
|
|
337
576
|
} catch (err) {
|
|
338
577
|
logger.debug('turn-event-wiring: chain-failure notify error', { error: String(err) });
|
|
339
578
|
}
|
|
@@ -368,5 +607,5 @@ export function wireTurnEventHandlers(
|
|
|
368
607
|
}));
|
|
369
608
|
}
|
|
370
609
|
|
|
371
|
-
return { refreshGit, unsubs, transcriptJournal };
|
|
610
|
+
return { refreshGit, unsubs, transcriptJournal, continueTurnAfterFailover };
|
|
372
611
|
}
|