@pellux/goodvibes-tui 1.16.1 → 1.17.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +89 -9
- package/README.md +1 -1
- package/docs/foundation-artifacts/operator-contract.json +1091 -271
- package/package.json +7 -7
- package/src/cli/completions/generate.ts +5 -4
- package/src/cli/config-overrides.ts +51 -11
- package/src/cli/doctor.ts +4 -5
- package/src/cli/entrypoint.ts +13 -8
- package/src/cli/help.ts +7 -6
- package/src/cli/launch-auto-update.ts +214 -0
- package/src/cli/parser.ts +5 -0
- package/src/cli/status.ts +13 -0
- package/src/cli/surface-command.ts +5 -5
- package/src/cli/tui-startup.ts +60 -97
- package/src/cli/types.ts +2 -0
- package/src/config/index.ts +1 -1
- package/src/config/tui-extension-settings.ts +33 -0
- package/src/core/composer-state.ts +1 -1
- package/src/core/conversation-line-cache.ts +3 -3
- package/src/core/conversation-rendering.ts +4 -4
- package/src/core/conversation.ts +2 -2
- package/src/core/long-task-notifier.ts +7 -7
- package/src/core/stream-event-wiring.ts +8 -6
- package/src/core/system-message-noise.ts +1 -1
- package/src/core/system-message-router.ts +6 -6
- package/src/core/turn-cancellation.ts +1 -1
- package/src/core/turn-event-wiring.ts +8 -8
- package/src/daemon/cli.ts +20 -5
- package/src/daemon/lifecycle.ts +41 -0
- package/src/daemon/service-commands.ts +5 -5
- package/src/export/cost-utils.ts +128 -6
- package/src/input/autocomplete.ts +2 -2
- package/src/input/command-registry.ts +34 -9
- package/src/input/commands/ci-runtime.ts +5 -1
- package/src/input/commands/codebase-runtime.ts +6 -6
- package/src/input/commands/cost-runtime.ts +1 -1
- package/src/input/commands/health-runtime.ts +2 -2
- package/src/input/commands/intelligence-runtime.ts +1 -1
- package/src/input/commands/local-auth-runtime.ts +1 -1
- package/src/input/commands/local-provider-runtime.ts +49 -10
- package/src/input/commands/local-runtime.ts +53 -5
- package/src/input/commands/memory.ts +2 -2
- package/src/input/commands/operator-panel-runtime.ts +5 -5
- package/src/input/commands/permissions-provenance.ts +1 -1
- package/src/input/commands/permissions-runtime.ts +99 -8
- package/src/input/commands/planning-runtime.ts +3 -3
- package/src/input/commands/platform-sandbox-runtime.ts +1 -1
- package/src/input/commands/provider-accounts-runtime.ts +71 -6
- package/src/input/commands/provider.ts +7 -5
- package/src/input/commands/recall-files-git.ts +13 -51
- package/src/input/commands/recall-files.ts +8 -11
- package/src/input/commands/recall-review.ts +2 -2
- package/src/input/commands/relay-runtime.ts +1 -1
- package/src/input/commands/remote-runtime.ts +1 -1
- package/src/input/commands/runtime-services.ts +4 -4
- package/src/input/commands/schedule-runtime.ts +14 -7
- package/src/input/commands/services-runtime.ts +1 -1
- package/src/input/commands/session-workflow.ts +2 -2
- package/src/input/commands/settings-sync-runtime.ts +2 -2
- package/src/input/commands/share-runtime.ts +1 -1
- package/src/input/commands/shell-core.ts +46 -68
- package/src/input/commands/subscription-runtime.ts +1 -1
- package/src/input/commands/update-runtime.ts +153 -96
- package/src/input/commands/work-plan-runtime.ts +1 -1
- package/src/input/commands/workstream-runtime.ts +2 -2
- package/src/input/commands.ts +0 -2
- package/src/input/config-modal-types.ts +3 -3
- package/src/input/config-modal.ts +10 -11
- package/src/input/feature-unit-layout.ts +93 -82
- package/src/input/feed-context-factory.ts +2 -2
- package/src/input/handler-command-route.ts +2 -2
- package/src/input/handler-content-actions.ts +1 -1
- package/src/input/handler-feed-routes.ts +7 -7
- package/src/input/handler-feed.ts +4 -4
- package/src/input/handler-interactions.ts +3 -2
- package/src/input/handler-modal-routes.ts +12 -4
- package/src/input/handler-modal-token-routes.ts +1 -1
- package/src/input/handler-onboarding-daemon-adopt.ts +2 -2
- package/src/input/handler-onboarding.ts +3 -2
- package/src/input/handler-picker-routes.ts +26 -13
- package/src/input/handler-shortcuts.ts +6 -6
- package/src/input/handler.ts +2 -2
- package/src/input/keybindings.ts +2 -2
- package/src/input/onboarding/onboarding-feature-units.ts +34 -27
- package/src/input/onboarding/onboarding-provider-key-fields.ts +112 -0
- package/src/input/onboarding/onboarding-wizard-apply.ts +22 -21
- package/src/input/onboarding/onboarding-wizard-external-surfaces.ts +17 -0
- package/src/input/onboarding/onboarding-wizard-network-adopt.ts +2 -2
- package/src/input/onboarding/onboarding-wizard-steps.ts +30 -42
- package/src/input/panel-integration-actions.ts +2 -2
- package/src/input/panel-mouse-geometry.ts +1 -1
- package/src/input/panel-paste-flood-guard.ts +1 -1
- package/src/input/provider-key-intake.ts +167 -0
- package/src/input/sandbox-exec-config.ts +2 -2
- package/src/input/selection-modal.ts +1 -1
- package/src/input/settings-modal-activation.ts +5 -0
- package/src/input/settings-modal-adjustment.ts +4 -31
- package/src/input/settings-modal-data.ts +22 -21
- package/src/input/settings-modal-mutations.ts +12 -68
- package/src/input/settings-modal-types.ts +23 -16
- package/src/input/settings-modal.ts +74 -55
- package/src/main.ts +48 -48
- package/src/panels/agent-inspector-shared.ts +4 -4
- package/src/panels/base-panel.ts +2 -2
- package/src/panels/builtin/agent.ts +4 -4
- package/src/panels/builtin/development.ts +4 -4
- package/src/panels/builtin/knowledge.ts +1 -1
- package/src/panels/builtin/operations.ts +29 -10
- package/src/panels/builtin/session.ts +5 -5
- package/src/panels/builtin/shared.ts +4 -4
- package/src/panels/builtin-modals.ts +10 -10
- package/src/panels/builtin-panels.ts +1 -1
- package/src/panels/cost-tracker-panel.ts +124 -7
- package/src/panels/diff-panel.ts +5 -5
- package/src/panels/eval-registry.ts +2 -2
- package/src/panels/fleet-deep-link.ts +1 -1
- package/src/panels/fleet-panel-format.ts +44 -8
- package/src/panels/fleet-panel.ts +16 -16
- package/src/panels/fleet-read-model.ts +52 -13
- package/src/panels/fleet-steer.ts +2 -2
- package/src/panels/fleet-stop.ts +3 -3
- package/src/panels/fleet-tabs.ts +13 -14
- package/src/panels/fleet-transcript.ts +5 -5
- package/src/panels/git-panel.ts +2 -2
- package/src/panels/index.ts +4 -1
- package/src/panels/local-auth-panel.ts +1 -1
- package/src/panels/modals/hooks-modal.ts +1 -1
- package/src/panels/modals/keybindings-modal.ts +2 -2
- package/src/panels/modals/knowledge-modal.ts +1 -1
- package/src/panels/modals/marketplace-modal.ts +4 -4
- package/src/panels/modals/memory-modal.ts +4 -4
- package/src/panels/modals/modal-surface-helpers.ts +1 -1
- package/src/panels/modals/modal-theme.ts +1 -1
- package/src/panels/modals/pairing-modal.ts +2 -2
- package/src/panels/modals/planning-modal.ts +2 -2
- package/src/panels/modals/plugins-modal.ts +1 -1
- package/src/panels/modals/policy-modal.ts +1 -1
- package/src/panels/modals/security-modal.ts +1 -1
- package/src/panels/modals/skills-modal.ts +1 -1
- package/src/panels/modals/work-plan-modal.ts +3 -3
- package/src/panels/notifications-feed.ts +127 -0
- package/src/panels/notifications-panel.ts +160 -0
- package/src/panels/panel-manager.ts +9 -9
- package/src/panels/plugins-panel.ts +4 -4
- package/src/panels/project-planning-answer-actions.ts +1 -1
- package/src/panels/provider-health-routes.ts +2 -2
- package/src/panels/skills-panel.ts +4 -5
- package/src/panels/token-budget-panel.ts +11 -3
- package/src/panels/types.ts +5 -5
- package/src/permissions/broker-approval-card.ts +201 -0
- package/src/permissions/hunk-selection.ts +28 -4
- package/src/permissions/prompt.ts +244 -29
- package/src/renderer/autocomplete-overlay.ts +107 -54
- package/src/renderer/code-block.ts +2 -2
- package/src/renderer/compaction-history-modal.ts +1 -1
- package/src/renderer/compaction-preview.ts +1 -1
- package/src/renderer/compaction-quality.ts +1 -1
- package/src/renderer/compositor.ts +2 -2
- package/src/renderer/config-modal.ts +1 -1
- package/src/renderer/conversation-overlays.ts +1 -1
- package/src/renderer/footer-tips.ts +3 -3
- package/src/renderer/hint-grammar.ts +1 -1
- package/src/renderer/modal-factory.ts +1 -1
- package/src/renderer/model-picker-overlay.ts +1 -1
- package/src/renderer/model-workspace.ts +7 -2
- package/src/renderer/panel-composite.ts +2 -2
- package/src/renderer/panel-workspace-bar.ts +1 -1
- package/src/renderer/process-indicator.ts +1 -1
- package/src/renderer/selection-modal-overlay.ts +1 -1
- package/src/renderer/session-picker-modal.ts +2 -2
- package/src/renderer/settings-modal-helpers.ts +14 -5
- package/src/renderer/settings-modal.ts +127 -39
- package/src/renderer/shell-surface.ts +3 -0
- package/src/renderer/terminal-bg-probe.ts +1 -1
- package/src/renderer/theme.ts +3 -3
- package/src/renderer/tool-call.ts +1 -1
- package/src/renderer/tool-result-summary.ts +1 -1
- package/src/renderer/turn-injection.ts +7 -6
- package/src/renderer/ui-factory.ts +6 -1
- package/src/runtime/bootstrap-command-context.ts +4 -1
- package/src/runtime/bootstrap-command-parts.ts +6 -4
- package/src/runtime/bootstrap-core.ts +35 -35
- package/src/runtime/bootstrap-hook-bridge.ts +1 -1
- package/src/runtime/bootstrap-shell.ts +6 -5
- package/src/runtime/bootstrap.ts +34 -32
- package/src/runtime/channel-composition.ts +46 -0
- package/src/runtime/code-index-services.ts +3 -4
- package/src/runtime/daemon-attach-notices.ts +120 -0
- package/src/runtime/durability-services.ts +55 -0
- package/src/runtime/embedded-service-factories.ts +40 -0
- package/src/runtime/feature-settings.ts +147 -0
- package/src/runtime/index.ts +4 -21
- package/src/runtime/legacy-daemon-migration.ts +29 -19
- package/src/runtime/memory-fold.ts +1 -1
- package/src/runtime/notification-dispatch.ts +110 -0
- package/src/runtime/onboarding/apply.ts +3 -29
- package/src/runtime/onboarding/fast-path.ts +39 -6
- package/src/runtime/onboarding/provider-key-capture.ts +126 -0
- package/src/runtime/onboarding/snapshot.ts +0 -1
- package/src/runtime/onboarding/types.ts +9 -3
- package/src/runtime/provider-fallback.ts +55 -0
- package/src/runtime/provider-optimizer-wiring.ts +55 -0
- package/src/runtime/release-artifacts.ts +19 -103
- package/src/runtime/resume-notice.ts +1 -1
- package/src/runtime/services.ts +81 -82
- package/src/runtime/session-continuity-hints.ts +38 -0
- package/src/runtime/surface-feature-flags.ts +40 -9
- package/src/runtime/terminal-output-guard.ts +2 -2
- package/src/runtime/trust/workspace-registration.ts +10 -2
- package/src/runtime/trust/workspace-trust.ts +58 -31
- package/src/runtime/ui-services.ts +3 -1
- package/src/runtime/update-check.ts +15 -69
- package/src/runtime/workstream-services.ts +2 -2
- package/src/shell/blocking-input.ts +102 -6
- package/src/shell/ui-openers.ts +38 -6
- package/src/utils/format-duration.ts +1 -1
- package/src/verification/verification-ledger.ts +21 -4
- package/src/version.ts +1 -1
- package/src/input/commands/flags-runtime.ts +0 -328
|
@@ -11,7 +11,7 @@ import type { ProcessCostState, ProcessNode, ProcessUsage } from '@pellux/goodvi
|
|
|
11
11
|
import type { Line } from '../types/grid.ts';
|
|
12
12
|
import { formatAgentCost } from './agent-inspector-shared.ts';
|
|
13
13
|
import { buildPanelLine, DEFAULT_PANEL_PALETTE, type ColumnSpec, type PanelPalette } from './polish.ts';
|
|
14
|
-
import { fleetUsageTokens, hasFleetCost, type FleetStateTone } from './fleet-read-model.ts';
|
|
14
|
+
import { fleetAttentionText, fleetNodeAttention, fleetStallMarker, fleetUsageTokens, hasFleetCost, type FleetStateTone } from './fleet-read-model.ts';
|
|
15
15
|
import { fleetStateDisplay } from './fleet-stop.ts';
|
|
16
16
|
import { formatElapsed } from '../utils/format-elapsed.ts';
|
|
17
17
|
import { truncateDisplay } from '../utils/terminal-width.ts';
|
|
@@ -96,16 +96,39 @@ export function renderFleetRowLine(
|
|
|
96
96
|
? ` [attempt ${node.attemptGroup.index + 1}/${node.attemptGroup.total}${node.attemptGroup.held ? ', held' : ''}]`
|
|
97
97
|
: '';
|
|
98
98
|
const label = `${row.treePrefix}${node.label}${attemptBadge}`;
|
|
99
|
-
// Activity column doubles as the badge slot
|
|
100
|
-
//
|
|
99
|
+
// Activity column doubles as the badge slot, in priority order:
|
|
100
|
+
// stopping > waiting-on-human text ('blocked on you' / 'needs your input')
|
|
101
|
+
// > the read-model HEADLINE (derived from task/phase identity only —
|
|
102
|
+
// replaced in place, never a scrolling feed) with the stall marker
|
|
103
|
+
// ('quiet Nm') appended when the node has gone observably silent.
|
|
104
|
+
// Nodes without a headline fall back to the live currentActivity text.
|
|
105
|
+
const attention = fleetNodeAttention(node);
|
|
106
|
+
const stallMarker = fleetStallMarker(node);
|
|
107
|
+
const steadyText = node.headline?.text ?? node.currentActivity?.text ?? '';
|
|
108
|
+
// The stall marker must survive the 20-char activity cell: budget the
|
|
109
|
+
// headline around it (the marker is the honesty signal; a stalled node must
|
|
110
|
+
// never render as if it were healthily mid-headline).
|
|
111
|
+
const steadyWithStall = stallMarker
|
|
112
|
+
? (steadyText
|
|
113
|
+
? `${truncateDisplay(steadyText, Math.max(1, ACTIVITY_W - stallMarker.length - 3))} · ${stallMarker}`
|
|
114
|
+
: stallMarker)
|
|
115
|
+
: steadyText;
|
|
101
116
|
const activity = stopping
|
|
102
117
|
? disp.label
|
|
103
118
|
: blocked
|
|
104
|
-
?
|
|
119
|
+
? fleetAttentionText(attention ?? { reason: 'approval' })
|
|
105
120
|
: badge
|
|
106
|
-
? `${steerBadgeGlyph(badge.status)} ${
|
|
107
|
-
:
|
|
108
|
-
const activityColor = stopping
|
|
121
|
+
? `${steerBadgeGlyph(badge.status)} ${steadyWithStall}`.trimEnd()
|
|
122
|
+
: steadyWithStall;
|
|
123
|
+
const activityColor = stopping
|
|
124
|
+
? color
|
|
125
|
+
: blocked
|
|
126
|
+
? color
|
|
127
|
+
: badge
|
|
128
|
+
? steerBadgeTone(badge.status, C)
|
|
129
|
+
: stallMarker
|
|
130
|
+
? (C.warn ?? DEFAULT_PANEL_PALETTE.warn)
|
|
131
|
+
: C.dim;
|
|
109
132
|
|
|
110
133
|
return buildAlignedRow(
|
|
111
134
|
width,
|
|
@@ -160,6 +183,19 @@ export function renderFleetDetailLines(
|
|
|
160
183
|
[' cost ', C.label],
|
|
161
184
|
[formatFleetCost(node.costUsd, node.costState), C.value],
|
|
162
185
|
]);
|
|
186
|
+
// The headline (task/phase identity, replaced in place) gets its own row
|
|
187
|
+
// when present, with the stall marker appended — the detail block mirrors
|
|
188
|
+
// exactly what the tree row's steady slot shows.
|
|
189
|
+
const stallMarker = fleetStallMarker(node);
|
|
190
|
+
const headlineText = node.headline
|
|
191
|
+
? `${node.headline.text}${stallMarker ? ` · ${stallMarker}` : ''}`
|
|
192
|
+
: stallMarker;
|
|
193
|
+
const headlineLine = headlineText
|
|
194
|
+
? [buildPanelLine(width, [
|
|
195
|
+
[' headline ', C.label],
|
|
196
|
+
[truncateDisplay(headlineText, Math.max(0, width - 11)), stallMarker ? (C.warn ?? DEFAULT_PANEL_PALETTE.warn) : C.value],
|
|
197
|
+
])]
|
|
198
|
+
: [];
|
|
163
199
|
const activityText = node.currentActivity
|
|
164
200
|
? `${node.currentActivity.kind}: ${node.currentActivity.text}`
|
|
165
201
|
: '(no recent activity)';
|
|
@@ -170,5 +206,5 @@ export function renderFleetDetailLines(
|
|
|
170
206
|
// Approval history attaches here once session tabs land.
|
|
171
207
|
const line4 = buildPanelLine(width, [[' approvals ', C.label], ['—', C.dim]]);
|
|
172
208
|
const isolationDetail = node.kind === 'work-item' ? formatWorkItemIsolationDetailFromRaw(node.raw) : null;
|
|
173
|
-
return [line1, line2, line3, line4, ...(isolationDetail ? [buildPanelLine(width, [[' isolation ', C.label], [isolationDetail, C.dim]])] : [])];
|
|
209
|
+
return [line1, line2, ...headlineLine, line3, line4, ...(isolationDetail ? [buildPanelLine(width, [[' isolation ', C.label], [isolationDetail, C.dim]])] : [])];
|
|
174
210
|
}
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
// ---------------------------------------------------------------------------
|
|
2
2
|
// fleet-panel.ts
|
|
3
3
|
//
|
|
4
|
-
//
|
|
4
|
+
// — FleetPanel: the live unified observability tree. Renders the
|
|
5
5
|
// registered process fleet (agents incl. WRFC roles, WRFC chains/subtasks,
|
|
6
6
|
// workflow-tool FSMs, watchers, background processes) as a depth-first tree
|
|
7
|
-
// with a selected-row detail region. Session tabs (
|
|
7
|
+
// with a selected-row detail region. Session tabs (Part C): Enter on an
|
|
8
8
|
// attachable node (agent/wrfc-chain) opens a tab with that process's
|
|
9
9
|
// transcript/member summary — see fleet-tabs.ts + fleet-transcript.ts. This
|
|
10
10
|
// file owns input dispatch and layout; fleet-deep-link.ts owns the
|
|
11
|
-
// cross-panel jump target (
|
|
11
|
+
// cross-panel jump target (item 4).
|
|
12
12
|
// ---------------------------------------------------------------------------
|
|
13
13
|
|
|
14
14
|
import { readFile } from 'node:fs/promises';
|
|
@@ -21,7 +21,7 @@ import { PanelConfirmOverlay } from './panel-confirm-overlay.ts';
|
|
|
21
21
|
import { isPanelSearchBackspace, isPanelSearchCancel, isPanelSearchCommit, isPanelSearchPrintable } from './search-focus.ts';
|
|
22
22
|
import { buildKeyboardHints, buildPanelWorkspace, buildSearchInputLine, DEFAULT_PANEL_PALETTE, getPanelWorkspaceContentBudget, type PanelPalette } from './polish.ts';
|
|
23
23
|
import {
|
|
24
|
-
|
|
24
|
+
isBlockedOnUserNode,
|
|
25
25
|
isRunningProcessState,
|
|
26
26
|
isTerminalProcessState,
|
|
27
27
|
type FleetReadModel,
|
|
@@ -105,7 +105,7 @@ export class FleetPanel extends ScrollableListPanel<FleetTreeRow> {
|
|
|
105
105
|
*/
|
|
106
106
|
private selectedNodeId: string | null = null;
|
|
107
107
|
|
|
108
|
-
/** d1
|
|
108
|
+
/** d1: the "stopping…" write-window overlay tracker (see fleet-stop.ts). */
|
|
109
109
|
private readonly stopTracker = new FleetStopTracker();
|
|
110
110
|
|
|
111
111
|
public constructor(
|
|
@@ -114,7 +114,7 @@ export class FleetPanel extends ScrollableListPanel<FleetTreeRow> {
|
|
|
114
114
|
private readonly configManager: ConfigManager | null = null,
|
|
115
115
|
) {
|
|
116
116
|
super('fleet', 'Fleet', '⊟', 'runtime-ops');
|
|
117
|
-
this.showSelectionGutter = true; //
|
|
117
|
+
this.showSelectionGutter = true; // visible ▸ focus indicator
|
|
118
118
|
this.actions = { ...NOOP_ACTIONS, ...actions };
|
|
119
119
|
this.confirmOverlay = new PanelConfirmOverlay(() => this.markDirty());
|
|
120
120
|
this.unsub = readModel.subscribe(() => {
|
|
@@ -232,10 +232,10 @@ export class FleetPanel extends ScrollableListPanel<FleetTreeRow> {
|
|
|
232
232
|
}
|
|
233
233
|
|
|
234
234
|
/**
|
|
235
|
-
*
|
|
235
|
+
* (cross-restart honesty): a restart always starts from an empty
|
|
236
236
|
* `AgentManager` Map (no bridge from a prior TUI/daemon registry), so a
|
|
237
237
|
* completed process from a previous session can never reappear here — a
|
|
238
|
-
* real, permanent limitation (
|
|
238
|
+
* real, permanent limitation (brief point 5), documented rather than
|
|
239
239
|
* silently implying "nothing has ever run" or that a restart brings it back.
|
|
240
240
|
*/
|
|
241
241
|
protected override getEmptyStateActions(): Array<{ command: string; summary: string }> {
|
|
@@ -303,7 +303,7 @@ export class FleetPanel extends ScrollableListPanel<FleetTreeRow> {
|
|
|
303
303
|
return this.getItems().find((row) => row.node.id === nodeId)?.node ?? null;
|
|
304
304
|
}
|
|
305
305
|
|
|
306
|
-
/**
|
|
306
|
+
/** item 4 (see fleet-deep-link.ts). */
|
|
307
307
|
public receiveDeepLink(target: { readonly id: string; readonly kind?: string }): void {
|
|
308
308
|
const idx = resolveFleetDeepLinkIndex(this.getItems(), target);
|
|
309
309
|
if (idx < 0) { this.setError('node no longer present.'); return; }
|
|
@@ -391,7 +391,7 @@ export class FleetPanel extends ScrollableListPanel<FleetTreeRow> {
|
|
|
391
391
|
// owns EVERY key (same full-priority-while-composing contract as
|
|
392
392
|
// git-panel.ts's commitMessage entry) until Enter (submit) or Esc
|
|
393
393
|
// (cancel) — a burst/paste and single hotkeys like 'j'/'s'/'[' all land
|
|
394
|
-
// in the draft, never as tree/tab navigation (
|
|
394
|
+
// in the draft, never as tree/tab navigation (focus rule).
|
|
395
395
|
const composingTab = activeFleetTab(this.tabsState);
|
|
396
396
|
if (composingTab && composingTab.steerDraft !== null) {
|
|
397
397
|
return this.handleSteerInput(composingTab, key);
|
|
@@ -491,7 +491,7 @@ export class FleetPanel extends ScrollableListPanel<FleetTreeRow> {
|
|
|
491
491
|
id: node.id,
|
|
492
492
|
label: `${node.kind} ${shortId}${suffix}`,
|
|
493
493
|
verb: 'Kill',
|
|
494
|
-
// d1
|
|
494
|
+
// d1: mark 'stopping…' only once the kill is confirmed, not while
|
|
495
495
|
// the confirm overlay is merely armed.
|
|
496
496
|
onConfirm: () => {
|
|
497
497
|
this.actions.kill(node.id, { cascade: true });
|
|
@@ -501,7 +501,7 @@ export class FleetPanel extends ScrollableListPanel<FleetTreeRow> {
|
|
|
501
501
|
return true;
|
|
502
502
|
}
|
|
503
503
|
|
|
504
|
-
// d2
|
|
504
|
+
// d2: 'p' toggles pause<->resume by the selected node's state (see
|
|
505
505
|
// toggleFleetPause in fleet-stop.ts).
|
|
506
506
|
if (key === 'p') {
|
|
507
507
|
if (!selected) return false;
|
|
@@ -621,7 +621,7 @@ export class FleetPanel extends ScrollableListPanel<FleetTreeRow> {
|
|
|
621
621
|
|
|
622
622
|
/**
|
|
623
623
|
* Submit a composed steer message. Returns whether it was queued so the caller
|
|
624
|
-
* clears the draft only on a confirmed send (WO
|
|
624
|
+
* clears the draft only on a confirmed send (WO item 4): a refusal (target
|
|
625
625
|
* went idle while composing) PRESERVES the typed text and suggests which agents
|
|
626
626
|
* ARE steerable, instead of silently discarding the draft. On a successful queue
|
|
627
627
|
* the ⧗ badge line is the immediate honest acknowledgment.
|
|
@@ -644,7 +644,7 @@ export class FleetPanel extends ScrollableListPanel<FleetTreeRow> {
|
|
|
644
644
|
// Display-only overrides (stopping wins, then blocked-on-user) are derived
|
|
645
645
|
// here from panel state and passed into the pure row renderer.
|
|
646
646
|
const stopping = this.stopTracker.isStopping(row.node.id);
|
|
647
|
-
const blocked = !stopping &&
|
|
647
|
+
const blocked = !stopping && isBlockedOnUserNode(row.node);
|
|
648
648
|
return renderFleetRowLine(row, width, stopping, blocked, this.steerBadgeForNode(row.node.id), C);
|
|
649
649
|
}
|
|
650
650
|
|
|
@@ -658,7 +658,7 @@ export class FleetPanel extends ScrollableListPanel<FleetTreeRow> {
|
|
|
658
658
|
// Mirror the tree row's display-only overrides (stopping wins, then blocked)
|
|
659
659
|
// so the detail's literal state text never contradicts the row glyph.
|
|
660
660
|
const stopping = this.stopTracker.isStopping(node.id);
|
|
661
|
-
const blocked = !stopping &&
|
|
661
|
+
const blocked = !stopping && isBlockedOnUserNode(node);
|
|
662
662
|
return renderFleetDetailLines(node, width, stopping, blocked, C);
|
|
663
663
|
}
|
|
664
664
|
|
|
@@ -718,7 +718,7 @@ export class FleetPanel extends ScrollableListPanel<FleetTreeRow> {
|
|
|
718
718
|
|
|
719
719
|
if (tab.kind === 'wrfc-chain') {
|
|
720
720
|
const memberRows = snapshotRows.filter((row) => row.node.parentId === tab.nodeId);
|
|
721
|
-
// d3
|
|
721
|
+
// d3: a completed chain prunes its wrapper node, so an empty member
|
|
722
722
|
// list can mean "finished", not "not started yet". Absent (pruned) OR
|
|
723
723
|
// terminal => the honest "completed" wording; present + non-terminal with
|
|
724
724
|
// no members yet => the "not started" wording.
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
// ---------------------------------------------------------------------------
|
|
2
2
|
// fleet-read-model.ts
|
|
3
3
|
//
|
|
4
|
-
//
|
|
4
|
+
// (Fleet tree panel) — pure, testable read-model over the SDK's live
|
|
5
5
|
// process registry (@pellux/goodvibes-sdk/platform/runtime/fleet, landed on
|
|
6
|
-
// SDK main as
|
|
6
|
+
// SDK main as). No BasePanel/rendering dependency: this module owns the
|
|
7
7
|
// flat ProcessNode[] -> tree transformation, the honest cost/token
|
|
8
8
|
// aggregates, and the state->glyph/tone mapping that FleetPanel renders.
|
|
9
9
|
//
|
|
10
10
|
// Design notes:
|
|
11
|
-
// - The
|
|
11
|
+
// - The brief predates the real SDK API and sketched a bespoke
|
|
12
12
|
// FleetRegistryNode/FleetRegistry adapter pair. The real
|
|
13
13
|
// createProcessRegistry() ProcessNode already carries elapsedMs,
|
|
14
14
|
// usage, costUsd, costState, and currentActivity directly, so no
|
|
@@ -30,6 +30,7 @@
|
|
|
30
30
|
|
|
31
31
|
import type {
|
|
32
32
|
ArchivableProcessRegistry,
|
|
33
|
+
ProcessAttention,
|
|
33
34
|
ProcessCostState,
|
|
34
35
|
ProcessKind,
|
|
35
36
|
ProcessNode,
|
|
@@ -136,13 +137,11 @@ const STATE_TONES: Record<ProcessState, FleetStateTone> = {
|
|
|
136
137
|
const TERMINAL_STATES = new Set<ProcessState>(['done', 'failed', 'killed', 'interrupted']);
|
|
137
138
|
|
|
138
139
|
/**
|
|
139
|
-
* States that mean "this node is waiting on ME right now"
|
|
140
|
-
*
|
|
141
|
-
*
|
|
142
|
-
*
|
|
143
|
-
*
|
|
144
|
-
* surface (badge, sort, jump) picks it up for free. Kept as a set for that
|
|
145
|
-
* forward-compatibility, not because more than one state qualifies today.
|
|
140
|
+
* States that mean "this node is waiting on ME right now" via the state field
|
|
141
|
+
* alone. The registry now also publishes the canonical `needsAttention`
|
|
142
|
+
* projection (reason 'approval' | 'input' — see fleetNodeAttention below),
|
|
143
|
+
* which every surface prefers; this set remains only as the fallback mapping
|
|
144
|
+
* for nodes without the projection, so nothing regresses on older registries.
|
|
146
145
|
*/
|
|
147
146
|
const BLOCKED_ON_USER_STATES = new Set<ProcessState>(['awaiting-approval']);
|
|
148
147
|
|
|
@@ -195,8 +194,48 @@ export function isBlockedOnUserState(state: ProcessState): boolean {
|
|
|
195
194
|
return BLOCKED_ON_USER_STATES.has(state);
|
|
196
195
|
}
|
|
197
196
|
|
|
197
|
+
/**
|
|
198
|
+
* The node's waiting-on-human classification, preferring the SDK's canonical
|
|
199
|
+
* `needsAttention` projection (reason 'approval' | 'input' — broader than the
|
|
200
|
+
* awaiting-approval state alone: it also covers a session waiting on user
|
|
201
|
+
* input). Falls back to the state-derived mapping for registries that predate
|
|
202
|
+
* the projection, so nothing regresses. This is THE membership every
|
|
203
|
+
* "blocked on me" surface (badge, sort, jump) derives from.
|
|
204
|
+
*/
|
|
205
|
+
export function fleetNodeAttention(node: ProcessNode): ProcessAttention | null {
|
|
206
|
+
if (node.needsAttention) return node.needsAttention;
|
|
207
|
+
if (isBlockedOnUserState(node.state)) return { reason: 'approval' };
|
|
208
|
+
return null;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
/** True when the node is waiting on the operator right now (approval or input). */
|
|
212
|
+
export function isBlockedOnUserNode(node: ProcessNode): boolean {
|
|
213
|
+
return fleetNodeAttention(node) !== null;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
/** The literal badge text for a waiting-on-human node, by reason. */
|
|
217
|
+
export function fleetAttentionText(attention: ProcessAttention): string {
|
|
218
|
+
return attention.reason === 'input' ? 'needs your input' : 'blocked on you';
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
/**
|
|
222
|
+
* The stall marker for a row — 'quiet Nm' (or 'quiet NhMm' past an hour),
|
|
223
|
+
* derived from the read-model's stall tell (pure timestamp comparison on the
|
|
224
|
+
* registry side; a live node whose last observable output is older than the
|
|
225
|
+
* threshold). Null when the node is not stalled-quiet.
|
|
226
|
+
*/
|
|
227
|
+
export function fleetStallMarker(node: ProcessNode): string | null {
|
|
228
|
+
const stall = node.stall;
|
|
229
|
+
if (!stall) return null;
|
|
230
|
+
const minutes = Math.max(1, Math.floor(stall.quietForMs / 60_000));
|
|
231
|
+
if (minutes < 60) return `quiet ${minutes}m`;
|
|
232
|
+
const hours = Math.floor(minutes / 60);
|
|
233
|
+
const rest = minutes % 60;
|
|
234
|
+
return rest > 0 ? `quiet ${hours}h${rest}m` : `quiet ${hours}h`;
|
|
235
|
+
}
|
|
236
|
+
|
|
198
237
|
// ---------------------------------------------------------------------------
|
|
199
|
-
// Honest usage/cost helpers (
|
|
238
|
+
// Honest usage/cost helpers (convention, ported from agent-inspector-shared.ts)
|
|
200
239
|
// ---------------------------------------------------------------------------
|
|
201
240
|
|
|
202
241
|
/**
|
|
@@ -261,7 +300,7 @@ function collectBlockedSubtrees(
|
|
|
261
300
|
if (cached !== undefined) return cached;
|
|
262
301
|
if (visiting.has(node.id)) return false; // cycle guard
|
|
263
302
|
visiting.add(node.id);
|
|
264
|
-
let has =
|
|
303
|
+
let has = isBlockedOnUserNode(node);
|
|
265
304
|
for (const child of childrenByParent.get(node.id) ?? []) {
|
|
266
305
|
if (dfs(child)) has = true;
|
|
267
306
|
}
|
|
@@ -458,7 +497,7 @@ export function buildFleetSnapshot(nodes: readonly ProcessNode[], capturedAt: nu
|
|
|
458
497
|
// Blocked ids in display (row) order so the panel's jump key steps through
|
|
459
498
|
// them top-to-bottom exactly as they appear on screen.
|
|
460
499
|
const blockedNodeIds = rows
|
|
461
|
-
.filter((row) =>
|
|
500
|
+
.filter((row) => isBlockedOnUserNode(row.node))
|
|
462
501
|
.map((row) => row.node.id);
|
|
463
502
|
|
|
464
503
|
return { rows, totalCost, totalTokens, runningCount, blockedNodeIds, capturedAt };
|
|
@@ -34,7 +34,7 @@ export function steerBadgeTone(status: SteerBadgeStatus, palette: PanelPalette):
|
|
|
34
34
|
}
|
|
35
35
|
}
|
|
36
36
|
|
|
37
|
-
/** Labels of the currently live, steerable agent nodes other than `excludeNodeId` — offered when a steer target has gone inactive (WO
|
|
37
|
+
/** Labels of the currently live, steerable agent nodes other than `excludeNodeId` — offered when a steer target has gone inactive (WO item 4). */
|
|
38
38
|
export function liveSteerableLabels(nodes: readonly ProcessNode[], excludeNodeId: string): string[] {
|
|
39
39
|
return nodes
|
|
40
40
|
.filter((node) => node.id !== excludeNodeId && node.kind === 'agent' && node.capabilities.steerable && !isTerminalProcessState(node.state))
|
|
@@ -49,7 +49,7 @@ export function steerRefusalMessage(reason: string, siblingLabels: readonly stri
|
|
|
49
49
|
return `${reason}.${suggestion}`;
|
|
50
50
|
}
|
|
51
51
|
|
|
52
|
-
/** One-line honest status for a tab's steer badge; the queued line names the target and points at the ⧗ delivery badge (WO
|
|
52
|
+
/** One-line honest status for a tab's steer badge; the queued line names the target and points at the ⧗ delivery badge (WO item 4). */
|
|
53
53
|
export function renderSteerBadgeLine(badge: SteerBadge, width: number, palette: PanelPalette, targetLabel?: string): Line {
|
|
54
54
|
const glyph = steerBadgeGlyph(badge.status);
|
|
55
55
|
const tone = steerBadgeTone(badge.status, palette);
|
package/src/panels/fleet-stop.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// ---------------------------------------------------------------------------
|
|
2
2
|
// fleet-stop.ts
|
|
3
3
|
//
|
|
4
|
-
// d1
|
|
4
|
+
// d1 — the "stopping…" write-window overlay, split out of fleet-panel.ts
|
|
5
5
|
// to keep that file under the architecture 800-line gate (same rationale as
|
|
6
6
|
// fleet-steer.ts). FleetPanel owns nothing here beyond a FleetStopTracker
|
|
7
7
|
// instance; all the state->display logic lives in these pure helpers.
|
|
@@ -80,7 +80,7 @@ export interface FleetPauseDeps {
|
|
|
80
80
|
}
|
|
81
81
|
|
|
82
82
|
/**
|
|
83
|
-
* d2
|
|
83
|
+
* d2: 'p' toggles pause<->resume by the node's state. A `paused`
|
|
84
84
|
* resumable node is resumed (interrupt's inverse); a live pausable node is
|
|
85
85
|
* paused via the registry's interrupt() (its disable path). Honest refusal for
|
|
86
86
|
* non-resumable/non-pausable kinds. Returns true when the key is consumed,
|
|
@@ -152,7 +152,7 @@ export function buildFleetTreeHints(
|
|
|
152
152
|
return hints;
|
|
153
153
|
}
|
|
154
154
|
|
|
155
|
-
/** K-confirm descendant stats (
|
|
155
|
+
/** K-confirm descendant stats (item 6): total = every non-terminal descendant (what a cascade kill takes down); active = the individually-killable subset. Was "active only" in an earlier version, undercounting a mixed subtree. */
|
|
156
156
|
export function countDescendantStats(rows: readonly FleetTreeRow[], nodeId: string): { total: number; active: number } {
|
|
157
157
|
const byParent = new Map<string, string[]>();
|
|
158
158
|
for (const row of rows) {
|
package/src/panels/fleet-tabs.ts
CHANGED
|
@@ -91,17 +91,16 @@ export function appendSteerText(draft: string, ch: string): string {
|
|
|
91
91
|
* One attached session tab. `agentId` is the SDK attach handle for
|
|
92
92
|
* `AgentManager.getConversationSnapshot(agentId)` — populated only for
|
|
93
93
|
* 'agent' tabs (agent.ts sets ProcessNode.id = record.id, so node.id IS the
|
|
94
|
-
* agentId
|
|
95
|
-
*
|
|
96
|
-
*
|
|
97
|
-
*
|
|
94
|
+
* agentId). 'wrfc-chain' tabs render a live member-summary instead of a
|
|
95
|
+
* transcript (a chain has no single conversation of its own — see
|
|
96
|
+
* fleet-transcript.ts renderFleetChainSummary) and carry an empty agentId,
|
|
97
|
+
* unused.
|
|
98
98
|
*
|
|
99
|
-
* `lineCache` is a PER-TAB `MessageLineCache
|
|
100
|
-
*
|
|
101
|
-
*
|
|
102
|
-
*
|
|
103
|
-
*
|
|
104
|
-
* Line[] are not retained (brief risk #8).
|
|
99
|
+
* `lineCache` is a PER-TAB `MessageLineCache`: switching tabs must never
|
|
100
|
+
* invalidate or thrash the main session's own cache, and a background
|
|
101
|
+
* (non-focused) tab must render nothing beyond its existing tree-row, so its
|
|
102
|
+
* cache simply stays empty (size 0) until the tab is focused. Disposed
|
|
103
|
+
* (`.clear()`) on detach so a closed tab's rendered Line[] are not retained.
|
|
105
104
|
*/
|
|
106
105
|
export interface FleetTab {
|
|
107
106
|
readonly nodeId: string;
|
|
@@ -111,10 +110,10 @@ export interface FleetTab {
|
|
|
111
110
|
readonly lineCache: MessageLineCache;
|
|
112
111
|
/**
|
|
113
112
|
* Cached parsed ledger fallback for a terminal agent whose conversation
|
|
114
|
-
* snapshot has been evicted from the SDK's retention ring (
|
|
115
|
-
* path). `null` = not yet loaded/attempted; `[]` = loaded and
|
|
116
|
-
* the load failed) — both render the same honest "no transcript"
|
|
117
|
-
* once loaded is true.
|
|
113
|
+
* snapshot has been evicted from the SDK's retention ring (the degraded
|
|
114
|
+
* fallback path). `null` = not yet loaded/attempted; `[]` = loaded and
|
|
115
|
+
* empty (or the load failed) — both render the same honest "no transcript"
|
|
116
|
+
* state once loaded is true.
|
|
118
117
|
*/
|
|
119
118
|
ledgerEntries: Record<string, unknown>[] | null;
|
|
120
119
|
ledgerLoadStarted: boolean;
|
|
@@ -59,7 +59,7 @@ function toneColor(tone: FleetStateTone, palette: PanelPalette): string {
|
|
|
59
59
|
}
|
|
60
60
|
|
|
61
61
|
// ---------------------------------------------------------------------------
|
|
62
|
-
// Agent transcript (
|
|
62
|
+
// Agent transcript (live/frozen split)
|
|
63
63
|
// ---------------------------------------------------------------------------
|
|
64
64
|
|
|
65
65
|
export type FleetTranscriptMode = 'live' | 'frozen' | 'unavailable';
|
|
@@ -70,7 +70,7 @@ export interface FleetTranscriptRender {
|
|
|
70
70
|
}
|
|
71
71
|
|
|
72
72
|
/**
|
|
73
|
-
*
|
|
73
|
+
* (design point 4) — a 'frozen' transcript is a static capture of a
|
|
74
74
|
* completed process, not something that will change on the next render. The
|
|
75
75
|
* distinction matters because this wave also introduced kill/interrupt
|
|
76
76
|
* display bugs (elapsed/usage briefly climbing after a terminal state
|
|
@@ -157,7 +157,7 @@ export function renderFleetAgentTranscript(
|
|
|
157
157
|
}
|
|
158
158
|
|
|
159
159
|
// ---------------------------------------------------------------------------
|
|
160
|
-
// Chain summary — 'wrfc-chain' tabs have no single conversation
|
|
160
|
+
// Chain summary — 'wrfc-chain' tabs have no single conversation
|
|
161
161
|
// ---------------------------------------------------------------------------
|
|
162
162
|
|
|
163
163
|
/**
|
|
@@ -197,7 +197,7 @@ export function renderFleetChainSummary(
|
|
|
197
197
|
|
|
198
198
|
// ---------------------------------------------------------------------------
|
|
199
199
|
// Ledger fallback — degraded activity view for a terminal agent whose
|
|
200
|
-
// full-fidelity snapshot is unavailable (
|
|
200
|
+
// full-fidelity snapshot is unavailable (an honest fallback)
|
|
201
201
|
// ---------------------------------------------------------------------------
|
|
202
202
|
|
|
203
203
|
/**
|
|
@@ -257,7 +257,7 @@ function renderLedgerEntry(width: number, entry: Record<string, unknown>, palett
|
|
|
257
257
|
const toolName = typeof entry['toolName'] === 'string' ? entry['toolName'] : 'tool';
|
|
258
258
|
const success = entry['success'] !== false;
|
|
259
259
|
const bad = palette.bad ?? DEFAULT_PANEL_PALETTE.bad;
|
|
260
|
-
//
|
|
260
|
+
// the writer already truncates this to 500 chars (session.ts's
|
|
261
261
|
// resultPreview field) — passed through verbatim (collapsed to one
|
|
262
262
|
// line), never re-summarized or fabricated, then tail-truncated again
|
|
263
263
|
// to fit the row.
|
package/src/panels/git-panel.ts
CHANGED
|
@@ -77,7 +77,7 @@ const C = extendPalette(DEFAULT_PANEL_PALETTE, {
|
|
|
77
77
|
commitAuthor: '244',
|
|
78
78
|
selected: '#1c1c1c',
|
|
79
79
|
selectedFg: '#ffffff',
|
|
80
|
-
diffMeta: DIFF_TONES.hunk, //
|
|
80
|
+
diffMeta: DIFF_TONES.hunk, // shared diff-hunk token, was a local literal
|
|
81
81
|
diffNeutral: '250',
|
|
82
82
|
// Reuses the existing workflow accent token rather than adding a new hex
|
|
83
83
|
// literal (architecture gate ratchets the raw-hex-literal count).
|
|
@@ -626,7 +626,7 @@ export class GitPanel extends BasePanel {
|
|
|
626
626
|
{ keys: 'r', label: 'refresh' },
|
|
627
627
|
], DEFAULT_PANEL_PALETTE),
|
|
628
628
|
];
|
|
629
|
-
const emptyStateLines = buildEmptyState(width, ' No git rows', 'This repository has no staged or unstaged changes and no commits to display yet.', [{ command: 'r', summary: 'refresh working-tree status in this panel, or stage changes to populate this view' }], DEFAULT_PANEL_PALETTE); //
|
|
629
|
+
const emptyStateLines = buildEmptyState(width, ' No git rows', 'This repository has no staged or unstaged changes and no commits to display yet.', [{ command: 'r', summary: 'refresh working-tree status in this panel, or stage changes to populate this view' }], DEFAULT_PANEL_PALETTE); // was '/git status' (chat-only, never touches this panel)
|
|
630
630
|
const rows: Line[] = [];
|
|
631
631
|
for (let i = 0; i < this.items.length; i++) {
|
|
632
632
|
const item = this.items[i];
|
package/src/panels/index.ts
CHANGED
|
@@ -7,7 +7,7 @@ export { CostTrackerPanel } from './cost-tracker-panel.ts';
|
|
|
7
7
|
export { GitPanel } from './git-panel.ts';
|
|
8
8
|
export { registerBuiltinPanels } from './builtin-panels.ts';
|
|
9
9
|
export type { BuiltinPanelDeps } from './builtin-panels.ts';
|
|
10
|
-
//
|
|
10
|
+
// (the purge) — group B: plugins/skills panels are register-retired but
|
|
11
11
|
// their modules are retained for shared non-class exports (PluginManagerControls;
|
|
12
12
|
// discoverSkills + SkillRecord/SkillOrigin, consumed by command runtimes and the
|
|
13
13
|
// skills modal). The other group-A and group-B panel classes are deleted outright
|
|
@@ -16,3 +16,6 @@ export { PluginsPanel } from './plugins-panel.ts';
|
|
|
16
16
|
export { SkillsPanel } from './skills-panel.ts';
|
|
17
17
|
export { FleetPanel } from './fleet-panel.ts';
|
|
18
18
|
export type { FleetActionCallbacks } from './fleet-panel.ts';
|
|
19
|
+
export { NotificationsPanel } from './notifications-panel.ts';
|
|
20
|
+
export { PanelNotificationFeed, getSharedNotificationFeed } from './notifications-feed.ts';
|
|
21
|
+
export type { PanelFeedEntry } from './notifications-feed.ts';
|
|
@@ -21,7 +21,7 @@ import { summarizeError } from '@pellux/goodvibes-sdk/platform/utils';
|
|
|
21
21
|
import { type ConfirmState, handleConfirmInput } from './confirm-state.ts';
|
|
22
22
|
|
|
23
23
|
// Base chrome only — state colors and text tokens come straight from
|
|
24
|
-
// DEFAULT_PANEL_PALETTE
|
|
24
|
+
// DEFAULT_PANEL_PALETTE.
|
|
25
25
|
const C = DEFAULT_PANEL_PALETTE;
|
|
26
26
|
|
|
27
27
|
function formatRoles(roles: readonly string[]): string {
|
|
@@ -16,7 +16,7 @@ import type {
|
|
|
16
16
|
} from '../../input/config-modal-types.ts';
|
|
17
17
|
|
|
18
18
|
// ---------------------------------------------------------------------------
|
|
19
|
-
// Hooks → config-modal surface (
|
|
19
|
+
// Hooks → config-modal surface (group-B port). Two tabs: 'Hooks' (active
|
|
20
20
|
// registrations + contract posture) and 'Activity' (recent runtime activity +
|
|
21
21
|
// managed authoring + last simulation — the panel's expand-activity view, now a
|
|
22
22
|
// dedicated tab). All sources are in-memory (dispatcher/workbench/tracker hold
|
|
@@ -9,7 +9,7 @@ import type {
|
|
|
9
9
|
import { KeybindingsManager } from '../../input/keybindings.ts';
|
|
10
10
|
|
|
11
11
|
// ---------------------------------------------------------------------------
|
|
12
|
-
// Docs + Shortcuts → 'keybindings' config-modal surface (
|
|
12
|
+
// Docs + Shortcuts → 'keybindings' config-modal surface (group-B port).
|
|
13
13
|
// A MERGE of the retired DocsPanel (tools/models/shortcuts) and the
|
|
14
14
|
// shortcuts-overlay reference. Three tabs: 'Tools', 'Models', and 'Shortcuts'
|
|
15
15
|
// (the categorized keyboard reference — driven live off keybindingsManager so
|
|
@@ -112,7 +112,7 @@ class KeybindingsModalSurface implements ConfigModalSurface {
|
|
|
112
112
|
if (id === 'refresh') { ctx.setStatus('Docs & shortcuts are read live.'); ctx.requestRender(); return; }
|
|
113
113
|
if (id !== 'activate') return;
|
|
114
114
|
if (ctx.tabId === 'tools') {
|
|
115
|
-
//
|
|
115
|
+
// item 4: pass the tool name through as a deep-link target.
|
|
116
116
|
// Honest caveat (documented, not a bug to silently paper over): fleet's
|
|
117
117
|
// ProcessKind set has no 'tool' node — a docs Tools row names a static
|
|
118
118
|
// tool DEFINITION, not a live process, so FleetPanel.receiveDeepLink
|
|
@@ -9,7 +9,7 @@ import type {
|
|
|
9
9
|
import { summarizeError } from '@pellux/goodvibes-sdk/platform/utils';
|
|
10
10
|
|
|
11
11
|
// ---------------------------------------------------------------------------
|
|
12
|
-
// Knowledge (graph) → config-modal surface (
|
|
12
|
+
// Knowledge (graph) → config-modal surface (group-B port). Two tabs:
|
|
13
13
|
// 'Browse' (sources/nodes/issues + schedules) and 'Review' (the open-issue
|
|
14
14
|
// queue) — the panel's browse/review toggle, now host tabs. Issue review
|
|
15
15
|
// mutations (accept/reject/resolve/reopen) route to the `/knowledge
|
|
@@ -38,14 +38,14 @@ interface MarketplaceRow {
|
|
|
38
38
|
const KINDS: readonly EcosystemEntryKind[] = ['plugin', 'skill', 'hook-pack', 'policy-pack'];
|
|
39
39
|
|
|
40
40
|
/**
|
|
41
|
-
* Marketplace → config-modal surface (
|
|
41
|
+
* Marketplace → config-modal surface (group-B port). Local-first
|
|
42
42
|
* publish/import catalog (NOT a remote store) with provenance, compatibility,
|
|
43
43
|
* and install posture. Disk catalog loads happen in refresh() (never in
|
|
44
44
|
* buildView), mirroring the panel's explicit render()/refresh() split.
|
|
45
45
|
* Install/uninstall route to the `/marketplace` command path (charter: no
|
|
46
46
|
* destructive confirm folded into a modal).
|
|
47
47
|
*
|
|
48
|
-
*
|
|
48
|
+
* Honest empty-state: the panel's original "No curated marketplace
|
|
49
49
|
* entries found yet." copy implied a curated remote catalog. It isn't one —
|
|
50
50
|
* loadEcosystemCatalog only ever reads .goodvibes/ecosystem/<kind>s.json under
|
|
51
51
|
* the project/home, populated solely by `/marketplace publish` and
|
|
@@ -118,7 +118,7 @@ class MarketplaceModalSurface implements ConfigModalSurface {
|
|
|
118
118
|
const snapshot = this.deps.readModel?.getSnapshot();
|
|
119
119
|
const rows: ConfigModalRow[] = [];
|
|
120
120
|
|
|
121
|
-
// Honest empty / degraded state
|
|
121
|
+
// Honest empty / degraded state. loadError → degraded banner.
|
|
122
122
|
if (this.loadError) {
|
|
123
123
|
return { title: 'Marketplace', degraded: this.loadError, tabs: [{ id: 'catalog', label: 'Catalog', rows: [] }] };
|
|
124
124
|
}
|
|
@@ -205,7 +205,7 @@ export function createMarketplaceModalSurface(deps: MarketplaceModalDeps): Confi
|
|
|
205
205
|
* Deterministic golden fixture: catalog roots wired at a fresh tmp path that is
|
|
206
206
|
* removed immediately (loadEcosystemCatalog/listInstalledEcosystemEntries guard
|
|
207
207
|
* missing paths with existsSync → [], so refresh() finds nothing and renders the
|
|
208
|
-
*
|
|
208
|
+
* honest empty-state copy). The random tmp path never appears in the
|
|
209
209
|
* rendered lines (the empty-state copy is static), so the golden is byte-stable.
|
|
210
210
|
*/
|
|
211
211
|
export function marketplaceModalGoldenSurface(): ConfigModalSurface {
|
|
@@ -9,7 +9,7 @@ import type {
|
|
|
9
9
|
import { memoryRecordTemporalStatus } from '@pellux/goodvibes-sdk/platform/state';
|
|
10
10
|
|
|
11
11
|
// ---------------------------------------------------------------------------
|
|
12
|
-
// Memory → config-modal surface (
|
|
12
|
+
// Memory → config-modal surface (group-B port). Two tabs: 'All Records'
|
|
13
13
|
// (the full record list) and 'Review Queue' (the operator review queue) — the
|
|
14
14
|
// same split the panel toggled with Tab, now real host tabs. Review-state
|
|
15
15
|
// mutations (reviewed/stale/contradicted/fresh) and record delete route to the
|
|
@@ -227,9 +227,9 @@ export async function memoryModalGoldenSurface(): Promise<ConfigModalSurface> {
|
|
|
227
227
|
const records: readonly MemoryRecordLike[] = [
|
|
228
228
|
{
|
|
229
229
|
id: 'mem-0000001a', scope: 'project', cls: 'decision',
|
|
230
|
-
summary: '
|
|
231
|
-
detail: 'Applies to
|
|
232
|
-
tags: ['
|
|
230
|
+
summary: 'Panel retirements are batched behind modal builders.',
|
|
231
|
+
detail: 'Applies to the knowledge, memory, and work-plan modals.',
|
|
232
|
+
tags: ['panels', 'modals'], reviewState: 'reviewed', confidence: 90,
|
|
233
233
|
reviewedAt: FIXED_CREATED_AT + 3600000, reviewedBy: 'operator', createdAt: FIXED_CREATED_AT,
|
|
234
234
|
provenance: [{ kind: 'session', ref: 'session-fixed-1' }],
|
|
235
235
|
},
|
|
@@ -3,7 +3,7 @@ import type { ModalSectionStyle } from '../../renderer/modal-factory.ts';
|
|
|
3
3
|
import type { ConfigModalRow } from '../../input/config-modal-types.ts';
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
|
-
* Shared formatting helpers for
|
|
6
|
+
* Shared formatting helpers for config-modal surfaces. Row styling reuses
|
|
7
7
|
* DEFAULT_PANEL_PALETTE (the retired panels' palette) so a migrated modal reads
|
|
8
8
|
* as the same surface it replaced — the goldens stay panel-consistent.
|
|
9
9
|
*/
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { activeUiTones, registerThemeRefresh } from '../../renderer/theme.ts';
|
|
2
2
|
|
|
3
3
|
// ---------------------------------------------------------------------------
|
|
4
|
-
// Semantic tone tokens for the
|
|
4
|
+
// Semantic tone tokens for the group-B config modals.
|
|
5
5
|
//
|
|
6
6
|
// Derived entirely from the chrome token set so the builders carry NO raw hex
|
|
7
7
|
// color literals (architecture: no-raw-hex-literal-growth). Rebuilt IN PLACE
|