@pellux/goodvibes-tui 2.0.14 → 2.0.15
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/.goodvibes/GOODVIBES.md +1 -1
- package/.goodvibes/agents/reviewer.md +8 -8
- package/.goodvibes/skills/add-provider/SKILL.md +22 -22
- package/CHANGELOG.md +35 -0
- package/README.md +40 -29
- package/docs/README.md +1 -1
- package/docs/foundation-artifacts/README.md +5 -5
- package/docs/foundation-artifacts/operator-contract.json +123 -123
- package/package.json +12 -10
- package/scripts/postinstall.js +3 -3
- package/src/audio/activation-sound.ts +7 -7
- package/src/audio/capture.ts +7 -7
- package/src/audio/onnxruntime-assets.d.ts +1 -1
- package/src/audio/player.ts +4 -4
- package/src/audio/spoken-turn-wiring.ts +4 -4
- package/src/audio/voice-capture-wiring.ts +5 -5
- package/src/audio/voice-input-session.ts +3 -3
- package/src/audio/wake-inference.ts +13 -8
- package/src/audio/wake-runtime.ts +10 -10
- package/src/cli/completions/generate.ts +2 -2
- package/src/cli/doctor.ts +13 -13
- package/src/cli/ensure-goodvibes-gitignore.ts +3 -3
- package/src/cli/entrypoint.ts +3 -3
- package/src/cli/help.ts +6 -6
- package/src/cli/hooks-command.ts +4 -4
- package/src/cli/hooks-report.ts +4 -4
- package/src/cli/launch-auto-update.ts +17 -17
- package/src/cli/management-commands.ts +8 -8
- package/src/cli/management-utils.ts +3 -3
- package/src/cli/management.ts +1 -1
- package/src/cli/plugin-bundles-command.ts +10 -10
- package/src/cli/plugin-command.ts +9 -9
- package/src/cli/service-posture.ts +1 -1
- package/src/cli/status.ts +10 -10
- package/src/cli/surface-command.ts +4 -4
- package/src/cli/tui-startup.ts +23 -23
- package/src/config/run-daemon-config-migration.ts +3 -3
- package/src/config/secret-config.ts +10 -10
- package/src/config/surface.ts +1 -1
- package/src/config/tui-extension-settings.ts +8 -8
- package/src/core/active-model-identity.ts +7 -7
- package/src/core/approval-alert.ts +5 -5
- package/src/core/bookmark-navigation.ts +3 -3
- package/src/core/budget-breach-notifier.ts +6 -6
- package/src/core/compaction-receipt.ts +8 -8
- package/src/core/composer-state.ts +2 -2
- package/src/core/context-usage.ts +1 -1
- package/src/core/conversation-compaction-render.ts +3 -3
- package/src/core/conversation-event-navigation.ts +2 -2
- package/src/core/conversation-fold.ts +9 -9
- package/src/core/conversation-line-cache.ts +14 -14
- package/src/core/conversation-render-context.ts +6 -6
- package/src/core/conversation-rendering.ts +27 -30
- package/src/core/conversation-search-expansion.ts +6 -6
- package/src/core/conversation-splash-state.ts +3 -3
- package/src/core/conversation-turn-structure.ts +13 -13
- package/src/core/conversation-types.ts +3 -3
- package/src/core/conversation-user-receipts.ts +5 -5
- package/src/core/conversation.ts +17 -17
- package/src/core/format-user-error.ts +5 -5
- package/src/core/long-task-notifier.ts +9 -9
- package/src/core/memory-diagnostics-gateway.ts +8 -8
- package/src/core/memory-provenance.ts +5 -5
- package/src/core/model-routing-chip.ts +3 -3
- package/src/core/pairing-tailscale-gateway.ts +3 -3
- package/src/core/permission-mode.ts +5 -5
- package/src/core/power-chip-source.ts +3 -3
- package/src/core/power-status.ts +8 -8
- package/src/core/rewind-receipt.ts +6 -6
- package/src/core/session-resume-core.ts +8 -8
- package/src/core/stream-event-wiring.ts +36 -36
- package/src/core/stream-stall-watchdog.ts +4 -4
- package/src/core/system-message-noise.ts +7 -7
- package/src/core/system-message-router.ts +10 -10
- package/src/core/terminal-notifier.ts +5 -5
- package/src/core/turn-budget-outcome.ts +5 -5
- package/src/core/turn-cancellation.ts +3 -3
- package/src/core/turn-event-wiring.ts +16 -16
- package/src/core/voice-capture-status.ts +2 -2
- package/src/core/voice-provision-gateway.ts +12 -12
- package/src/core/voice-provision-status.ts +19 -19
- package/src/core/voice-stt-gateway.ts +6 -6
- package/src/core/wake-provision-runner.ts +7 -7
- package/src/core/wake-provision-status.ts +17 -17
- package/src/core/workstream-notification.ts +8 -8
- package/src/export/gist-uploader.ts +7 -7
- package/src/input/autocomplete.ts +3 -3
- package/src/input/bookmark-modal.ts +1 -1
- package/src/input/command-args-hint.ts +2 -2
- package/src/input/command-reference.ts +9 -9
- package/src/input/command-registry.ts +32 -32
- package/src/input/commands/acp-runtime.ts +6 -6
- package/src/input/commands/calendar-runtime.ts +9 -9
- package/src/input/commands/channel-pairing.ts +11 -11
- package/src/input/commands/channel-runtime.ts +1 -1
- package/src/input/commands/checkin-runtime.ts +4 -4
- package/src/input/commands/checkpoint-runtime.ts +4 -4
- package/src/input/commands/ci-runtime.ts +4 -4
- package/src/input/commands/cluster-runtime.ts +3 -3
- package/src/input/commands/codebase-runtime.ts +16 -16
- package/src/input/commands/config.ts +6 -6
- package/src/input/commands/connection-status.ts +12 -12
- package/src/input/commands/context-window.ts +2 -2
- package/src/input/commands/cost-attribution-format.ts +4 -4
- package/src/input/commands/cost-runtime.ts +2 -2
- package/src/input/commands/devices-runtime.ts +7 -7
- package/src/input/commands/diff-runtime.ts +2 -2
- package/src/input/commands/editor-runtime.ts +1 -1
- package/src/input/commands/eval.ts +4 -4
- package/src/input/commands/experience-runtime.ts +1 -1
- package/src/input/commands/git-runtime.ts +3 -3
- package/src/input/commands/graph-runtime.ts +3 -3
- package/src/input/commands/guidance-runtime.ts +1 -1
- package/src/input/commands/health-metrics-format.ts +2 -2
- package/src/input/commands/health-runtime.ts +8 -8
- package/src/input/commands/hosted-runtime.ts +13 -13
- package/src/input/commands/image-runtime.ts +6 -6
- package/src/input/commands/intelligence-runtime.ts +2 -2
- package/src/input/commands/local-auth-runtime.ts +3 -3
- package/src/input/commands/local-provider-runtime.ts +3 -3
- package/src/input/commands/local-runtime.ts +7 -7
- package/src/input/commands/mail-runtime.ts +9 -9
- package/src/input/commands/mcp-runtime.ts +1 -1
- package/src/input/commands/memory.ts +13 -13
- package/src/input/commands/notify-runtime.ts +1 -1
- package/src/input/commands/operator-panel-runtime.ts +8 -8
- package/src/input/commands/operator-rpc.ts +3 -3
- package/src/input/commands/operator-runtime.ts +3 -3
- package/src/input/commands/palette-runtime.ts +1 -1
- package/src/input/commands/payment-card-intake.ts +17 -17
- package/src/input/commands/permissions-provenance.ts +7 -7
- package/src/input/commands/permissions-runtime.ts +1 -1
- package/src/input/commands/planning-runtime.ts +9 -9
- package/src/input/commands/platform-services-runtime.ts +2 -2
- package/src/input/commands/policy-dispatch.ts +6 -6
- package/src/input/commands/power-runtime.ts +6 -6
- package/src/input/commands/principals-runtime.ts +3 -3
- package/src/input/commands/product-runtime.ts +3 -3
- package/src/input/commands/profile-render.ts +13 -13
- package/src/input/commands/profile-runtime.ts +13 -13
- package/src/input/commands/profile-types.ts +13 -13
- package/src/input/commands/provider-accounts-runtime.ts +5 -5
- package/src/input/commands/provider.ts +10 -10
- package/src/input/commands/qrcode-runtime.ts +3 -3
- package/src/input/commands/queue-runtime.ts +6 -6
- package/src/input/commands/recall-files-config.ts +4 -4
- package/src/input/commands/recall-files-git.ts +3 -3
- package/src/input/commands/recall-files.ts +12 -12
- package/src/input/commands/recall-query.ts +3 -3
- package/src/input/commands/recall-review.ts +3 -3
- package/src/input/commands/recall-shared.ts +1 -1
- package/src/input/commands/relay-runtime.ts +5 -5
- package/src/input/commands/remote-runtime.ts +3 -3
- package/src/input/commands/review-runtime.ts +8 -8
- package/src/input/commands/rewind-runtime.ts +21 -21
- package/src/input/commands/runtime-services.ts +1 -2
- package/src/input/commands/schedule-nl.ts +2 -2
- package/src/input/commands/schedule-runtime.ts +2 -2
- package/src/input/commands/secret-runtime.ts +3 -3
- package/src/input/commands/session-content.ts +7 -7
- package/src/input/commands/session-picker-filter.ts +3 -3
- package/src/input/commands/session-workflow.ts +10 -10
- package/src/input/commands/session.ts +15 -15
- package/src/input/commands/settings-sync-runtime.ts +2 -2
- package/src/input/commands/share-runtime.ts +4 -4
- package/src/input/commands/shell-core.ts +11 -11
- package/src/input/commands/tasks-runtime.ts +4 -4
- package/src/input/commands/test-runtime.ts +6 -6
- package/src/input/commands/tts-runtime.ts +4 -4
- package/src/input/commands/update-runtime.ts +27 -27
- package/src/input/commands/websearch-runtime.ts +2 -2
- package/src/input/commands/work-plan-runtime.ts +2 -2
- package/src/input/commands/workstream-attempt-dependents.ts +4 -4
- package/src/input/commands/workstream-attempts.ts +16 -16
- package/src/input/commands/workstream-runtime.ts +36 -36
- package/src/input/commands/worktree-runtime.ts +6 -6
- package/src/input/composer-capture.ts +5 -5
- package/src/input/concealed-input.ts +2 -2
- package/src/input/config-modal-types.ts +16 -16
- package/src/input/config-modal.ts +30 -30
- package/src/input/daemon-owned-settings-descriptions.ts +4 -4
- package/src/input/delete-key-policy.ts +1 -1
- package/src/input/exec-env-scrub-config.ts +7 -7
- package/src/input/feature-unit-layout.ts +5 -5
- package/src/input/feed-context-factory.ts +38 -38
- package/src/input/file-picker.ts +1 -1
- package/src/input/handler-command-route.ts +2 -2
- package/src/input/handler-content-actions.ts +8 -8
- package/src/input/handler-feed-routes.ts +13 -13
- package/src/input/handler-feed.ts +31 -31
- package/src/input/handler-interactions.ts +2 -2
- package/src/input/handler-modal-routes.ts +13 -13
- package/src/input/handler-modal-stack.ts +1 -1
- package/src/input/handler-modal-token-routes.ts +1 -1
- package/src/input/handler-onboarding-daemon-adopt.ts +6 -6
- package/src/input/handler-picker-routes.ts +7 -7
- package/src/input/handler-prompt-buffer.ts +1 -1
- package/src/input/handler-shortcuts.ts +12 -12
- package/src/input/handler-types.ts +3 -3
- package/src/input/handler-ui-state.ts +1 -1
- package/src/input/handler.ts +9 -9
- package/src/input/input-history.ts +1 -1
- package/src/input/keybindings.ts +13 -13
- package/src/input/kill-ring.ts +7 -11
- package/src/input/model-picker-filter.ts +3 -3
- package/src/input/model-picker-items.ts +5 -5
- package/src/input/model-picker-types.ts +2 -2
- package/src/input/model-picker.ts +18 -18
- package/src/input/onboarding/onboarding-feature-units.ts +9 -13
- package/src/input/onboarding/onboarding-provider-key-fields.ts +3 -3
- package/src/input/onboarding/onboarding-runtime-status.ts +3 -3
- package/src/input/onboarding/onboarding-wizard-apply.ts +6 -6
- package/src/input/onboarding/onboarding-wizard-cloudflare-step.ts +1 -1
- package/src/input/onboarding/onboarding-wizard-constants.ts +1 -1
- package/src/input/onboarding/onboarding-wizard-external-surfaces.ts +4 -4
- package/src/input/onboarding/onboarding-wizard-network-adopt.ts +6 -6
- package/src/input/onboarding/onboarding-wizard-steps.ts +2 -2
- package/src/input/panel-integration-actions.ts +3 -3
- package/src/input/panel-mouse-geometry.ts +1 -1
- package/src/input/panel-paste-flood-guard.ts +9 -9
- package/src/input/payments-config.ts +15 -15
- package/src/input/profile-picker-modal.ts +1 -1
- package/src/input/provider-key-intake.ts +9 -9
- package/src/input/relay-settings-descriptions.ts +3 -3
- package/src/input/sandbox-exec-config.ts +9 -9
- package/src/input/search.ts +18 -18
- package/src/input/selection-modal-queue.ts +10 -10
- package/src/input/selection-modal.ts +2 -2
- package/src/input/session-picker-modal.ts +7 -7
- package/src/input/settings-modal-activation.ts +1 -1
- package/src/input/settings-modal-adjustment.ts +2 -2
- package/src/input/settings-modal-commit.ts +6 -6
- package/src/input/settings-modal-connections.ts +2 -2
- package/src/input/settings-modal-data.ts +29 -29
- package/src/input/settings-modal-mutations.ts +7 -7
- package/src/input/settings-modal-reset.ts +3 -3
- package/src/input/settings-modal-search.ts +6 -6
- package/src/input/settings-modal-secrets.ts +3 -3
- package/src/input/settings-modal-types.ts +16 -16
- package/src/input/settings-modal.ts +4 -4
- package/src/input/voice-local-settings.ts +3 -3
- package/src/input/worktree-setup-config.ts +8 -8
- package/src/main.ts +15 -15
- package/src/panels/agent-inspector-shared.ts +5 -5
- package/src/panels/base-panel.ts +3 -3
- package/src/panels/builtin/agent.ts +3 -3
- package/src/panels/builtin/development.ts +4 -4
- package/src/panels/builtin/knowledge.ts +1 -1
- package/src/panels/builtin/operations.ts +12 -12
- package/src/panels/builtin/session.ts +8 -8
- package/src/panels/builtin/shared.ts +6 -6
- package/src/panels/builtin-modals.ts +8 -8
- package/src/panels/confirm-state.ts +5 -5
- package/src/panels/cost-tracker-panel.ts +22 -22
- package/src/panels/diff-panel.ts +20 -20
- package/src/panels/diff-review-model.ts +5 -5
- package/src/panels/diff-review-panel.ts +5 -5
- package/src/panels/eval-registry.ts +2 -2
- package/src/panels/expandable-list-panel.ts +5 -5
- package/src/panels/fleet-acts.ts +20 -20
- package/src/panels/fleet-deep-link.ts +5 -5
- package/src/panels/fleet-gateway.ts +12 -12
- package/src/panels/fleet-observed-render.ts +11 -11
- package/src/panels/fleet-panel-format.ts +14 -14
- package/src/panels/fleet-panel-worktree-detail.ts +8 -8
- package/src/panels/fleet-panel.ts +34 -34
- package/src/panels/fleet-read-model.ts +49 -49
- package/src/panels/fleet-spawn.ts +15 -15
- package/src/panels/fleet-steer.ts +7 -7
- package/src/panels/fleet-stop.ts +5 -5
- package/src/panels/fleet-tabs.ts +14 -14
- package/src/panels/fleet-transcript.ts +26 -26
- package/src/panels/git-panel.ts +8 -8
- package/src/panels/hosted-session-feed.ts +13 -13
- package/src/panels/hosted-session-panel.ts +7 -7
- package/src/panels/index.ts +1 -1
- package/src/panels/local-auth-panel.ts +15 -15
- package/src/panels/memory-consolidation-gateway.ts +8 -8
- package/src/panels/modals/devices-modal.ts +8 -8
- package/src/panels/modals/hooks-modal.ts +1 -1
- package/src/panels/modals/keybindings-modal.ts +5 -5
- package/src/panels/modals/knowledge-modal.ts +2 -2
- package/src/panels/modals/local-auth-modal.ts +4 -4
- package/src/panels/modals/marketplace-modal.ts +3 -3
- package/src/panels/modals/memory-modal.ts +19 -19
- package/src/panels/modals/modal-surface-helpers.ts +1 -1
- package/src/panels/modals/modal-theme.ts +3 -3
- package/src/panels/modals/pairing-modal.ts +19 -19
- package/src/panels/modals/planning-modal.ts +4 -4
- package/src/panels/modals/plugins-modal.ts +1 -1
- package/src/panels/modals/policy-modal.ts +4 -4
- package/src/panels/modals/provider-health-modal.ts +1 -1
- package/src/panels/modals/remote-modal.ts +1 -1
- package/src/panels/modals/sandbox-modal.ts +2 -2
- package/src/panels/modals/security-modal.ts +4 -4
- package/src/panels/modals/services-modal.ts +1 -1
- package/src/panels/modals/work-plan-modal.ts +4 -4
- package/src/panels/notifications-feed.ts +6 -6
- package/src/panels/notifications-panel.ts +6 -6
- package/src/panels/panel-confirm-overlay.ts +4 -4
- package/src/panels/panel-manager.ts +13 -13
- package/src/panels/plugins-panel.ts +6 -6
- package/src/panels/polish-core.ts +2 -2
- package/src/panels/polish-tables.ts +2 -2
- package/src/panels/polish.ts +1 -1
- package/src/panels/project-planning-answer-actions.ts +3 -3
- package/src/panels/scrollable-list-panel.ts +15 -15
- package/src/panels/search-focus.ts +1 -1
- package/src/panels/session-maintenance.ts +1 -1
- package/src/panels/skills-panel.ts +3 -3
- package/src/panels/token-budget-panel.ts +11 -11
- package/src/panels/types.ts +14 -14
- package/src/panels/workstream-graph-render.ts +5 -5
- package/src/panels/wrfc-panel-format.ts +1 -1
- package/src/permissions/broker-approval-card.ts +13 -13
- package/src/permissions/hunk-selection.ts +13 -13
- package/src/permissions/prompt.ts +22 -22
- package/src/permissions/sandbox-exec-gate.ts +5 -5
- package/src/providers/provider-classification.ts +1 -1
- package/src/providers/reasoning-effort-surface.ts +12 -12
- package/src/renderer/ansi-sanitize.ts +5 -6
- package/src/renderer/autocomplete-overlay.ts +5 -5
- package/src/renderer/block-actions-overlay.ts +1 -1
- package/src/renderer/bookmark-modal.ts +1 -1
- package/src/renderer/code-block.ts +2 -2
- package/src/renderer/compaction-history-modal.ts +4 -4
- package/src/renderer/compaction-preview.ts +5 -5
- package/src/renderer/compaction-quality.ts +5 -5
- package/src/renderer/composer-fragments.ts +6 -6
- package/src/renderer/compositor.ts +9 -9
- package/src/renderer/config-modal.ts +3 -3
- package/src/renderer/context-inspector.ts +1 -1
- package/src/renderer/context-status-hint.ts +10 -10
- package/src/renderer/conversation-layout.ts +2 -2
- package/src/renderer/conversation-overlays.ts +2 -2
- package/src/renderer/conversation-surface.ts +6 -6
- package/src/renderer/diff.ts +4 -4
- package/src/renderer/fleet-tab-strip.ts +2 -2
- package/src/renderer/footer-tips.ts +4 -4
- package/src/renderer/fullscreen-primitives.ts +1 -1
- package/src/renderer/git-status.ts +5 -5
- package/src/renderer/header-line.ts +5 -5
- package/src/renderer/help-overlay.ts +5 -5
- package/src/renderer/hint-grammar.ts +2 -2
- package/src/renderer/layout.ts +1 -1
- package/src/renderer/markdown-inline.ts +3 -3
- package/src/renderer/markdown-table.ts +7 -7
- package/src/renderer/modal-factory.ts +3 -3
- package/src/renderer/model-picker-overlay.ts +2 -2
- package/src/renderer/model-workspace.ts +7 -7
- package/src/renderer/onboarding/onboarding-wizard.ts +1 -1
- package/src/renderer/overlay-box.ts +1 -1
- package/src/renderer/panel-composite.ts +5 -5
- package/src/renderer/panel-workspace-bar.ts +1 -1
- package/src/renderer/process-indicator.ts +8 -8
- package/src/renderer/profile-picker-modal.ts +3 -3
- package/src/renderer/prompt-content-width.ts +1 -1
- package/src/renderer/search-overlay.ts +3 -3
- package/src/renderer/selection-modal-overlay.ts +6 -6
- package/src/renderer/semantic-diff.ts +6 -7
- package/src/renderer/session-picker-modal.ts +24 -24
- package/src/renderer/settings-modal-connections.ts +3 -3
- package/src/renderer/settings-modal-helpers.ts +16 -16
- package/src/renderer/settings-modal.ts +10 -10
- package/src/renderer/shell-surface.ts +8 -8
- package/src/renderer/status-glyphs.ts +6 -6
- package/src/renderer/syntax-highlighter.ts +2 -3
- package/src/renderer/system-message.ts +3 -3
- package/src/renderer/tab-strip.ts +2 -2
- package/src/renderer/term-caps.ts +12 -12
- package/src/renderer/terminal-bg-probe.ts +10 -10
- package/src/renderer/terminal-escapes.ts +1 -1
- package/src/renderer/theme-mode-config.ts +3 -3
- package/src/renderer/theme.ts +34 -34
- package/src/renderer/thinking.ts +1 -1
- package/src/renderer/tool-call.ts +5 -5
- package/src/renderer/tool-result-expanded-lines.ts +3 -3
- package/src/renderer/tool-result-summary.ts +6 -6
- package/src/renderer/turn-injection.ts +9 -9
- package/src/renderer/ui-factory.ts +33 -33
- package/src/renderer/ui-primitives.ts +4 -4
- package/src/runtime/bootstrap-command-parts.ts +3 -3
- package/src/runtime/bootstrap-core.ts +21 -21
- package/src/runtime/bootstrap-hook-bridge.ts +8 -8
- package/src/runtime/bootstrap-shell.ts +9 -9
- package/src/runtime/bootstrap.ts +16 -16
- package/src/runtime/client/approval-updates.ts +3 -3
- package/src/runtime/client/build-floors.ts +7 -7
- package/src/runtime/client/daemon-attach-handshake.ts +5 -5
- package/src/runtime/client/fleet-union.ts +7 -7
- package/src/runtime/client/host-service-status.ts +2 -2
- package/src/runtime/client/hosted-exit.ts +4 -4
- package/src/runtime/client/hosted-roster.ts +4 -4
- package/src/runtime/client/hosted-session-stream.ts +5 -5
- package/src/runtime/client/hosted-sessions.ts +8 -8
- package/src/runtime/client/operator-endpoint.ts +14 -14
- package/src/runtime/client/spine-adoption.ts +8 -8
- package/src/runtime/context-accounting-source.ts +4 -4
- package/src/runtime/context.ts +2 -2
- package/src/runtime/conversation-rewind-port.ts +6 -6
- package/src/runtime/credential-composition.ts +2 -2
- package/src/runtime/daemon-attach-notices.ts +6 -6
- package/src/runtime/diagnostics/panels/index.ts +1 -1
- package/src/runtime/diagnostics/panels/ops.ts +2 -2
- package/src/runtime/disposal-wiring.ts +6 -6
- package/src/runtime/index.ts +11 -11
- package/src/runtime/install-self-check-startup.ts +4 -4
- package/src/runtime/install-self-check.ts +6 -6
- package/src/runtime/interaction-seams.ts +4 -4
- package/src/runtime/knowledge-services.ts +2 -2
- package/src/runtime/legacy-daemon-migration.ts +37 -37
- package/src/runtime/legacy-daemon-reconcile.ts +30 -30
- package/src/runtime/memory-governance-services.ts +3 -3
- package/src/runtime/memory-spine-transport.ts +3 -3
- package/src/runtime/notification-dispatch.ts +4 -4
- package/src/runtime/onboarding/apply.ts +1 -1
- package/src/runtime/onboarding/derivation.ts +1 -1
- package/src/runtime/onboarding/fast-path.ts +7 -7
- package/src/runtime/onboarding/progress.ts +2 -2
- package/src/runtime/onboarding/provider-key-capture.ts +4 -4
- package/src/runtime/onboarding/snapshot.ts +2 -2
- package/src/runtime/onboarding/state.ts +4 -4
- package/src/runtime/onboarding/types.ts +4 -4
- package/src/runtime/orchestrator-core-services.ts +5 -5
- package/src/runtime/path-shadow-startup.ts +3 -3
- package/src/runtime/perf/panel-contracts.ts +1 -1
- package/src/runtime/power-keepawake-remote.ts +6 -6
- package/src/runtime/process-lifecycle.ts +14 -14
- package/src/runtime/provider-fallback.ts +3 -3
- package/src/runtime/recovery-autosave.ts +3 -3
- package/src/runtime/recovery-decisions.ts +19 -19
- package/src/runtime/recovery-offer-wiring.ts +4 -4
- package/src/runtime/recovery-prompt.ts +20 -20
- package/src/runtime/relay-reachability-bridge.ts +3 -3
- package/src/runtime/release-artifacts.ts +1 -1
- package/src/runtime/resume-notice.ts +20 -20
- package/src/runtime/runtime-services-types.ts +20 -20
- package/src/runtime/services.ts +20 -20
- package/src/runtime/session-continuity-hints.ts +1 -1
- package/src/runtime/session-inbound-inputs.ts +12 -12
- package/src/runtime/session-resume-liveness-confirm.ts +4 -4
- package/src/runtime/session-spine-transport.ts +3 -3
- package/src/runtime/store/domains/panels.ts +2 -2
- package/src/runtime/store/domains/ui-perf.ts +2 -2
- package/src/runtime/store/index.ts +1 -1
- package/src/runtime/store/selectors/index.ts +1 -1
- package/src/runtime/store/state.ts +2 -2
- package/src/runtime/ui-services.ts +3 -3
- package/src/runtime/workspace-checkpointing.ts +3 -3
- package/src/runtime/wrfc-persistence.ts +15 -15
- package/src/scripts/process-messages.ts +1 -1
- package/src/shell/blocking-input.ts +9 -9
- package/src/shell/recovery-input-helpers.ts +2 -2
- package/src/shell/retry-affordance.ts +9 -9
- package/src/shell/ui-openers.ts +14 -14
- package/src/shell/voice-capture-shell.ts +3 -3
- package/src/tools/wrfc-agent-guard.ts +4 -4
- package/src/utils/browser.ts +2 -2
- package/src/utils/format-duration.ts +3 -3
- package/src/utils/format-number.ts +2 -2
- package/src/utils/splash-lines.ts +4 -4
- package/src/utils/terminal-width.ts +6 -6
- package/src/verification/verification-ledger.ts +25 -25
- package/src/version.ts +3 -3
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
// ---------------------------------------------------------------------------
|
|
2
|
-
// Diff review model
|
|
2
|
+
// Diff review model, pure parsing + steering-payload helpers for the
|
|
3
3
|
// comment-on-hunk review loop (see diff-review-panel.ts).
|
|
4
4
|
//
|
|
5
5
|
// The diff this operates on is the git working tree vs a base ref (HEAD),
|
|
6
6
|
// filtered to the files the current session actually edited (the SDK's
|
|
7
7
|
// SessionChangeTracker). That is the honest source: it is CURRENT working-tree
|
|
8
|
-
// content, not a turn-start snapshot
|
|
8
|
+
// content, not a turn-start snapshot, the panel labels it exactly that way.
|
|
9
9
|
// ---------------------------------------------------------------------------
|
|
10
10
|
|
|
11
11
|
/** One hunk of one file's diff, with enough structure to steer a comment at it. */
|
|
@@ -126,7 +126,7 @@ export function hunkExcerpt(hunk: ReviewHunk, maxLines = 8): string {
|
|
|
126
126
|
return [hunk.header, ...body, ...truncated].join('\n');
|
|
127
127
|
}
|
|
128
128
|
|
|
129
|
-
/** The full unified-diff hunk text (header + body) for one ReviewHunk
|
|
129
|
+
/** The full unified-diff hunk text (header + body) for one ReviewHunk, the input checkpoints.revertHunk takes. */
|
|
130
130
|
export function hunkPatchText(hunk: ReviewHunk): string {
|
|
131
131
|
return [hunk.header, ...hunk.bodyLines].join('\n');
|
|
132
132
|
}
|
|
@@ -148,10 +148,10 @@ export interface HunkRevertReceiptInput {
|
|
|
148
148
|
export function buildHunkRevertReceiptBlock(receipt: HunkRevertReceiptInput): string {
|
|
149
149
|
const plural = (n: number, word: string): string => `${n} ${word}${n === 1 ? '' : 's'}`;
|
|
150
150
|
const lines = [
|
|
151
|
-
`[Revert] Receipt
|
|
151
|
+
`[Revert] Receipt: reverted one hunk in ${receipt.path} (${receipt.hunkHeader}).`,
|
|
152
152
|
` Restored ${plural(receipt.removedLinesRestored, 'deleted line')}, removed ${plural(receipt.addedLinesRemoved, 'added line')}.`,
|
|
153
153
|
receipt.safetyCheckpointId
|
|
154
|
-
? ` Reversible: a pre-revert checkpoint was taken
|
|
154
|
+
? ` Reversible: a pre-revert checkpoint was taken; /checkpoints restore ${receipt.safetyCheckpointId} undoes it.`
|
|
155
155
|
: ' Reversible: the working tree already matched the latest checkpoint (nothing extra to snapshot).',
|
|
156
156
|
];
|
|
157
157
|
return lines.join('\n');
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// ---------------------------------------------------------------------------
|
|
2
|
-
// DiffReviewPanel
|
|
2
|
+
// DiffReviewPanel, comment-on-hunk review loop.
|
|
3
3
|
//
|
|
4
4
|
// Shows the current session's file changes per-file and hunk-boundaried, lets
|
|
5
5
|
// the user select a hunk, attach a comment, and submit that comment to the
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
//
|
|
10
10
|
// Data source (labelled honestly in the panel): `git diff <base> -- <files>`,
|
|
11
11
|
// where <files> is the set the SDK SessionChangeTracker recorded this session.
|
|
12
|
-
// That is CURRENT working-tree content vs the base ref
|
|
12
|
+
// That is CURRENT working-tree content vs the base ref, not a turn-start
|
|
13
13
|
// snapshot. The panel never presents a stale diff as current.
|
|
14
14
|
// ---------------------------------------------------------------------------
|
|
15
15
|
|
|
@@ -88,7 +88,7 @@ export class DiffReviewPanel extends BasePanel {
|
|
|
88
88
|
}
|
|
89
89
|
|
|
90
90
|
// -------------------------------------------------------------------------
|
|
91
|
-
// Public API
|
|
91
|
+
// Public API, driven by the /review command
|
|
92
92
|
// -------------------------------------------------------------------------
|
|
93
93
|
|
|
94
94
|
/** Wire the steering submit path (the /review command passes ctx.submitInput). */
|
|
@@ -108,7 +108,7 @@ export class DiffReviewPanel extends BasePanel {
|
|
|
108
108
|
this.requestRender();
|
|
109
109
|
}
|
|
110
110
|
|
|
111
|
-
/** Re-load the session diff
|
|
111
|
+
/** Re-load the session diff, used after a revert mutates the working tree. */
|
|
112
112
|
async refresh(): Promise<void> {
|
|
113
113
|
await this.loadSessionReview();
|
|
114
114
|
this.requestRender();
|
|
@@ -143,7 +143,7 @@ export class DiffReviewPanel extends BasePanel {
|
|
|
143
143
|
*/
|
|
144
144
|
async loadSessionReview(ref = 'HEAD'): Promise<void> {
|
|
145
145
|
if (!GitService.isGitRepo(this.workingDirectory)) {
|
|
146
|
-
this.status = 'Not a git repository
|
|
146
|
+
this.status = 'Not a git repository: no working-tree diff to review.';
|
|
147
147
|
this.loaded = true;
|
|
148
148
|
this.markDirty();
|
|
149
149
|
return;
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* EvalRegistry
|
|
2
|
+
* EvalRegistry, holds the latest evaluation harness run state.
|
|
3
3
|
*
|
|
4
4
|
* (the purge): extracted out of eval-panel.ts. 'eval' was
|
|
5
|
-
* DELETE-disposition (no surviving human surface
|
|
5
|
+
* DELETE-disposition (no surviving human surface, the evaluation harness
|
|
6
6
|
* is driven and reviewed via the `/eval` CLI command, input/commands/eval.ts)
|
|
7
7
|
* but this registry is still a live consumer of `/eval run` output, so it
|
|
8
8
|
* survives as a standalone read-model rather than being deleted with the
|
|
@@ -20,12 +20,12 @@ import {
|
|
|
20
20
|
* detail view for that item, and Esc/← returns to the list.
|
|
21
21
|
*
|
|
22
22
|
* Subclasses implement (in addition to the `ScrollableListPanel` contract):
|
|
23
|
-
* - `getDetailLines(item, width)
|
|
23
|
+
* - `getDetailLines(item, width)`, the detail body for the expanded item.
|
|
24
24
|
*
|
|
25
25
|
* Optionally override:
|
|
26
|
-
* - `getDetailTitle(item)
|
|
27
|
-
* - `getListHints()` / `getDetailHints()
|
|
28
|
-
* - `renderListView(width, height)
|
|
26
|
+
* - `getDetailTitle(item)`, detail-view title (defaults to `this.name`).
|
|
27
|
+
* - `getListHints()` / `getDetailHints()`, footer keyboard hints per mode.
|
|
28
|
+
* - `renderListView(width, height)`, the list-mode body (defaults to
|
|
29
29
|
* `renderList` with the list hints footer).
|
|
30
30
|
*
|
|
31
31
|
* Do NOT override `render()`; it dispatches between list and detail modes.
|
|
@@ -160,7 +160,7 @@ export abstract class ExpandableListPanel<T> extends ScrollableListPanel<T> {
|
|
|
160
160
|
return this.renderList(width, height, { hints: this.getListHints() });
|
|
161
161
|
}
|
|
162
162
|
|
|
163
|
-
/** Detail-mode body
|
|
163
|
+
/** Detail-mode body, scrollable detail lines + standardized chrome. */
|
|
164
164
|
protected renderDetailView(width: number, height: number, item: T): Line[] {
|
|
165
165
|
this.needsRender = false;
|
|
166
166
|
const palette = this.getPalette();
|
package/src/panels/fleet-acts.ts
CHANGED
|
@@ -4,22 +4,22 @@
|
|
|
4
4
|
// The Fleet panel's waiting-on-human ACTS: the flagged pick row, the flagged
|
|
5
5
|
// conflict row, and the worktree discard all act from the panel selection with
|
|
6
6
|
// no id ever typed. This controller owns that flow so fleet-panel.ts stays under
|
|
7
|
-
// the 800-line architecture cap
|
|
7
|
+
// the 800-line architecture cap, the panel delegates the trigger keys, the
|
|
8
8
|
// pick-mode input, and the pick-mode render here.
|
|
9
9
|
//
|
|
10
|
-
// • Pick
|
|
10
|
+
// • Pick , the flagged workstream row (needsAttention 'pick') opens a
|
|
11
11
|
// candidate picker (best-of-N held attempts, from fleet.attempts.list);
|
|
12
12
|
// ↑↓ chooses the winner, its diff shows in the shared DiffPanel, and Enter
|
|
13
13
|
// drives fleet.attempts.pick preview (confirm:false) -> confirm (confirm:true)
|
|
14
14
|
// through the DiffPanel's existing confirm overlay. No group/candidate id is
|
|
15
|
-
// ever typed
|
|
16
|
-
// • Conflict
|
|
15
|
+
// ever typed, the panel derives them from the node and the selection.
|
|
16
|
+
// • Conflict, the flagged work-item row (needsAttention 'conflict') runs
|
|
17
17
|
// fleet.conflicts.resolve and hands the STAMPED resolution session id to the
|
|
18
18
|
// shared one-key jump/attach affordance (the CI fix-session machinery). On
|
|
19
19
|
// resolution the SDK reclaims the tree and the row clears on the next tick.
|
|
20
|
-
// • Discard
|
|
20
|
+
// • Discard, a worktree-owning work-item row runs worktrees.discard behind a
|
|
21
21
|
// confirm and renders the honest receipt (branch KEPT, dirty state preserved
|
|
22
|
-
// as a commit)
|
|
22
|
+
// as a commit), no path retyping.
|
|
23
23
|
// ---------------------------------------------------------------------------
|
|
24
24
|
|
|
25
25
|
import type { ProcessNode } from '@pellux/goodvibes-sdk/platform/runtime/fleet';
|
|
@@ -91,7 +91,7 @@ function shortId(id: string): string {
|
|
|
91
91
|
return id.length > 10 ? `${id.slice(0, 10)}…` : id;
|
|
92
92
|
}
|
|
93
93
|
|
|
94
|
-
/** The held (pick-ready) candidates of a group, in attempt order
|
|
94
|
+
/** The held (pick-ready) candidates of a group, in attempt order, the only ones a winner can be chosen from. */
|
|
95
95
|
export function heldCandidates(group: FleetHeldMergeGroup): FleetAttemptCandidate[] {
|
|
96
96
|
return group.candidates.filter((c) => c.state === 'held-merge').slice().sort((a, b) => a.attemptIndex - b.attemptIndex);
|
|
97
97
|
}
|
|
@@ -147,7 +147,7 @@ export class FleetActs {
|
|
|
147
147
|
return this.observedSteer !== null;
|
|
148
148
|
}
|
|
149
149
|
|
|
150
|
-
/** The active observed-steer draft for `nodeId`, or null
|
|
150
|
+
/** The active observed-steer draft for `nodeId`, or null, the detail renderer shows the compose line only for the composing row. */
|
|
151
151
|
public observedSteerDraftFor(nodeId: string): string | null {
|
|
152
152
|
return this.observedSteer && this.observedSteer.nodeId === nodeId ? this.observedSteer.draft : null;
|
|
153
153
|
}
|
|
@@ -161,7 +161,7 @@ export class FleetActs {
|
|
|
161
161
|
public openObservedSteer(node: ObservedNode): boolean {
|
|
162
162
|
const channel = node.observed.steer;
|
|
163
163
|
if (channel.kind !== 'tmux') {
|
|
164
|
-
this.deps.notify(`Cannot steer this ${observedKindLabel(node.observed.externalKind)} session
|
|
164
|
+
this.deps.notify(`Cannot steer this ${observedKindLabel(node.observed.externalKind)} session: ${channel.reason}.`);
|
|
165
165
|
return true;
|
|
166
166
|
}
|
|
167
167
|
this.observedSteer = { nodeId: node.id, draft: '' };
|
|
@@ -246,7 +246,7 @@ export class FleetActs {
|
|
|
246
246
|
this.deps.notify(`Could not read the best-of-N candidates: ${summarizeError(err)}`);
|
|
247
247
|
return;
|
|
248
248
|
}
|
|
249
|
-
if (!group) { this.deps.notify('No ready best-of-N group on this workstream
|
|
249
|
+
if (!group) { this.deps.notify('No ready best-of-N group on this workstream; every attempt must settle first.'); return; }
|
|
250
250
|
this.pick = { workstreamNodeId: node.id, group, selectedHeldIndex: 0 };
|
|
251
251
|
this.showSelectedDiff();
|
|
252
252
|
this.deps.markDirty();
|
|
@@ -284,7 +284,7 @@ export class FleetActs {
|
|
|
284
284
|
|
|
285
285
|
/**
|
|
286
286
|
* Drive fleet.attempts.pick preview (confirm:false) then, behind the DiffPanel
|
|
287
|
-
* confirm overlay, confirm (confirm:true). No id is typed
|
|
287
|
+
* confirm overlay, confirm (confirm:true). No id is typed, the group id and
|
|
288
288
|
* the winner item id both come from the picker state.
|
|
289
289
|
*/
|
|
290
290
|
private async confirmSelectedPick(): Promise<void> {
|
|
@@ -307,7 +307,7 @@ export class FleetActs {
|
|
|
307
307
|
this.deps.diffSurface.armConfirm({
|
|
308
308
|
id: `${group.groupId}:${cand.itemId}`,
|
|
309
309
|
verb: 'Pick',
|
|
310
|
-
label: `Pick attempt ${cand.attemptIndex + 1} ("${cand.title}")
|
|
310
|
+
label: `Pick attempt ${cand.attemptIndex + 1} ("${cand.title}"): merge it, clean the ${losers} other worktree(s)`,
|
|
311
311
|
onConfirm: async () => {
|
|
312
312
|
try {
|
|
313
313
|
const result = await gateway.pick({ groupId: group.groupId, winnerItemId: cand.itemId, confirm: true });
|
|
@@ -315,7 +315,7 @@ export class FleetActs {
|
|
|
315
315
|
const losersCleaned = result.loserItemIds?.length ?? losers;
|
|
316
316
|
this.deps.notify(`[Fleet] Winner picked for group ${shortId(group.groupId)}: attempt ${cand.attemptIndex + 1} merged, ${losersCleaned} loser worktree(s) cleaned.`);
|
|
317
317
|
} else {
|
|
318
|
-
this.deps.notify(`[Fleet] Pick not applied for group ${shortId(group.groupId)}
|
|
318
|
+
this.deps.notify(`[Fleet] Pick not applied for group ${shortId(group.groupId)}: the daemon still requires confirmation.`);
|
|
319
319
|
}
|
|
320
320
|
} catch (err) {
|
|
321
321
|
this.deps.notify(`Pick failed: ${summarizeError(err)}`);
|
|
@@ -327,7 +327,7 @@ export class FleetActs {
|
|
|
327
327
|
onCancel: () => {
|
|
328
328
|
this.deps.diffSurface.close();
|
|
329
329
|
this.pick = null;
|
|
330
|
-
this.deps.notify('Pick cancelled
|
|
330
|
+
this.deps.notify('Pick cancelled: nothing merged, no worktree cleaned.');
|
|
331
331
|
this.deps.markDirty();
|
|
332
332
|
},
|
|
333
333
|
});
|
|
@@ -344,7 +344,7 @@ export class FleetActs {
|
|
|
344
344
|
try {
|
|
345
345
|
const result = await gateway.resolveConflict(itemId);
|
|
346
346
|
const files = result.files.length > 0 ? ` over ${result.files.length} conflicted file(s)` : '';
|
|
347
|
-
this.deps.notify(`[Fleet] Conflict resolution session started for ${shortId(itemId)}${files}
|
|
347
|
+
this.deps.notify(`[Fleet] Conflict resolution session started for ${shortId(itemId)}${files}; press j to jump to it.`);
|
|
348
348
|
// Reuse the CI fix-session machinery: hand the STAMPED session id to the
|
|
349
349
|
// shared one-key jump affordance. The kept tree is reclaimed by the SDK on
|
|
350
350
|
// a successful re-merge, and the flagged row clears on the next snapshot.
|
|
@@ -370,12 +370,12 @@ export class FleetActs {
|
|
|
370
370
|
this.deps.diffSurface.armConfirm({
|
|
371
371
|
id: `discard:${path}`,
|
|
372
372
|
verb: 'Discard',
|
|
373
|
-
label: `Discard worktree ${path}
|
|
373
|
+
label: `Discard worktree ${path}: the branch is KEPT and dirty state preserved as a commit`,
|
|
374
374
|
onConfirm: async () => {
|
|
375
375
|
try {
|
|
376
376
|
const receipt = await gateway.discardWorktree(path);
|
|
377
377
|
if (receipt.ok) {
|
|
378
|
-
this.deps.notify(`[Fleet] Worktree discarded: ${receipt.path}\n branch kept: ${receipt.branch || '(unknown)'}\n preservation commit: ${receipt.preservedCommit || '(none
|
|
378
|
+
this.deps.notify(`[Fleet] Worktree discarded: ${receipt.path}\n branch kept: ${receipt.branch || '(unknown)'}\n preservation commit: ${receipt.preservedCommit || '(none; nothing to preserve)'}\n ${receipt.detail}`);
|
|
379
379
|
} else {
|
|
380
380
|
this.deps.notify(`[Fleet] Worktree discard refused for ${receipt.path}: ${receipt.detail}`);
|
|
381
381
|
}
|
|
@@ -387,7 +387,7 @@ export class FleetActs {
|
|
|
387
387
|
},
|
|
388
388
|
onCancel: () => {
|
|
389
389
|
this.deps.diffSurface.close();
|
|
390
|
-
this.deps.notify('Discard cancelled
|
|
390
|
+
this.deps.notify('Discard cancelled: the worktree is untouched.');
|
|
391
391
|
this.deps.markDirty();
|
|
392
392
|
},
|
|
393
393
|
});
|
|
@@ -404,7 +404,7 @@ export class FleetActs {
|
|
|
404
404
|
const held = heldCandidates(group);
|
|
405
405
|
const lines: Line[] = [];
|
|
406
406
|
lines.push(buildPanelLine(width, [
|
|
407
|
-
[' Best-of-N winner pick
|
|
407
|
+
[' Best-of-N winner pick: ', P.label],
|
|
408
408
|
[group.sourceTitle, P.value],
|
|
409
409
|
]));
|
|
410
410
|
lines.push(buildPanelLine(width, [[' Choose the winner; its diff shows in the diff panel. No id is typed.', P.dim]]));
|
|
@@ -435,7 +435,7 @@ export class FleetActs {
|
|
|
435
435
|
{ keys: 'Enter', label: 'pick (confirm)' },
|
|
436
436
|
{ keys: 'Esc', label: 'cancel' },
|
|
437
437
|
], P)];
|
|
438
|
-
return buildPanelWorkspace(width, height, { title: 'Fleet
|
|
438
|
+
return buildPanelWorkspace(width, height, { title: 'Fleet: Pick winner', sections: [{ lines }], footerLines, palette: P });
|
|
439
439
|
}
|
|
440
440
|
|
|
441
441
|
private requireGateway(): FleetGateway | null {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// ---------------------------------------------------------------------------
|
|
2
|
-
// fleet-deep-link.ts
|
|
2
|
+
// fleet-deep-link.ts, item 4.
|
|
3
3
|
//
|
|
4
4
|
// PanelManager.open('fleet', pane, target) hands FleetPanel a
|
|
5
5
|
// PanelDeepLinkTarget (a process id + optional ProcessKind) so a cross-panel
|
|
@@ -10,13 +10,13 @@
|
|
|
10
10
|
//
|
|
11
11
|
// `kind`, when given, must also match: ProcessNode ids are unique per the
|
|
12
12
|
// SDK's own contract, so this is a cheap extra honesty check, not the
|
|
13
|
-
// primary lookup key. Reveal is "free" once selectedIndex is set
|
|
13
|
+
// primary lookup key. Reveal is "free" once selectedIndex is set,
|
|
14
14
|
// ScrollableListPanel's render-time scroll-window resolution keeps the
|
|
15
15
|
// selected row visible, so FleetPanel.receiveDeepLink does not need its own
|
|
16
16
|
// scroll math.
|
|
17
17
|
//
|
|
18
|
-
// A target that isn't found (pruned, killed, never existed, or a domain
|
|
19
|
-
// like the docs Tools tab's static tool definitions
|
|
18
|
+
// A target that isn't found (pruned, killed, never existed, or a domain,
|
|
19
|
+
// like the docs Tools tab's static tool definitions, that has no
|
|
20
20
|
// corresponding fleet node at all) resolves to -1; the caller degrades
|
|
21
21
|
// honestly (FleetPanel.setError('node no longer present.')) rather than
|
|
22
22
|
// silently landing on an unrelated row.
|
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
import type { PanelDeepLinkTarget } from './types.ts';
|
|
26
26
|
import type { FleetTreeRow } from './fleet-read-model.ts';
|
|
27
27
|
|
|
28
|
-
/** The row index matching `target`'s id (+ kind, if given), or -1. Pure
|
|
28
|
+
/** The row index matching `target`'s id (+ kind, if given), or -1. Pure, no I/O, no mutation. */
|
|
29
29
|
export function resolveFleetDeepLinkIndex(rows: readonly FleetTreeRow[], target: PanelDeepLinkTarget): number {
|
|
30
30
|
return rows.findIndex((row) => row.node.id === target.id && (!target.kind || row.node.kind === target.kind));
|
|
31
31
|
}
|
|
@@ -2,10 +2,10 @@
|
|
|
2
2
|
// fleet-gateway.ts
|
|
3
3
|
//
|
|
4
4
|
// The async, daemon-backed verb surface the Fleet panel's waiting-on-human acts
|
|
5
|
-
// drive
|
|
5
|
+
// drive, a best-of-N winner pick (fleet.attempts.list / fleet.attempts.pick),
|
|
6
6
|
// a merge-conflict resolution (fleet.conflicts.resolve), and a worktree discard
|
|
7
7
|
// (worktrees.discard). These are gateway verbs with no named facade on the
|
|
8
|
-
// in-process OperatorClient, so
|
|
8
|
+
// in-process OperatorClient, so, exactly like /ci and /worktree setup, they
|
|
9
9
|
// go over the generic operator invoke path (operator-rpc.ts's resolveOperatorRpc
|
|
10
10
|
// -> sdk.operator.invoke), reaching the SAME daemon the command layer does.
|
|
11
11
|
//
|
|
@@ -20,27 +20,27 @@ import type { ConfigManager } from '@pellux/goodvibes-sdk/platform/config';
|
|
|
20
20
|
import type { OperatorMethodOutput } from '@pellux/goodvibes-sdk';
|
|
21
21
|
import { resolveOperatorRpc } from '../input/commands/operator-rpc.ts';
|
|
22
22
|
|
|
23
|
-
/** fleet.attempts.list output
|
|
23
|
+
/** fleet.attempts.list output, the held-merge groups awaiting a winner pick. */
|
|
24
24
|
export type FleetAttemptsList = OperatorMethodOutput<'fleet.attempts.list'>;
|
|
25
25
|
/** One held-merge group (candidates + their diffs + optional model judgment). */
|
|
26
26
|
export type FleetHeldMergeGroup = FleetAttemptsList['groups'][number];
|
|
27
27
|
/** One candidate attempt within a group. */
|
|
28
28
|
export type FleetAttemptCandidate = FleetHeldMergeGroup['candidates'][number];
|
|
29
|
-
/** fleet.attempts.pick output
|
|
29
|
+
/** fleet.attempts.pick output, the confirm preview (applied:false) OR the applied receipt (applied:true). */
|
|
30
30
|
export type FleetPickResult = OperatorMethodOutput<'fleet.attempts.pick'>;
|
|
31
|
-
/** fleet.conflicts.resolve output
|
|
31
|
+
/** fleet.conflicts.resolve output, the seeded resolution session stamped on the kept tree. */
|
|
32
32
|
export type FleetConflictResolution = OperatorMethodOutput<'fleet.conflicts.resolve'>;
|
|
33
|
-
/** worktrees.discard output
|
|
33
|
+
/** worktrees.discard output, the honest receipt: dir removed, branch KEPT, dirty state preserved as a commit. */
|
|
34
34
|
export type FleetWorktreeDiscardReceipt = OperatorMethodOutput<'worktrees.discard'>;
|
|
35
|
-
/** fleet.graph.get output
|
|
35
|
+
/** fleet.graph.get output, the surface-facing task graph of one workstream (nodes/edges/pool). */
|
|
36
36
|
export type FleetGraphSnapshot = OperatorMethodOutput<'fleet.graph.get'>;
|
|
37
|
-
/** fleet.observed.steer output
|
|
37
|
+
/** fleet.observed.steer output, queued:true with a messageId, or queued:false with an honest reason. */
|
|
38
38
|
export type FleetObservedSteerResult = OperatorMethodOutput<'fleet.observed.steer'>;
|
|
39
39
|
|
|
40
40
|
/**
|
|
41
41
|
* The narrow async verb surface the Fleet panel's acts drive. Every method is a
|
|
42
42
|
* real daemon round-trip in production (createFleetGateway) and a mocked shape
|
|
43
|
-
* in tests. `armFixSessionAttach` is the ONLY sync member
|
|
43
|
+
* in tests. `armFixSessionAttach` is the ONLY sync member, it hands a spawned
|
|
44
44
|
* session id to the shared one-key jump affordance (the CI fix-session
|
|
45
45
|
* machinery), never a separate attach path.
|
|
46
46
|
*/
|
|
@@ -57,7 +57,7 @@ export interface FleetGateway {
|
|
|
57
57
|
resolveConflict(itemId: string): Promise<FleetConflictResolution>;
|
|
58
58
|
/** Discard a worktree directory (branch kept, dirty state preserved as a commit). */
|
|
59
59
|
discardWorktree(path: string): Promise<FleetWorktreeDiscardReceipt>;
|
|
60
|
-
/** Fetch the task graph (nodes/edges/pool) for one workstream
|
|
60
|
+
/** Fetch the task graph (nodes/edges/pool) for one workstream, the observability graph view. */
|
|
61
61
|
getGraph(workstreamId: string): Promise<FleetGraphSnapshot>;
|
|
62
62
|
/** Steer an observed foreign agent over its own channel (tmux send-keys); the daemon honestly refuses a channel-less row. */
|
|
63
63
|
steerObserved(input: { readonly id: string; readonly text: string }): Promise<FleetObservedSteerResult>;
|
|
@@ -81,7 +81,7 @@ export function workItemIdFromNodeId(nodeId: string): string | null {
|
|
|
81
81
|
/**
|
|
82
82
|
* Why the gateway could not be built (daemon disabled / no control-plane URL),
|
|
83
83
|
* surfaced verbatim so an act can print an honest "not available" line rather
|
|
84
|
-
* than guessing
|
|
84
|
+
* than guessing, mirrors OperatorRpcUnavailable.reason.
|
|
85
85
|
*/
|
|
86
86
|
export type FleetGatewayResolution =
|
|
87
87
|
| { readonly available: true; readonly gateway: FleetGateway }
|
|
@@ -95,7 +95,7 @@ export interface FleetGatewayDeps {
|
|
|
95
95
|
}
|
|
96
96
|
|
|
97
97
|
/**
|
|
98
|
-
* Build the live Fleet gateway over the generic operator invoke path
|
|
98
|
+
* Build the live Fleet gateway over the generic operator invoke path, the same
|
|
99
99
|
* daemon resolution the command layer uses (resolveOperatorRpc). Returns an
|
|
100
100
|
* honest unavailable reason when no daemon is reachable, so the panel acts can
|
|
101
101
|
* refuse cleanly instead of throwing into the render loop.
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
// ---------------------------------------------------------------------------
|
|
2
|
-
// fleet-observed-render.ts
|
|
2
|
+
// fleet-observed-render.ts, the fleet-pane rows for observed FOREIGN coding
|
|
3
3
|
// agents (kind 'observed-external').
|
|
4
4
|
//
|
|
5
|
-
// goodvibes did NOT spawn these
|
|
5
|
+
// goodvibes did NOT spawn these, they are externally-launched Claude Code /
|
|
6
6
|
// Codex / opencode sessions, detected read-only from OS signals. The row's
|
|
7
7
|
// FIRST job is visibility ("it's in claude or codex"): goodvibes never presents
|
|
8
8
|
// itself as the foreign session's cockpit. So an observed row:
|
|
@@ -44,7 +44,7 @@ function livenessGlyph(state: ProcessObserved['liveness']['state']): string {
|
|
|
44
44
|
/**
|
|
45
45
|
* One observed-external row. Deliberately NOT the standard fleet row: no cost/
|
|
46
46
|
* token/steer-badge columns (a foreign session has no LLM turn we account for),
|
|
47
|
-
* no stop marker
|
|
47
|
+
* no stop marker, just the glyph, an "observed" tag, the session label, its
|
|
48
48
|
* external kind, and its liveness state.
|
|
49
49
|
*/
|
|
50
50
|
export function renderObservedRowLine(node: ObservedNode, width: number, palette: PanelPalette = DEFAULT_PANEL_PALETTE): Line {
|
|
@@ -53,7 +53,7 @@ export function renderObservedRowLine(node: ObservedNode, width: number, palette
|
|
|
53
53
|
const glyph = livenessGlyph(observed.liveness.state);
|
|
54
54
|
const kindLabel = observedKindLabel(observed.externalKind);
|
|
55
55
|
// The label is width-budgeted so the trailing "kind · state" tell never
|
|
56
|
-
// overflows the row (the tell is the honesty signal
|
|
56
|
+
// overflows the row (the tell is the honesty signal, it must survive).
|
|
57
57
|
const tell = `${kindLabel} · ${observed.liveness.state}`;
|
|
58
58
|
const labelBudget = Math.max(4, width - tell.length - 14);
|
|
59
59
|
return buildPanelLine(width, [
|
|
@@ -68,15 +68,15 @@ export function renderObservedRowLine(node: ObservedNode, width: number, palette
|
|
|
68
68
|
|
|
69
69
|
/**
|
|
70
70
|
* The detail block for a selected observed-external row. States the foreign
|
|
71
|
-
* facts (external kind, pid, cwd, liveness meaning), then the steer affordance
|
|
71
|
+
* facts (external kind, pid, cwd, liveness meaning), then the steer affordance,
|
|
72
72
|
* a DRILL-IN: where a real channel exists it names the action; where steer.kind
|
|
73
|
-
* is 'none' it states the honest reason. It NEVER shows a stop affordance
|
|
73
|
+
* is 'none' it states the honest reason. It NEVER shows a stop affordance,
|
|
74
74
|
* observing a foreign session is not owning its lifecycle.
|
|
75
75
|
*/
|
|
76
76
|
/**
|
|
77
77
|
* Render the observed-row detail. When `steerDraft` is a string the drill-in
|
|
78
78
|
* steer composer is open on THIS row: an active input line replaces the passive
|
|
79
|
-
* "s: steer" hint. A channel-less row never shows an input (owner ruling)
|
|
79
|
+
* "s: steer" hint. A channel-less row never shows an input (owner ruling), its
|
|
80
80
|
* detail states the honest reason and stops there.
|
|
81
81
|
*/
|
|
82
82
|
export function renderObservedDetailLines(node: ObservedNode, width: number, palette: PanelPalette = DEFAULT_PANEL_PALETTE, steerDraft: string | null = null): Line[] {
|
|
@@ -96,17 +96,17 @@ export function renderObservedDetailLines(node: ObservedNode, width: number, pal
|
|
|
96
96
|
if (observed.cwd) {
|
|
97
97
|
lines.push(buildPanelLine(width, [[' cwd ', C.label], [truncateDisplay(observed.cwd, Math.max(0, width - 6)), C.dim]]));
|
|
98
98
|
}
|
|
99
|
-
// Liveness meaning, verbatim
|
|
99
|
+
// Liveness meaning, verbatim, honest about what 'quiet' can and cannot tell.
|
|
100
100
|
for (const segment of wrapText(observed.liveness.detail, Math.max(1, width - 2))) {
|
|
101
101
|
lines.push(buildPanelLine(width, [[' ', C.dim], [segment, C.dim]]));
|
|
102
102
|
}
|
|
103
103
|
// Steer is a drill-in: a real channel is named here (a message sent from this
|
|
104
|
-
// detail travels over it
|
|
104
|
+
// detail travels over it, fleet.observed.steer), and where none exists the
|
|
105
105
|
// honest reason is stated instead of a dead action. Wrapped so a long reason
|
|
106
106
|
// is never clipped at a narrow width.
|
|
107
107
|
const steerText = observed.steer.kind === 'tmux'
|
|
108
108
|
? `available via tmux pane ${observed.steer.paneId} (send-keys)`
|
|
109
|
-
: `unavailable
|
|
109
|
+
: `unavailable: ${observed.steer.reason}`;
|
|
110
110
|
const steerFg = observed.steer.kind === 'tmux' ? (C.info ?? DEFAULT_PANEL_PALETTE.info) : C.dim;
|
|
111
111
|
const steerSegments = wrapText(steerText, Math.max(1, width - 8));
|
|
112
112
|
steerSegments.forEach((segment, i) => {
|
|
@@ -123,6 +123,6 @@ export function renderObservedDetailLines(node: ObservedNode, width: number, pal
|
|
|
123
123
|
}
|
|
124
124
|
}
|
|
125
125
|
// Visibility, not a cockpit: there is no stop here, by design.
|
|
126
|
-
lines.push(buildPanelLine(width, [[' stop ', C.label], ['not offered
|
|
126
|
+
lines.push(buildPanelLine(width, [[' stop ', C.label], ['not offered: goodvibes only observes this foreign session', C.dim]]));
|
|
127
127
|
return lines;
|
|
128
128
|
}
|
|
@@ -29,7 +29,7 @@ import { renderPoolSummary, type WorkstreamGraphSnapshot } from './workstream-gr
|
|
|
29
29
|
// left over after the fixed columns + gaps; on hostile (narrow) widths the
|
|
30
30
|
// trailing columns simply clip (buildAlignedRow/buildSelectablePanelLine stop
|
|
31
31
|
// writing once a cell would overflow the row) rather than throwing or
|
|
32
|
-
// wrapping
|
|
32
|
+
// wrapping, the tree stays readable, just denser.
|
|
33
33
|
const KIND_W = 8;
|
|
34
34
|
const ELAPSED_W = 7;
|
|
35
35
|
const TOKENS_W = 7;
|
|
@@ -67,7 +67,7 @@ export function formatFleetTokens(usage: ProcessUsage | undefined): string {
|
|
|
67
67
|
return total >= 1000 ? `${(total / 1000).toFixed(1)}k` : String(total);
|
|
68
68
|
}
|
|
69
69
|
|
|
70
|
-
/** Honest cost display: never a fabricated $0.00
|
|
70
|
+
/** Honest cost display: never a fabricated $0.00, 'unpriced' when costState says so. */
|
|
71
71
|
export function formatFleetCost(costUsd: number | null | undefined, costState: ProcessCostState): string {
|
|
72
72
|
if (!hasFleetCost(costUsd, costState)) return 'unpriced';
|
|
73
73
|
const formatted = formatAgentCost(costUsd as number);
|
|
@@ -92,7 +92,7 @@ export function renderFleetRowLine(
|
|
|
92
92
|
const C = palette;
|
|
93
93
|
const node = row.node;
|
|
94
94
|
// Observed foreign agents render as their own visibility row (no cost/steer
|
|
95
|
-
// columns, no stop marker)
|
|
95
|
+
// columns, no stop marker), see fleet-observed-render.ts.
|
|
96
96
|
if (isObservedExternalNode(node)) return renderObservedRowLine(node, width, palette);
|
|
97
97
|
const disp = fleetStateDisplay(node.state, stopping, blocked);
|
|
98
98
|
const color = toneColor(disp.tone, C);
|
|
@@ -104,7 +104,7 @@ export function renderFleetRowLine(
|
|
|
104
104
|
const label = `${row.treePrefix}${node.label}${attemptBadge}`;
|
|
105
105
|
// Activity column doubles as the badge slot, in priority order:
|
|
106
106
|
// stopping > waiting-on-human text ('blocked on you' / 'needs your input')
|
|
107
|
-
// > the read-model HEADLINE (derived from task/phase identity only
|
|
107
|
+
// > the read-model HEADLINE (derived from task/phase identity only,
|
|
108
108
|
// replaced in place, never a scrolling feed) with the stall marker
|
|
109
109
|
// ('quiet Nm') appended when the node has gone observably silent.
|
|
110
110
|
// Nodes without a headline fall back to the live currentActivity text.
|
|
@@ -155,14 +155,14 @@ export function renderFleetRowLine(
|
|
|
155
155
|
* The selected-node detail block under the fleet tree. Pure (extracted from
|
|
156
156
|
* FleetPanel.renderDetail to hold that file under the 800-line architecture
|
|
157
157
|
* cap). `stopping`/`blocked` are display-only overrides the caller derives from
|
|
158
|
-
* the stop tracker + node state
|
|
158
|
+
* the stop tracker + node state, mirror the tree row exactly so the literal
|
|
159
159
|
* 'state' text never claims a past-tense outcome mid-write, and a
|
|
160
160
|
* blocked-on-user node reads 'blocked on you' here too.
|
|
161
161
|
*/
|
|
162
162
|
/**
|
|
163
163
|
* The reviewer's acceptance checklist + verdict, from the fleet node's served
|
|
164
164
|
* `review` field (ProcessReviewSummary, rides fleet.snapshot/list). Rendered
|
|
165
|
-
* only when a review has completed
|
|
165
|
+
* only when a review has completed, never an empty shell. Each item shows
|
|
166
166
|
* whether it was verified, the evidence, and (when present) how it was
|
|
167
167
|
* exercised, matching what the webui's review detail surfaces.
|
|
168
168
|
*/
|
|
@@ -180,8 +180,8 @@ export function renderReviewLines(review: ProcessReviewSummary, width: number, p
|
|
|
180
180
|
]),
|
|
181
181
|
];
|
|
182
182
|
if (review.checklist.length === 0) {
|
|
183
|
-
// Empty checklist is itself a gate failure
|
|
184
|
-
lines.push(buildPanelLine(width, [[' ', C.dim], ['(the reviewer emitted no acceptance checklist
|
|
183
|
+
// Empty checklist is itself a gate failure, say so honestly, don't hide it.
|
|
184
|
+
lines.push(buildPanelLine(width, [[' ', C.dim], ['(the reviewer emitted no acceptance checklist; a gate failure)', failTone]]));
|
|
185
185
|
return lines;
|
|
186
186
|
}
|
|
187
187
|
for (const it of review.checklist) {
|
|
@@ -209,7 +209,7 @@ export function renderReviewLines(review: ProcessReviewSummary, width: number, p
|
|
|
209
209
|
/**
|
|
210
210
|
* The task graph's edges + elastic-pool posture for a workstream row, rendered
|
|
211
211
|
* IN the fleet detail (the tree shows nodes; edges/pool used to be reachable
|
|
212
|
-
* only via /graph
|
|
212
|
+
* only via /graph, /graph still works). Fetched lazily and cached by fleet-acts;
|
|
213
213
|
* rendered only once a snapshot is in hand. An empty graph states so honestly.
|
|
214
214
|
*/
|
|
215
215
|
export function renderGraphPostureLines(graph: WorkstreamGraphSnapshot, width: number, palette: PanelPalette = DEFAULT_PANEL_PALETTE): Line[] {
|
|
@@ -244,7 +244,7 @@ export function renderFleetDetailLines(
|
|
|
244
244
|
): Line[] {
|
|
245
245
|
const C = palette;
|
|
246
246
|
// Observed foreign agents drill into their own detail (facts + steer-or-reason,
|
|
247
|
-
// never a stop)
|
|
247
|
+
// never a stop), see fleet-observed-render.ts. The drill-in steer draft (when
|
|
248
248
|
// the composer is open on this row) renders an input line in that detail.
|
|
249
249
|
if (isObservedExternalNode(node)) return renderObservedDetailLines(node, width, palette, observedSteerDraft);
|
|
250
250
|
const disp = fleetStateDisplay(node.state, stopping, blocked);
|
|
@@ -277,7 +277,7 @@ export function renderFleetDetailLines(
|
|
|
277
277
|
[formatFleetCost(node.costUsd, node.costState), C.value],
|
|
278
278
|
]);
|
|
279
279
|
// The headline (task/phase identity, replaced in place) gets its own row
|
|
280
|
-
// when present, with the stall marker appended
|
|
280
|
+
// when present, with the stall marker appended, the detail block mirrors
|
|
281
281
|
// exactly what the tree row's steady slot shows.
|
|
282
282
|
const stallMarker = fleetStallMarker(node);
|
|
283
283
|
const headlineText = node.headline
|
|
@@ -300,12 +300,12 @@ export function renderFleetDetailLines(
|
|
|
300
300
|
const line4 = buildPanelLine(width, [[' approvals ', C.label], ['—', C.dim]]);
|
|
301
301
|
const isolationDetail = node.kind === 'work-item' ? formatWorkItemIsolationDetailFromRaw(node.raw) : null;
|
|
302
302
|
// A merge-conflict row shows its STRUCTURED conflicting-path list, wrapped so a
|
|
303
|
-
// long path is fully readable
|
|
303
|
+
// long path is fully readable, never clipped (STEP 4: the conflict row acts,
|
|
304
304
|
// and the operator sees exactly which files need resolving before pressing Enter).
|
|
305
305
|
const conflictFiles = node.kind === 'work-item' ? conflictFilesFromRaw(node.raw) : null;
|
|
306
306
|
const conflictLines: Line[] = conflictFiles
|
|
307
307
|
? [
|
|
308
|
-
buildPanelLine(width, [[' conflicts ', C.label], [`${conflictFiles.length} file(s)
|
|
308
|
+
buildPanelLine(width, [[' conflicts ', C.label], [`${conflictFiles.length} file(s): press Enter to resolve`, C.warn ?? DEFAULT_PANEL_PALETTE.warn]]),
|
|
309
309
|
// Prefix is 3 spaces of indent + a 2-col bullet/continuation marker, so
|
|
310
310
|
// wrap the path at width-5 to keep the composed line within `width`
|
|
311
311
|
// (a hard-wrapped long path is fully readable, never clipped).
|
|
@@ -317,7 +317,7 @@ export function renderFleetDetailLines(
|
|
|
317
317
|
]
|
|
318
318
|
: [];
|
|
319
319
|
// The reviewer's acceptance checklist + verdict, when a review has completed
|
|
320
|
-
// (served on node.review; absent before any review
|
|
320
|
+
// (served on node.review; absent before any review, never an empty shell).
|
|
321
321
|
const reviewLines = node.review ? renderReviewLines(node.review, width, C) : [];
|
|
322
322
|
// The task graph's edges/pool posture for a workstream row (fetched + cached
|
|
323
323
|
// by fleet-acts); rendered in-panel under the chain, /graph still available.
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// ---------------------------------------------------------------------------
|
|
2
2
|
// fleet-panel-worktree-detail.ts
|
|
3
3
|
//
|
|
4
|
-
// NET-NEW helper (not moved code
|
|
4
|
+
// NET-NEW helper (not moved code, it was authored here because fleet-panel.ts
|
|
5
5
|
// sits at the architecture check's 800-line cap, scripts/check-architecture.ts):
|
|
6
6
|
// a single pure helper for the Fleet panel's per-work-item detail view, so a
|
|
7
7
|
// new panel feature doesn't have to fight that cap inline.
|
|
@@ -14,7 +14,7 @@ import type { WorkItem } from '@pellux/goodvibes-sdk/platform/orchestration';
|
|
|
14
14
|
* node detail (fleet-panel.ts renderDetail), or null for a shared-isolation
|
|
15
15
|
* item (or any node that isn't a work item). Inferred from the item's OWN
|
|
16
16
|
* fields (worktreePath/worktreeBranch/mergeState) rather than a
|
|
17
|
-
* workstream-level flag
|
|
17
|
+
* workstream-level flag, adaptWorkItem's ProcessNode.raw carries
|
|
18
18
|
* `{ item, workstreamId }`, not the full Workstream, and these fields are
|
|
19
19
|
* set ONLY when the owning workstream's isolation is 'worktree'
|
|
20
20
|
* (engine.ts/worktree-isolation.ts, SDK), so they are a reliable
|
|
@@ -26,22 +26,22 @@ export function formatWorkItemIsolationDetail(item: WorkItem): string | null {
|
|
|
26
26
|
switch (item.mergeState) {
|
|
27
27
|
case 'merged':
|
|
28
28
|
// Papercut sweep item 3: worktree-isolation items whose integration had
|
|
29
|
-
// no changes to merge (mergeState 'merged', mergeHash undefined
|
|
29
|
+
// no changes to merge (mergeState 'merged', mergeHash undefined, a
|
|
30
30
|
// documented cosmetic case, not an error) used to read as a bare/awkward
|
|
31
31
|
// "merged (nothing to merge)". "merged (no changes)" says the same thing
|
|
32
32
|
// in the same shape as the other merge-state phrases below.
|
|
33
|
-
return item.mergeHash ? `worktree
|
|
33
|
+
return item.mergeHash ? `worktree: merged ${item.mergeHash.slice(0, 12)}` : 'worktree: merged (no changes)';
|
|
34
34
|
case 'conflict':
|
|
35
|
-
return 'worktree
|
|
35
|
+
return 'worktree: merge-conflict (kept for inspection)';
|
|
36
36
|
case 'pending':
|
|
37
|
-
return 'worktree
|
|
37
|
+
return 'worktree: merge pending';
|
|
38
38
|
case 'n-a':
|
|
39
39
|
default:
|
|
40
|
-
return item.worktreeKept ? 'worktree
|
|
40
|
+
return item.worktreeKept ? 'worktree: kept' : 'worktree: not yet integrated';
|
|
41
41
|
}
|
|
42
42
|
}
|
|
43
43
|
|
|
44
|
-
/** `node.raw` for a 'work-item' ProcessNode is `{ item, workstreamId }` (unknown to the panel's own types)
|
|
44
|
+
/** `node.raw` for a 'work-item' ProcessNode is `{ item, workstreamId }` (unknown to the panel's own types), this narrows without fleet-panel.ts needing its own WorkItem import at the call site. Non-work-item nodes (raw is undefined/some other shape) fall through to null. */
|
|
45
45
|
export function formatWorkItemIsolationDetailFromRaw(raw: unknown): string | null {
|
|
46
46
|
const item = (raw as { item?: WorkItem } | undefined)?.item;
|
|
47
47
|
return item ? formatWorkItemIsolationDetail(item) : null;
|