@pellux/goodvibes-tui 1.1.0 → 1.9.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 +85 -21
- package/README.md +28 -18
- package/docs/foundation-artifacts/operator-contract.json +4045 -1763
- package/package.json +2 -2
- package/src/audio/spoken-turn-controller.ts +12 -2
- package/src/audio/spoken-turn-wiring.ts +2 -1
- package/src/cli/help.ts +8 -1
- package/src/cli/service-posture.ts +2 -1
- package/src/cli/status.ts +2 -1
- package/src/cli/surface-command.ts +2 -2
- package/src/core/alert-gating.ts +67 -0
- package/src/core/approval-alert.ts +72 -0
- package/src/core/budget-breach-notifier.ts +106 -0
- package/src/core/composer-state.ts +11 -3
- package/src/core/conversation-line-cache.ts +27 -3
- package/src/core/conversation-rendering.ts +90 -14
- package/src/core/conversation.ts +18 -0
- package/src/core/focus-tracker.ts +41 -0
- package/src/core/long-task-notifier.ts +33 -6
- package/src/core/stream-event-wiring.ts +20 -1
- package/src/core/system-message-noise.ts +87 -0
- package/src/core/system-message-router.ts +104 -51
- package/src/core/turn-cancellation.ts +25 -0
- package/src/core/turn-event-wiring.ts +72 -3
- package/src/daemon/cli.ts +29 -2
- package/src/daemon/handlers/register.ts +8 -1
- package/src/daemon/service-commands.ts +329 -0
- package/src/export/cost-utils.ts +36 -0
- package/src/input/autocomplete.ts +27 -1
- package/src/input/command-registry.ts +84 -5
- package/src/input/commands/checkpoint-runtime.ts +280 -0
- package/src/input/commands/codebase-runtime.ts +232 -0
- package/src/input/commands/config.ts +43 -3
- package/src/input/commands/eval.ts +1 -1
- package/src/input/commands/health-runtime.ts +10 -2
- package/src/input/commands/image-runtime.ts +112 -0
- package/src/input/commands/intelligence-runtime.ts +11 -1
- package/src/input/commands/local-auth-runtime.ts +4 -1
- package/src/input/commands/local-runtime.ts +9 -3
- package/src/input/commands/marketplace-runtime.ts +2 -2
- package/src/input/commands/memory.ts +73 -35
- package/src/input/commands/operator-panel-runtime.ts +71 -31
- package/src/input/commands/planning-runtime.ts +116 -4
- package/src/input/commands/platform-sandbox-runtime.ts +1 -6
- package/src/input/commands/plugin-runtime.ts +2 -2
- package/src/input/commands/policy-dispatch.ts +21 -0
- package/src/input/commands/provider-accounts-runtime.ts +1 -1
- package/src/input/commands/qrcode-runtime.ts +26 -6
- package/src/input/commands/recall-review.ts +35 -0
- package/src/input/commands/remote-runtime-setup.ts +5 -3
- package/src/input/commands/remote-runtime.ts +3 -3
- package/src/input/commands/runtime-services.ts +54 -13
- package/src/input/commands/services-runtime.ts +1 -1
- package/src/input/commands/session-content.ts +20 -9
- package/src/input/commands/session-workflow.ts +16 -1
- package/src/input/commands/settings-sync-runtime.ts +16 -4
- package/src/input/commands/shell-core.ts +25 -8
- package/src/input/commands/skills-runtime.ts +2 -8
- package/src/input/commands/subscription-runtime.ts +2 -2
- package/src/input/commands/test-runtime.ts +277 -0
- package/src/input/commands/websearch-runtime.ts +101 -0
- package/src/input/commands/work-plan-runtime.ts +36 -10
- package/src/input/commands/workstream-runtime.ts +488 -0
- package/src/input/commands.ts +12 -0
- package/src/input/config-modal-types.ts +175 -0
- package/src/input/config-modal.ts +592 -0
- package/src/input/feed-context-factory.ts +10 -8
- package/src/input/handler-command-route.ts +37 -20
- package/src/input/handler-content-actions.ts +17 -2
- package/src/input/handler-feed-routes.ts +74 -114
- package/src/input/handler-feed.ts +79 -27
- package/src/input/handler-interactions.ts +1 -3
- package/src/input/handler-modal-routes.ts +136 -6
- package/src/input/handler-modal-stack.ts +14 -7
- package/src/input/handler-modal-token-routes.ts +16 -49
- package/src/input/handler-onboarding-daemon-adopt.ts +149 -0
- package/src/input/handler-onboarding.ts +71 -59
- package/src/input/handler-picker-routes.ts +26 -102
- package/src/input/handler-shortcuts.ts +74 -14
- package/src/input/handler-types.ts +2 -6
- package/src/input/handler-ui-state.ts +10 -22
- package/src/input/handler.ts +12 -29
- package/src/input/keybindings.ts +23 -8
- package/src/input/model-picker-types.ts +24 -6
- package/src/input/model-picker.ts +77 -2
- package/src/input/onboarding/onboarding-runtime-status.ts +10 -1
- package/src/input/onboarding/onboarding-wizard-apply.ts +8 -1
- package/src/input/onboarding/onboarding-wizard-constants.ts +4 -1
- package/src/input/onboarding/onboarding-wizard-network-adopt.ts +136 -0
- package/src/input/onboarding/onboarding-wizard-steps.ts +9 -6
- package/src/input/onboarding/onboarding-wizard-types.ts +3 -1
- package/src/input/panel-integration-actions.ts +9 -170
- package/src/input/panel-mouse-geometry.ts +97 -0
- package/src/input/panel-paste-flood-guard.ts +86 -0
- package/src/input/selection-modal.ts +11 -0
- package/src/input/session-picker-modal.ts +44 -4
- package/src/input/settings-modal-data.ts +146 -2
- package/src/input/settings-modal-mutations.ts +6 -4
- package/src/input/settings-modal-types.ts +4 -2
- package/src/main.ts +49 -51
- package/src/panels/agent-inspector-shared.ts +2 -1
- package/src/panels/base-panel.ts +22 -1
- package/src/panels/builtin/agent.ts +21 -107
- package/src/panels/builtin/development.ts +15 -61
- package/src/panels/builtin/knowledge.ts +8 -32
- package/src/panels/builtin/operations.ts +84 -428
- package/src/panels/builtin/session.ts +21 -112
- package/src/panels/builtin/shared.ts +18 -46
- package/src/panels/builtin-modals.ts +218 -0
- package/src/panels/builtin-panels.ts +5 -0
- package/src/panels/cost-tracker-panel.ts +36 -10
- package/src/panels/diff-panel.ts +12 -43
- package/src/panels/eval-registry.ts +60 -0
- package/src/panels/fleet-deep-link.ts +31 -0
- package/src/panels/fleet-panel-format.ts +62 -0
- package/src/panels/fleet-panel-worktree-detail.ts +48 -0
- package/src/panels/fleet-panel.ts +758 -0
- package/src/panels/fleet-read-model.ts +520 -0
- package/src/panels/fleet-steer.ts +125 -0
- package/src/panels/fleet-stop.ts +153 -0
- package/src/panels/fleet-tabs.ts +230 -0
- package/src/panels/fleet-transcript.ts +356 -0
- package/src/panels/index.ts +7 -31
- package/src/panels/modals/hooks-modal.ts +187 -0
- package/src/panels/modals/keybindings-modal.ts +166 -0
- package/src/panels/modals/knowledge-modal.ts +158 -0
- package/src/panels/modals/local-auth-modal.ts +132 -0
- package/src/panels/modals/marketplace-modal.ts +218 -0
- package/src/panels/modals/memory-modal.ts +180 -0
- package/src/panels/modals/modal-surface-helpers.ts +54 -0
- package/src/panels/modals/modal-theme.ts +42 -0
- package/src/panels/modals/pairing-modal.ts +163 -0
- package/src/panels/modals/planning-modal.ts +304 -0
- package/src/panels/modals/plugins-modal.ts +174 -0
- package/src/panels/modals/policy-modal.ts +256 -0
- package/src/panels/modals/provider-health-modal.ts +136 -0
- package/src/panels/modals/remote-modal.ts +115 -0
- package/src/panels/modals/sandbox-modal.ts +150 -0
- package/src/panels/modals/security-modal.ts +217 -0
- package/src/panels/modals/services-modal.ts +179 -0
- package/src/panels/modals/settings-sync-modal.ts +142 -0
- package/src/panels/modals/skills-modal.ts +189 -0
- package/src/panels/modals/subscription-modal.ts +172 -0
- package/src/panels/modals/work-plan-modal.ts +177 -0
- package/src/panels/panel-confirm-overlay.ts +72 -0
- package/src/panels/panel-manager.ts +123 -9
- package/src/panels/polish-core.ts +38 -25
- package/src/panels/project-planning-answer-actions.ts +8 -11
- package/src/panels/skills-panel.ts +7 -51
- package/src/panels/types.ts +37 -0
- package/src/permissions/hunk-selection.ts +179 -0
- package/src/permissions/prompt.ts +216 -13
- package/src/renderer/autocomplete-overlay.ts +28 -2
- package/src/renderer/compaction-history-modal.ts +19 -3
- package/src/renderer/compaction-preview.ts +13 -2
- package/src/renderer/compaction-quality.ts +100 -0
- package/src/renderer/compositor.ts +2 -3
- package/src/renderer/config-modal.ts +95 -0
- package/src/renderer/conversation-overlays.ts +10 -17
- package/src/renderer/fleet-tab-strip.ts +56 -0
- package/src/renderer/footer-tips.ts +15 -3
- package/src/renderer/fullscreen-primitives.ts +32 -22
- package/src/renderer/git-status.ts +43 -1
- package/src/renderer/help-overlay.ts +2 -2
- package/src/renderer/layout.ts +0 -4
- package/src/renderer/markdown.ts +7 -3
- package/src/renderer/modal-factory.ts +25 -20
- package/src/renderer/model-workspace.ts +62 -4
- package/src/renderer/overlay-box.ts +21 -17
- package/src/renderer/panel-workspace-bar.ts +8 -2
- package/src/renderer/process-indicator.ts +14 -3
- package/src/renderer/selection-modal-overlay.ts +6 -1
- package/src/renderer/session-picker-modal.ts +196 -3
- package/src/renderer/settings-modal-helpers.ts +2 -0
- package/src/renderer/settings-modal.ts +7 -0
- package/src/renderer/shell-surface.ts +8 -1
- package/src/renderer/status-glyphs.ts +14 -15
- package/src/renderer/system-message.ts +15 -3
- package/src/renderer/terminal-bg-probe.ts +339 -0
- package/src/renderer/terminal-escapes.ts +20 -0
- package/src/renderer/theme-mode-config.ts +67 -0
- package/src/renderer/theme.ts +91 -1
- package/src/renderer/thinking.ts +11 -3
- package/src/renderer/tool-call.ts +15 -9
- package/src/renderer/tool-result-summary.ts +148 -0
- package/src/renderer/turn-injection.ts +133 -0
- package/src/renderer/ui-factory.ts +154 -85
- package/src/renderer/ui-primitives.ts +30 -129
- package/src/runtime/bootstrap-command-context.ts +27 -1
- package/src/runtime/bootstrap-command-parts.ts +41 -10
- package/src/runtime/bootstrap-core.ts +47 -15
- package/src/runtime/bootstrap-hook-bridge.ts +7 -0
- package/src/runtime/bootstrap-shell.ts +48 -17
- package/src/runtime/bootstrap.ts +129 -22
- package/src/runtime/code-index-services.ts +135 -0
- package/src/runtime/legacy-daemon-migration.ts +516 -0
- package/src/runtime/memory-fold.ts +26 -0
- package/src/runtime/onboarding/derivation.ts +7 -2
- package/src/runtime/onboarding/snapshot.ts +27 -1
- package/src/runtime/onboarding/types.ts +31 -1
- package/src/runtime/operator-token-cleanup.ts +82 -1
- package/src/runtime/orchestrator-core-services.ts +59 -0
- package/src/runtime/process-lifecycle.ts +3 -1
- package/src/runtime/resume-notice.ts +209 -0
- package/src/runtime/services.ts +98 -46
- package/src/runtime/session-inbound-inputs.ts +252 -0
- package/src/runtime/session-spine-transport.ts +64 -0
- package/src/runtime/terminal-output-guard.ts +15 -8
- package/src/runtime/ui-services.ts +27 -3
- package/src/runtime/workstream-services.ts +327 -0
- package/src/runtime/wrfc-persistence.ts +124 -17
- package/src/shell/blocking-input.ts +68 -4
- package/src/shell/recovery-input-helpers.ts +170 -1
- package/src/shell/ui-openers.ts +171 -26
- package/src/utils/format-duration.ts +8 -18
- package/src/utils/splash-lines.ts +1 -1
- package/src/utils/terminal-width.ts +52 -0
- package/src/version.ts +1 -1
- package/src/panels/agent-inspector-panel.ts +0 -786
- package/src/panels/approval-panel.ts +0 -252
- package/src/panels/automation-control-panel.ts +0 -479
- package/src/panels/cockpit-panel.ts +0 -481
- package/src/panels/cockpit-read-model.ts +0 -238
- package/src/panels/communication-panel.ts +0 -256
- package/src/panels/control-plane-panel.ts +0 -470
- package/src/panels/debug-panel.ts +0 -615
- package/src/panels/docs-panel.ts +0 -384
- package/src/panels/eval-panel.ts +0 -627
- package/src/panels/file-explorer-panel.ts +0 -673
- package/src/panels/file-preview-panel.ts +0 -517
- package/src/panels/hooks-panel.ts +0 -401
- package/src/panels/incident-review-panel.ts +0 -406
- package/src/panels/intelligence-panel.ts +0 -383
- package/src/panels/knowledge-graph-panel.ts +0 -515
- package/src/panels/marketplace-panel.ts +0 -399
- package/src/panels/memory-panel.ts +0 -558
- package/src/panels/ops-control-panel.ts +0 -329
- package/src/panels/ops-strategy-panel.ts +0 -321
- package/src/panels/orchestration-panel.ts +0 -465
- package/src/panels/panel-list-panel.ts +0 -566
- package/src/panels/plan-dashboard-panel.ts +0 -707
- package/src/panels/policy-panel.ts +0 -517
- package/src/panels/project-planning-panel.ts +0 -731
- package/src/panels/provider-health-panel.ts +0 -678
- package/src/panels/provider-health-tracker.ts +0 -310
- package/src/panels/provider-health-views.ts +0 -567
- package/src/panels/qr-panel.ts +0 -280
- package/src/panels/remote-panel.ts +0 -534
- package/src/panels/routes-panel.ts +0 -241
- package/src/panels/sandbox-panel.ts +0 -456
- package/src/panels/security-panel.ts +0 -447
- package/src/panels/services-panel.ts +0 -329
- package/src/panels/session-browser-panel.ts +0 -496
- package/src/panels/settings-sync-panel.ts +0 -398
- package/src/panels/subscription-panel.ts +0 -342
- package/src/panels/symbol-outline-panel.ts +0 -619
- package/src/panels/system-messages-panel.ts +0 -364
- package/src/panels/tasks-panel.ts +0 -608
- package/src/panels/thinking-panel.ts +0 -333
- package/src/panels/tool-inspector-panel.ts +0 -537
- package/src/panels/work-plan-panel.ts +0 -540
- package/src/panels/worktree-panel.ts +0 -360
- package/src/panels/wrfc-panel.ts +0 -790
- package/src/renderer/agent-detail-modal.ts +0 -466
- package/src/renderer/live-tail-modal.ts +0 -156
- package/src/renderer/process-modal.ts +0 -671
- package/src/renderer/qr-renderer.ts +0 -120
|
@@ -14,6 +14,14 @@ import type { McpRegistry } from '@pellux/goodvibes-sdk/platform/mcp';
|
|
|
14
14
|
import { buildSubscriptionEntries } from './settings-modal-subscriptions.ts';
|
|
15
15
|
import type { SubscriptionManager } from '@pellux/goodvibes-sdk/platform/config';
|
|
16
16
|
import type { ServiceInspectionQuery } from '../runtime/ui-service-queries.ts';
|
|
17
|
+
import { CODE_INDEX_ENABLED_CONFIG_KEY } from '../runtime/code-index-services.ts';
|
|
18
|
+
import {
|
|
19
|
+
THEME_MODE_CONFIG_KEY,
|
|
20
|
+
THEME_MODE_VALUES,
|
|
21
|
+
THEME_MODE_DEFAULT,
|
|
22
|
+
THEME_MODE_DESCRIPTION,
|
|
23
|
+
coerceThemeModeSetting,
|
|
24
|
+
} from '../renderer/theme-mode-config.ts';
|
|
17
25
|
import {
|
|
18
26
|
SETTINGS_CATEGORIES,
|
|
19
27
|
type FlagEntry,
|
|
@@ -93,6 +101,14 @@ export function buildSettingGroups(
|
|
|
93
101
|
}
|
|
94
102
|
}
|
|
95
103
|
|
|
104
|
+
// DEBT-2: inject the synthetic display.themeMode enum (auto|dark|light). TUI-local
|
|
105
|
+
// key stored under the existing `display` section (see theme-mode-config.ts for why
|
|
106
|
+
// not `appearance`), same rationale as the other synthetic settings below.
|
|
107
|
+
const displayEntries = groups.get('display');
|
|
108
|
+
if (displayEntries && !displayEntries.some((e) => e.setting.key === (THEME_MODE_CONFIG_KEY as ConfigKey))) {
|
|
109
|
+
displayEntries.push(buildThemeModeSyntheticEntry(configManager));
|
|
110
|
+
}
|
|
111
|
+
|
|
96
112
|
const uiEntries = groups.get('ui');
|
|
97
113
|
if (uiEntries) {
|
|
98
114
|
const uiPriority: Record<string, number> = {
|
|
@@ -130,6 +146,25 @@ export function buildSettingGroups(
|
|
|
130
146
|
behaviorEntries.push(buildNotifyAfterSecondsSyntheticEntry(configManager));
|
|
131
147
|
}
|
|
132
148
|
|
|
149
|
+
// Inject the W2.3 alert-class toggles + master focus gate. TUI-local
|
|
150
|
+
// synthetic settings, same rationale as notifyAfterSeconds above.
|
|
151
|
+
if (behaviorEntries) {
|
|
152
|
+
for (const entry of buildNotifyAlertSyntheticEntries(configManager)) {
|
|
153
|
+
if (!behaviorEntries.some((e) => e.setting.key === entry.setting.key)) {
|
|
154
|
+
behaviorEntries.push(entry);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// Wave 5 (wo804): inject the storage.codeIndexEnabled toggle into the
|
|
160
|
+
// storage category. TUI-local synthetic setting (not in the SDK ConfigKey
|
|
161
|
+
// union — see code-index-services.ts), same rationale as
|
|
162
|
+
// notifyAfterSeconds above: opt-in, default off, states its own bounds.
|
|
163
|
+
const storageEntries = groups.get('storage');
|
|
164
|
+
if (storageEntries && !storageEntries.some((e) => e.setting.key === (CODE_INDEX_ENABLED_CONFIG_KEY as ConfigKey))) {
|
|
165
|
+
storageEntries.push(buildCodeIndexEnabledSyntheticEntry(configManager));
|
|
166
|
+
}
|
|
167
|
+
|
|
133
168
|
return groups;
|
|
134
169
|
}
|
|
135
170
|
|
|
@@ -179,6 +214,34 @@ export function buildTtsSpeedSyntheticEntry(configManager: Pick<ConfigManager, '
|
|
|
179
214
|
};
|
|
180
215
|
}
|
|
181
216
|
|
|
217
|
+
// ---------------------------------------------------------------------------
|
|
218
|
+
// display.themeMode synthetic setting (DEBT-2 light theme)
|
|
219
|
+
// ---------------------------------------------------------------------------
|
|
220
|
+
|
|
221
|
+
/**
|
|
222
|
+
* The synthetic ConfigSetting descriptor for display.themeMode. TUI-local (not
|
|
223
|
+
* in the SDK ConfigKey union); the key is cast to ConfigKey as the other
|
|
224
|
+
* synthetic settings do. Stored under the existing `display` section so
|
|
225
|
+
* ConfigManager.setDynamic/get round-trip it with no SDK change.
|
|
226
|
+
*/
|
|
227
|
+
export const THEME_MODE_SYNTHETIC_SETTING: ConfigSetting = {
|
|
228
|
+
key: THEME_MODE_CONFIG_KEY as ConfigKey,
|
|
229
|
+
type: 'enum',
|
|
230
|
+
default: THEME_MODE_DEFAULT,
|
|
231
|
+
enumValues: [...THEME_MODE_VALUES],
|
|
232
|
+
description: THEME_MODE_DESCRIPTION,
|
|
233
|
+
};
|
|
234
|
+
|
|
235
|
+
/** Build the synthetic SettingEntry for display.themeMode. */
|
|
236
|
+
export function buildThemeModeSyntheticEntry(configManager: Pick<ConfigManager, 'get'>): SettingEntry {
|
|
237
|
+
const currentValue = coerceThemeModeSetting(configManager.get(THEME_MODE_CONFIG_KEY as ConfigKey));
|
|
238
|
+
return {
|
|
239
|
+
setting: THEME_MODE_SYNTHETIC_SETTING,
|
|
240
|
+
currentValue,
|
|
241
|
+
isDefault: currentValue === THEME_MODE_DEFAULT,
|
|
242
|
+
};
|
|
243
|
+
}
|
|
244
|
+
|
|
182
245
|
// ---------------------------------------------------------------------------
|
|
183
246
|
// behavior.notifyAfterSeconds synthetic setting
|
|
184
247
|
// ---------------------------------------------------------------------------
|
|
@@ -224,6 +287,81 @@ export function buildNotifyAfterSecondsSyntheticEntry(configManager: Pick<Config
|
|
|
224
287
|
};
|
|
225
288
|
}
|
|
226
289
|
|
|
290
|
+
// ---------------------------------------------------------------------------
|
|
291
|
+
// W2.3 alert-class synthetic settings — behavior.notifyOn* + notifyOnlyWhenUnfocused
|
|
292
|
+
// ---------------------------------------------------------------------------
|
|
293
|
+
|
|
294
|
+
/**
|
|
295
|
+
* The five W2.3 alert-gating booleans, all TUI-local (not yet in the SDK
|
|
296
|
+
* ConfigKey union), all defaulting to on. Read/written generically by
|
|
297
|
+
* core/alert-gating.ts (readBooleanConfig) and the per-alert-class modules
|
|
298
|
+
* (budget-breach-notifier.ts, approval-alert.ts, turn-event-wiring.ts,
|
|
299
|
+
* long-task-notifier.ts) — this is only the settings-modal-visible surface.
|
|
300
|
+
*/
|
|
301
|
+
const NOTIFY_ALERT_SYNTHETIC_SETTINGS: ReadonlyArray<{ readonly key: string; readonly description: string }> = [
|
|
302
|
+
{
|
|
303
|
+
key: 'behavior.notifyOnBudgetBreach',
|
|
304
|
+
description: 'Alert when session cost crosses the configured budget (set via the Cost panel\'s "b" key).',
|
|
305
|
+
},
|
|
306
|
+
{
|
|
307
|
+
key: 'behavior.notifyOnAgentFailure',
|
|
308
|
+
description: 'Alert when a delegated or background agent fails.',
|
|
309
|
+
},
|
|
310
|
+
{
|
|
311
|
+
key: 'behavior.notifyOnChainFailure',
|
|
312
|
+
description: 'Alert when a WRFC review chain fails.',
|
|
313
|
+
},
|
|
314
|
+
{
|
|
315
|
+
key: 'behavior.notifyOnApprovalPending',
|
|
316
|
+
description: 'Alert when a tool call is waiting on your approval.',
|
|
317
|
+
},
|
|
318
|
+
{
|
|
319
|
+
key: 'behavior.notifyOnlyWhenUnfocused',
|
|
320
|
+
description: 'Master gate for the four alerts above: fire only when the terminal window is unfocused, or when focus state was never observed (terminal does not report focus). Turn off to always fire regardless of focus.',
|
|
321
|
+
},
|
|
322
|
+
];
|
|
323
|
+
|
|
324
|
+
function buildBooleanSyntheticEntry(
|
|
325
|
+
configManager: Pick<ConfigManager, 'get'>,
|
|
326
|
+
key: string,
|
|
327
|
+
description: string,
|
|
328
|
+
defaultValue: boolean,
|
|
329
|
+
): SettingEntry {
|
|
330
|
+
const raw = configManager.get(key as ConfigKey);
|
|
331
|
+
const currentValue = typeof raw === 'boolean' ? raw : defaultValue;
|
|
332
|
+
return {
|
|
333
|
+
setting: { key: key as ConfigKey, type: 'boolean', default: defaultValue, description },
|
|
334
|
+
currentValue,
|
|
335
|
+
isDefault: currentValue === defaultValue,
|
|
336
|
+
};
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
/** Build the five synthetic SettingEntry rows for the behavior category. */
|
|
340
|
+
export function buildNotifyAlertSyntheticEntries(configManager: Pick<ConfigManager, 'get'>): SettingEntry[] {
|
|
341
|
+
return NOTIFY_ALERT_SYNTHETIC_SETTINGS.map((spec) => buildBooleanSyntheticEntry(configManager, spec.key, spec.description, true));
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
// ---------------------------------------------------------------------------
|
|
345
|
+
// Wave 5 (wo804) — storage.codeIndexEnabled synthetic setting
|
|
346
|
+
// ---------------------------------------------------------------------------
|
|
347
|
+
|
|
348
|
+
/**
|
|
349
|
+
* The repo source-tree code index's auto-build-on-startup toggle
|
|
350
|
+
* (code-index-services.ts). TUI-local, default OFF: /codebase build is the
|
|
351
|
+
* explicit trigger unless a user opts in here. Honest bounds stated inline
|
|
352
|
+
* so enabling this isn't a surprise — see code-index-services.ts's
|
|
353
|
+
* CODE_INDEX_MAX_FILES/CODE_INDEX_MAX_FILE_BYTES for the numbers this
|
|
354
|
+
* description would otherwise duplicate as magic numbers.
|
|
355
|
+
*/
|
|
356
|
+
export function buildCodeIndexEnabledSyntheticEntry(configManager: Pick<ConfigManager, 'get'>): SettingEntry {
|
|
357
|
+
return buildBooleanSyntheticEntry(
|
|
358
|
+
configManager,
|
|
359
|
+
CODE_INDEX_ENABLED_CONFIG_KEY,
|
|
360
|
+
'Auto-build the repo source-tree code index on startup (bounded file/size scan; see /codebase status for bounds). Off by default — /codebase build indexes on demand.',
|
|
361
|
+
false,
|
|
362
|
+
);
|
|
363
|
+
}
|
|
364
|
+
|
|
227
365
|
// ---------------------------------------------------------------------------
|
|
228
366
|
// buildFlagEntries — snapshot of current feature flag states
|
|
229
367
|
// ---------------------------------------------------------------------------
|
|
@@ -306,6 +444,8 @@ export function refreshEntryValues(
|
|
|
306
444
|
// construction time so isDefault stays accurate.
|
|
307
445
|
if (entry.setting.key === ('tts.speed' as ConfigKey)) {
|
|
308
446
|
entry.currentValue = normalizeTtsSpeedValue(raw);
|
|
447
|
+
} else if (entry.setting.key === (THEME_MODE_CONFIG_KEY as ConfigKey)) {
|
|
448
|
+
entry.currentValue = coerceThemeModeSetting(raw);
|
|
309
449
|
} else {
|
|
310
450
|
entry.currentValue = raw;
|
|
311
451
|
}
|
|
@@ -327,8 +467,12 @@ export function updateEntryForKey(
|
|
|
327
467
|
const entry = entries.find((candidate) => candidate.setting.key === key);
|
|
328
468
|
if (entry) {
|
|
329
469
|
const raw = configManager.get(key);
|
|
330
|
-
// Synthetic
|
|
331
|
-
entry.currentValue = key === ('tts.speed' as ConfigKey)
|
|
470
|
+
// Synthetic entries: normalize using the same fallback logic as construction.
|
|
471
|
+
entry.currentValue = key === ('tts.speed' as ConfigKey)
|
|
472
|
+
? normalizeTtsSpeedValue(raw)
|
|
473
|
+
: key === (THEME_MODE_CONFIG_KEY as ConfigKey)
|
|
474
|
+
? coerceThemeModeSetting(raw)
|
|
475
|
+
: raw;
|
|
332
476
|
entry.isDefault = deepEqual(entry.currentValue, entry.setting.default);
|
|
333
477
|
}
|
|
334
478
|
}
|
|
@@ -113,13 +113,15 @@ export function persistFlagState(
|
|
|
113
113
|
if (newState === 'killed') return; // never persist killed state
|
|
114
114
|
|
|
115
115
|
try {
|
|
116
|
-
const current = (configManager.getCategory('featureFlags') as Record<string, PersistedFlagState>) ?? {};
|
|
117
116
|
if (newState === defaultState) {
|
|
118
|
-
|
|
117
|
+
// Back at the default: the override must be REMOVED, not merged.
|
|
118
|
+
// getCategory clones and mergeCategory only sets keys, so the old
|
|
119
|
+
// delete-then-merge approach left the stale override on disk and the
|
|
120
|
+
// flag silently reloaded in the overridden state on the next start.
|
|
121
|
+
configManager.removeCategoryKey('featureFlags', flagId);
|
|
119
122
|
} else {
|
|
120
|
-
|
|
123
|
+
configManager.mergeCategory('featureFlags', { [flagId]: newState } as Record<string, PersistedFlagState>);
|
|
121
124
|
}
|
|
122
|
-
configManager.mergeCategory('featureFlags', current);
|
|
123
125
|
} catch (e) {
|
|
124
126
|
logger.error('SettingsModal: failed to persist flag state', { flagId, error: summarizeError(e) });
|
|
125
127
|
}
|
|
@@ -11,11 +11,13 @@ export type SettingsCategory =
|
|
|
11
11
|
| 'storage'
|
|
12
12
|
| 'permissions'
|
|
13
13
|
| 'orchestration'
|
|
14
|
+
| 'planner'
|
|
14
15
|
| 'wrfc'
|
|
15
16
|
| 'tools'
|
|
16
17
|
| 'helper'
|
|
17
18
|
| 'tts'
|
|
18
19
|
| 'service'
|
|
20
|
+
| 'daemon'
|
|
19
21
|
| 'controlPlane'
|
|
20
22
|
| 'httpListener'
|
|
21
23
|
| 'web'
|
|
@@ -42,9 +44,9 @@ export const SETTINGS_CATEGORY_GROUPS: ReadonlyArray<{
|
|
|
42
44
|
}> = [
|
|
43
45
|
{ label: 'Interface', categories: ['display', 'ui', 'behavior', 'permissions'] },
|
|
44
46
|
{ label: 'AI Routing', categories: ['provider', 'subscriptions', 'helper', 'tools', 'tts'] },
|
|
45
|
-
{ label: 'Service & Network', categories: ['service', 'network', 'controlPlane', 'httpListener', 'web'] },
|
|
47
|
+
{ label: 'Service & Network', categories: ['service', 'daemon', 'network', 'controlPlane', 'httpListener', 'web'] },
|
|
46
48
|
{ label: 'Surfaces & Cloud', categories: ['surfaces', 'mcp', 'cloudflare'] },
|
|
47
|
-
{ label: 'Automation', categories: ['batch', 'automation', 'watchers', 'orchestration', 'wrfc'] },
|
|
49
|
+
{ label: 'Automation', categories: ['batch', 'automation', 'watchers', 'orchestration', 'planner', 'wrfc'] },
|
|
48
50
|
{ label: 'Runtime & Data', categories: ['storage', 'sandbox', 'runtime', 'cache', 'telemetry'] },
|
|
49
51
|
{ label: 'Advanced', categories: ['flags', 'release', 'danger'] },
|
|
50
52
|
];
|
package/src/main.ts
CHANGED
|
@@ -12,7 +12,7 @@ import { registerAllTools } from '@pellux/goodvibes-sdk/platform/tools';
|
|
|
12
12
|
import { FileUndoManager } from '@pellux/goodvibes-sdk/platform/state';
|
|
13
13
|
import { PermissionManager } from '@pellux/goodvibes-sdk/platform/permissions';
|
|
14
14
|
import { AcpManager } from '@pellux/goodvibes-sdk/platform/acp';
|
|
15
|
-
import { PermissionPromptUI } from './permissions/prompt.ts';
|
|
15
|
+
import { PermissionPromptUI, buildPendingPermissionExtras } from './permissions/prompt.ts';
|
|
16
16
|
import { CommandRegistry } from './input/command-registry.ts';
|
|
17
17
|
import type { CommandContext } from './input/command-registry.ts';
|
|
18
18
|
import { renderProcessIndicator } from './renderer/process-indicator.ts';
|
|
@@ -32,16 +32,11 @@ import { logger } from '@pellux/goodvibes-sdk/platform/utils';
|
|
|
32
32
|
import { registerBuiltinPanels } from './panels/builtin-panels.ts';
|
|
33
33
|
import { bootstrapRuntime } from './runtime/bootstrap.ts';
|
|
34
34
|
import type { BootstrapContext } from './runtime/bootstrap.ts';
|
|
35
|
+
import { buildSharedOrchestratorCoreServices } from './runtime/orchestrator-core-services.ts';
|
|
35
36
|
import type { HITLMode } from '@pellux/goodvibes-sdk/platform/state';
|
|
36
|
-
import {
|
|
37
|
-
checkRecoveryFile,
|
|
38
|
-
deleteRecoveryFile,
|
|
39
|
-
loadRecoveryConversation,
|
|
40
|
-
readLastSessionPointer,
|
|
41
|
-
writeRecoveryFile,
|
|
42
|
-
} from '@/runtime/index.ts';
|
|
37
|
+
import { readLastSessionPointer, writeRecoveryFile } from '@/runtime/index.ts';
|
|
43
38
|
import { handleBlockingShellInput, type PendingPermissionState } from './shell/blocking-input.ts';
|
|
44
|
-
import { createPersistRecoverySnapshot, createReopenRecoveryPanels, handleErrorAffordanceKey } from './shell/recovery-input-helpers.ts';
|
|
39
|
+
import { createPersistRecoverySnapshot, createRecoveryFileOps, createReopenRecoveryPanels, handleErrorAffordanceKey, resolveStartupRecoveryInfo } from './shell/recovery-input-helpers.ts';
|
|
45
40
|
import { wireShellUiOpeners } from './shell/ui-openers.ts';
|
|
46
41
|
import { deriveComposerState } from './core/composer-state.ts';
|
|
47
42
|
import { buildPersistedSessionContext, formatReturnContextForDisplay, getReturnContextMode, maybeAssistReturnContextSummary } from '@/runtime/index.ts';
|
|
@@ -57,17 +52,18 @@ import { installProcessLifecycle } from './runtime/process-lifecycle.ts';
|
|
|
57
52
|
import { createRenderScheduler } from './runtime/render-scheduler.ts';
|
|
58
53
|
import { buildCommandArgsHint } from './input/command-args-hint.ts';
|
|
59
54
|
import { summarizeRunningAgents } from './renderer/process-summary.ts';
|
|
55
|
+
import { footerFleetCost } from './panels/fleet-read-model.ts';
|
|
60
56
|
import { formatUserFacingErrorLine } from './core/format-user-error.ts';
|
|
61
57
|
import { wireStreamEventMetrics, type StreamMetrics, type WireStreamEventMetricsResult } from './core/stream-event-wiring.ts';
|
|
62
58
|
import { wireTurnEventHandlers } from './core/turn-event-wiring.ts';
|
|
63
59
|
import { buildContextStatusHint } from './renderer/context-status-hint.ts';
|
|
64
60
|
import { evaluateSessionMaintenance } from '@/runtime/index.ts';
|
|
61
|
+
import { createCancelGeneration } from './core/turn-cancellation.ts';
|
|
62
|
+
import { wrapRequestPermissionWithAlert } from './core/approval-alert.ts';
|
|
63
|
+
import { setPanelFrameRequester } from './panels/base-panel.ts';
|
|
65
64
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
const CURSOR_HIDE = '\x1b[?25l'; const CURSOR_SHOW = '\x1b[?25h'; const CLEAR_SCREEN = '\x1b[2J\x1b[3J\x1b[H';
|
|
69
|
-
const KEYBOARD_EXT_ENABLE = '\x1b[>4;2m' + '\x1b[?1u'; const KEYBOARD_EXT_DISABLE = '\x1b[>4;0m' + '\x1b[?1l';
|
|
70
|
-
const PASTE_ENABLE = '\x1b[?2004h'; const PASTE_DISABLE = '\x1b[?2004l';
|
|
65
|
+
import { ALT_SCREEN_ENTER, ALT_SCREEN_EXIT, MOUSE_ENABLE, MOUSE_DISABLE, CURSOR_HIDE, CURSOR_SHOW, CLEAR_SCREEN, KEYBOARD_EXT_ENABLE, KEYBOARD_EXT_DISABLE, PASTE_ENABLE, PASTE_DISABLE, FOCUS_ENABLE, FOCUS_DISABLE } from './renderer/terminal-escapes.ts';
|
|
66
|
+
import { installBackgroundThemeProbe } from './renderer/terminal-bg-probe.ts';
|
|
71
67
|
|
|
72
68
|
async function main() {
|
|
73
69
|
const stdout = process.stdout;
|
|
@@ -103,22 +99,17 @@ async function main() {
|
|
|
103
99
|
permissionPromptRef,
|
|
104
100
|
_writeLastSessionPointer: writeLastSessionPointer,
|
|
105
101
|
systemMessageRouter,
|
|
106
|
-
setOpenAgentDetail,
|
|
107
102
|
} = ctx;
|
|
108
103
|
const workingDir = ctx.services.workingDirectory;
|
|
109
104
|
const homeDirectory = ctx.services.homeDirectory;
|
|
110
105
|
const { approvalBroker, agentManager, modeManager, processManager, providerRegistry, secretsManager, subscriptionManager } = ctx.services;
|
|
111
106
|
conversation.setSessionMemoryStore(ctx.services.sessionMemoryStore);
|
|
112
107
|
conversation.setSessionLineageTracker(ctx.services.sessionLineageTracker);
|
|
108
|
+
// Shared payload (single source of truth, includes wo805's memoryRegistry —
|
|
109
|
+
// see orchestrator-core-services.ts) plus this site's favoritesStore.
|
|
113
110
|
orchestrator.setCoreServices({
|
|
114
|
-
configManager,
|
|
115
|
-
providerRegistry,
|
|
111
|
+
...buildSharedOrchestratorCoreServices({ services: ctx.services, configManager, providerRegistry }),
|
|
116
112
|
favoritesStore: ctx.services.favoritesStore,
|
|
117
|
-
planManager: ctx.services.planManager,
|
|
118
|
-
adaptivePlanner: ctx.services.adaptivePlanner,
|
|
119
|
-
sessionMemoryStore: ctx.services.sessionMemoryStore,
|
|
120
|
-
sessionLineageTracker: ctx.services.sessionLineageTracker,
|
|
121
|
-
idempotencyStore: ctx.services.idempotencyStore,
|
|
122
113
|
});
|
|
123
114
|
ctx.services.wrfcController.setPlanManager(ctx.services.planManager);
|
|
124
115
|
let activeConversationWidth = stdout.columns || 80;
|
|
@@ -213,13 +204,15 @@ async function main() {
|
|
|
213
204
|
let recoveryInterval: ReturnType<typeof setInterval> | null = null;
|
|
214
205
|
let stopSpokenOutputForExit: (() => void) | null = null;
|
|
215
206
|
let recoveryPending = false;
|
|
207
|
+
// Which file the current recovery prompt should load/delete from (see recovery-input-helpers.ts).
|
|
208
|
+
let recoverySource: 'live' | 'preserved' = 'live';
|
|
216
209
|
|
|
217
210
|
const lifecycle = installProcessLifecycle({
|
|
218
211
|
stdin,
|
|
219
212
|
stdout,
|
|
220
213
|
ctx,
|
|
221
214
|
noAltScreen: cli.flags.noAltScreen,
|
|
222
|
-
ansi: { CLEAR_SCREEN, ALT_SCREEN_EXIT, PASTE_DISABLE, KEYBOARD_EXT_DISABLE, MOUSE_DISABLE, CURSOR_SHOW },
|
|
215
|
+
ansi: { CLEAR_SCREEN, ALT_SCREEN_EXIT, PASTE_DISABLE, KEYBOARD_EXT_DISABLE, MOUSE_DISABLE, CURSOR_SHOW, FOCUS_DISABLE },
|
|
223
216
|
getInput: () => input,
|
|
224
217
|
render: () => renderScheduler.flushNow(), // resize: synchronous immediate path
|
|
225
218
|
getPromptContentWidth,
|
|
@@ -303,12 +296,7 @@ async function main() {
|
|
|
303
296
|
}
|
|
304
297
|
};
|
|
305
298
|
|
|
306
|
-
const cancelGeneration = ()
|
|
307
|
-
spokenTurns.stop('Spoken output stopped.');
|
|
308
|
-
if (orchestrator.isThinking) {
|
|
309
|
-
orchestrator.abort();
|
|
310
|
-
}
|
|
311
|
-
};
|
|
299
|
+
const cancelGeneration = createCancelGeneration(orchestrator, spokenTurns);
|
|
312
300
|
|
|
313
301
|
const jumpToBookmark = (key: string) => {
|
|
314
302
|
conversation.getDisplayBlocks();
|
|
@@ -337,6 +325,7 @@ async function main() {
|
|
|
337
325
|
commandContext.pasteFromClipboard = () => input.handlePaste();
|
|
338
326
|
commandContext.executeCommand = (name, args) => commandRegistry.execute(name, args, commandContext);
|
|
339
327
|
commandContext.cancelGeneration = cancelGeneration;
|
|
328
|
+
commandContext.isGenerating = () => orchestrator.isThinking;
|
|
340
329
|
commandContext.jumpToBookmark = jumpToBookmark;
|
|
341
330
|
commandContext.scrollToLine = scrollToLine;
|
|
342
331
|
commandContext.clearScreen = () => {
|
|
@@ -345,14 +334,14 @@ async function main() {
|
|
|
345
334
|
render();
|
|
346
335
|
};
|
|
347
336
|
commandContext.requestFullRepaint = () => { compositor.resetDiff(); render(); };
|
|
348
|
-
permissionPromptRef.requestPermission = (request) =>
|
|
337
|
+
permissionPromptRef.requestPermission = wrapRequestPermissionWithAlert((request) =>
|
|
349
338
|
new Promise((resolve) => {
|
|
350
339
|
pendingPermission = {
|
|
351
340
|
...request,
|
|
352
|
-
|
|
341
|
+
...buildPendingPermissionExtras(request, resolve),
|
|
353
342
|
};
|
|
354
343
|
render();
|
|
355
|
-
});
|
|
344
|
+
}), { focusTracker: ctx.services.focusTracker, configGet: (k: string) => configManager.get(k as Parameters<typeof configManager.get>[0]), webhookNotifier: ctx.services.webhookNotifier });
|
|
356
345
|
|
|
357
346
|
const input: InputHandler = new InputHandler(
|
|
358
347
|
() => render(),
|
|
@@ -385,7 +374,7 @@ async function main() {
|
|
|
385
374
|
replayEngine: ctx.services.replayEngine,
|
|
386
375
|
webhookNotifier: ctx.services.webhookNotifier,
|
|
387
376
|
policyRuntimeState: ctx.services.policyRuntimeState,
|
|
388
|
-
externalServices: uiServices.platform.externalServices,
|
|
377
|
+
externalServices: uiServices.platform.externalServices, focusTracker: ctx.services.focusTracker,
|
|
389
378
|
},
|
|
390
379
|
shell: {
|
|
391
380
|
bookmarkManager: ctx.services.bookmarkManager,
|
|
@@ -396,7 +385,7 @@ async function main() {
|
|
|
396
385
|
},
|
|
397
386
|
sessions: {
|
|
398
387
|
sessionManager: ctx.services.sessionManager,
|
|
399
|
-
sessionBroker:
|
|
388
|
+
sessionBroker: uiServices.sessions.sessionBroker,
|
|
400
389
|
sessionOrchestration: ctx.services.sessionOrchestration,
|
|
401
390
|
sessionMemoryStore: ctx.services.sessionMemoryStore,
|
|
402
391
|
},
|
|
@@ -415,9 +404,8 @@ async function main() {
|
|
|
415
404
|
input.setConversationManager(conversation);
|
|
416
405
|
input.setContentWidth(getPromptContentWidth());
|
|
417
406
|
input.filePicker.setOnUpdate(() => render());
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
setOpenAgentDetail((id) => input.agentDetailModal.open(id));
|
|
407
|
+
// W6.1 retirement: agentDetailModal/processModal setOnRefresh wiring removed —
|
|
408
|
+
// those modals were deleted (Fleet subsumes the live process tree via F2).
|
|
421
409
|
|
|
422
410
|
// Model picker callback is handled in bootstrap.ts — do not duplicate here.
|
|
423
411
|
input.setHistory(inputHistory);
|
|
@@ -482,7 +470,7 @@ async function main() {
|
|
|
482
470
|
.slice(0, promptInfo.visibleCursorLine)
|
|
483
471
|
.reduce((sum: number, line: string) => sum + line.length + 1, 0) + promptInfo.visibleCursorCol
|
|
484
472
|
: undefined,
|
|
485
|
-
usage: { up: orchestrator.usage.input, down: orchestrator.usage.output },
|
|
473
|
+
usage: { up: orchestrator.usage.input, down: orchestrator.usage.output, fleetCostUsd: footerFleetCost(() => ctx.services.processRegistry.query().nodes, runningAgentCount > 0) },
|
|
486
474
|
showExitNotice: input.showExitNotice,
|
|
487
475
|
lastCopyTime: input.lastCopyTime,
|
|
488
476
|
model: runtime.model,
|
|
@@ -509,8 +497,8 @@ async function main() {
|
|
|
509
497
|
lastInputTokens: orchestrator.lastInputTokens,
|
|
510
498
|
commandArgsHint,
|
|
511
499
|
hitlMode: modeManager.getHITLMode(),
|
|
512
|
-
|
|
513
|
-
runningProcessCount,
|
|
500
|
+
// S3d: cross-surface spine posture segment (adopted-daemon mode only).
|
|
501
|
+
sessionSpineStatus: (() => { const s = uiServices.platform.externalServices?.inspect(); return s?.sessionSpineActive && s.sessionSpineStatus && s.sessionSpineStatus !== 'unknown' ? s.sessionSpineStatus : undefined; })(), runningAgentCount, runningProcessCount,
|
|
514
502
|
// Composer must not read as focused while the panel/process indicator owns keyboard focus.
|
|
515
503
|
promptFocused: !input.panelFocused && !input.indicatorFocused,
|
|
516
504
|
indicatorFocused: input.indicatorFocused,
|
|
@@ -557,7 +545,7 @@ async function main() {
|
|
|
557
545
|
// Calculate how many rows are consumed by overlays (thinking, permissions, queue, file picker)
|
|
558
546
|
let overlayRows = 0;
|
|
559
547
|
if (orchestrator.isThinking) overlayRows += 2; // spinner + blank
|
|
560
|
-
if (pendingPermission) overlayRows += PermissionPromptUI.getPromptHeight(pendingPermission);
|
|
548
|
+
if (pendingPermission) overlayRows += PermissionPromptUI.getPromptHeight(pendingPermission, pendingPermission.hunkState, pendingPermission.detailsExpanded);
|
|
561
549
|
overlayRows += orchestrator.messageQueue.length * 3; // queued messages
|
|
562
550
|
// File picker and model picker overlay rows computed from actual rendered line count below
|
|
563
551
|
// Selection modal overlay rows are computed from actual rendered line count below
|
|
@@ -596,6 +584,7 @@ async function main() {
|
|
|
596
584
|
turnElapsedMs,
|
|
597
585
|
streamMetrics.ttftMs,
|
|
598
586
|
stallInfo,
|
|
587
|
+
pendingPermission !== null,
|
|
599
588
|
);
|
|
600
589
|
viewport.push(...thinking);
|
|
601
590
|
// Live tool timer: render the currently executing tool row with ticking elapsed.
|
|
@@ -606,7 +595,7 @@ async function main() {
|
|
|
606
595
|
}
|
|
607
596
|
|
|
608
597
|
if (pendingPermission) {
|
|
609
|
-
viewport.push(...PermissionPromptUI.createPromptLines(conversationWidth, pendingPermission));
|
|
598
|
+
viewport.push(...PermissionPromptUI.createPromptLines(conversationWidth, pendingPermission, pendingPermission.hunkState, pendingPermission.detailsExpanded));
|
|
610
599
|
}
|
|
611
600
|
|
|
612
601
|
orchestrator.messageQueue.forEach(msg => {
|
|
@@ -653,10 +642,11 @@ async function main() {
|
|
|
653
642
|
});
|
|
654
643
|
};
|
|
655
644
|
const renderScheduler = createRenderScheduler(renderNow); // WO-208 same-tick coalescer
|
|
656
|
-
const render = (): void => renderScheduler.schedule();
|
|
657
|
-
const terminalOutputGuard = installTuiTerminalOutputGuard({ stdout, stderr: process.stderr,
|
|
645
|
+
const render = (): void => renderScheduler.schedule(); // captured direct writes → activity log + quiet /debug counter, not repeated transcript lines (UX-B 1a)
|
|
646
|
+
const terminalOutputGuard = installTuiTerminalOutputGuard({ stdout, stderr: process.stderr, onCapture: (total) => { commandContext.session.runtime.terminalWritesIntercepted = total; render(); } });
|
|
658
647
|
|
|
659
648
|
setRenderRequest(renderNow); // bootstrap's 16ms coalescer calls the composite directly
|
|
649
|
+
setPanelFrameRequester(render); // live panels repaint when idle (Wave-6 replay: fleet sat stale until keypress)
|
|
660
650
|
orchestratorRefs.requestRender = render;
|
|
661
651
|
commandContext.renderRequest = render;
|
|
662
652
|
wireShellUiOpeners({
|
|
@@ -672,6 +662,7 @@ async function main() {
|
|
|
672
662
|
subscriptionManager,
|
|
673
663
|
secretsManager,
|
|
674
664
|
serviceRegistry: ctx.services.serviceRegistry,
|
|
665
|
+
memoryEmbeddingRegistry: ctx.services.memoryEmbeddingRegistry,
|
|
675
666
|
workingDirectory: workingDir,
|
|
676
667
|
homeDirectory,
|
|
677
668
|
getConfiguredProviderIds: ctx._getConfiguredProviderIds,
|
|
@@ -694,7 +685,7 @@ async function main() {
|
|
|
694
685
|
gitStatusProvider,
|
|
695
686
|
lastGitInfoRef,
|
|
696
687
|
buildSessionContinuityHints,
|
|
697
|
-
render, webhookNotifier: ctx.services.webhookNotifier,
|
|
688
|
+
render, webhookNotifier: ctx.services.webhookNotifier, focusTracker: ctx.services.focusTracker,
|
|
698
689
|
});
|
|
699
690
|
unsubs.push(...turnUnsubs);
|
|
700
691
|
|
|
@@ -714,6 +705,7 @@ async function main() {
|
|
|
714
705
|
events: uiServices.events, orchestrator, providerRegistry,
|
|
715
706
|
systemMessageRouter, render, metrics: streamMetrics,
|
|
716
707
|
providerOptimizer: ctx.services.providerOptimizer, costLookup: providerRegistry, retryTurn,
|
|
708
|
+
isApprovalPending: () => pendingPermission !== null,
|
|
717
709
|
});
|
|
718
710
|
unsubs.push(...streamResult.unsubs);
|
|
719
711
|
// Activate one-key retry affordance when a user-visible error surfaces.
|
|
@@ -736,16 +728,19 @@ async function main() {
|
|
|
736
728
|
stdin.setRawMode(true);
|
|
737
729
|
stdin.resume();
|
|
738
730
|
stdin.setEncoding('utf8');
|
|
739
|
-
allowTerminalWrite(() => stdout.write((cli.flags.noAltScreen ? '' : ALT_SCREEN_ENTER) + CLEAR_SCREEN + CURSOR_HIDE + MOUSE_ENABLE + KEYBOARD_EXT_ENABLE + PASTE_ENABLE));
|
|
731
|
+
allowTerminalWrite(() => stdout.write((cli.flags.noAltScreen ? '' : ALT_SCREEN_ENTER) + CLEAR_SCREEN + CURSOR_HIDE + MOUSE_ENABLE + KEYBOARD_EXT_ENABLE + PASTE_ENABLE + FOCUS_ENABLE));
|
|
732
|
+
// DEBT-2: forced dark/light applies before first paint; auto (TTY only) fires the
|
|
733
|
+
// OSC 11 probe and repaints once if light wins. filterInput strips the reply from stdin.
|
|
734
|
+
const themeProbe = installBackgroundThemeProbe({ configManager, isTTY: Boolean(stdout.isTTY), env: process.env, writeQuery: (b) => allowTerminalWrite(() => stdout.write(b)), requestRepaint: () => { compositor.resetDiff(); render(); } });
|
|
740
735
|
|
|
741
736
|
applyInitialTuiCliState({ cli, input, commandRegistry, commandContext, shellPaths: ctx.services.shellPaths, render });
|
|
742
737
|
|
|
743
|
-
stdin.on('data', (
|
|
738
|
+
stdin.on('data', (raw: string) => {
|
|
739
|
+
const data = themeProbe.filterInput(raw); if (data.length === 0) return;
|
|
744
740
|
const blocking = handleBlockingShellInput({
|
|
745
741
|
data, pendingPermission, recoveryPending, conversation, systemMessageRouter, render,
|
|
746
742
|
abortTurn: () => orchestrator.abort(),
|
|
747
|
-
|
|
748
|
-
deleteRecoveryFile: () => deleteRecoveryFile({ homeDirectory }),
|
|
743
|
+
...createRecoveryFileOps(() => recoverySource, { homeDirectory }),
|
|
749
744
|
homeDirectory, sessionId: runtime.sessionId,
|
|
750
745
|
persistSnapshot: createPersistRecoverySnapshot({ sessionManager: ctx.services.sessionManager, runtime, conversation }),
|
|
751
746
|
reopenPanels: createReopenRecoveryPanels({ panelManager, render }),
|
|
@@ -772,9 +767,10 @@ async function main() {
|
|
|
772
767
|
conversation.rebuildHistory();
|
|
773
768
|
render();
|
|
774
769
|
|
|
775
|
-
// --- Crash recovery check ---
|
|
776
|
-
const recoveryInfo =
|
|
770
|
+
// --- Crash recovery check (also checks the preserve-on-dismiss sibling; see recovery-input-helpers.ts) ---
|
|
771
|
+
const recoveryInfo = resolveStartupRecoveryInfo({ workingDirectory: workingDir, homeDirectory });
|
|
777
772
|
if (recoveryInfo) {
|
|
773
|
+
recoverySource = recoveryInfo.source;
|
|
778
774
|
systemMessageRouter.high(`[Recovery] Found unsaved session from ${new Date(recoveryInfo.timestamp).toLocaleString()}. Title: "${recoveryInfo.title}". Press Ctrl+R to restore, Esc to discard, or start typing to ignore it.`);
|
|
779
775
|
for (const line of formatReturnContextForDisplay(recoveryInfo.returnContext)) {
|
|
780
776
|
systemMessageRouter.low(`[Recovery] ${line}`);
|
|
@@ -785,6 +781,8 @@ async function main() {
|
|
|
785
781
|
|
|
786
782
|
// --- Auto-save to recovery file every 60s ---
|
|
787
783
|
recoveryInterval = setInterval(() => {
|
|
784
|
+
// Skip while an earlier recovery prompt is unresolved (avoids racing preserve-on-dismiss).
|
|
785
|
+
if (recoveryPending) return;
|
|
788
786
|
const snapshot = conversation.toJSON() as { messages: Array<import('./core/conversation.ts').ConversationMessageSnapshot> };
|
|
789
787
|
const persisted = buildPersistedSessionContext(snapshot.messages, conversation.getTitleSource(), buildSessionContinuityHints());
|
|
790
788
|
writeRecoveryFile(
|
|
@@ -7,7 +7,8 @@ export type AgentInspectorEntryKind = 'user' | 'assistant' | 'tool_call' | 'tool
|
|
|
7
7
|
|
|
8
8
|
// ---------------------------------------------------------------------------
|
|
9
9
|
// Shared agent status / stall constants
|
|
10
|
-
// Used by
|
|
10
|
+
// Used by the Fleet panel's agent views and read-model consumers (formerly also
|
|
11
|
+
// AgentDetailModal, which was retired in W6.1).
|
|
11
12
|
// ---------------------------------------------------------------------------
|
|
12
13
|
|
|
13
14
|
/** Terminal statuses — cancel not offered; stall check skipped. */
|
package/src/panels/base-panel.ts
CHANGED
|
@@ -9,6 +9,20 @@ import { fitDisplay } from '../utils/terminal-width.ts';
|
|
|
9
9
|
/** Canonical error-surface foreground (bad/red), kept out of the render body. */
|
|
10
10
|
const ERROR_FG = '#ef4444';
|
|
11
11
|
|
|
12
|
+
/**
|
|
13
|
+
* Frame requester wired by main.ts to the render scheduler. Without it,
|
|
14
|
+
* markDirty() only sets a flag the compositor reads when a frame is ALREADY
|
|
15
|
+
* being composed for another reason (input, streaming) — so a live panel
|
|
16
|
+
* (fleet ticks, registry subscriptions) sat visibly stale while the app was
|
|
17
|
+
* idle until the next keypress (Wave-6 replay finding). The scheduler
|
|
18
|
+
* coalesces same-tick requests, so this stays cheap.
|
|
19
|
+
*/
|
|
20
|
+
let panelFrameRequester: (() => void) | null = null;
|
|
21
|
+
|
|
22
|
+
export function setPanelFrameRequester(requester: (() => void) | null): void {
|
|
23
|
+
panelFrameRequester = requester;
|
|
24
|
+
}
|
|
25
|
+
|
|
12
26
|
export abstract class BasePanel implements Panel {
|
|
13
27
|
public needsRender = true;
|
|
14
28
|
public isTransient = false;
|
|
@@ -219,7 +233,14 @@ export abstract class BasePanel implements Panel {
|
|
|
219
233
|
/** R2: Called by the compositor after a successful render to clear the dirty flag. */
|
|
220
234
|
public markRendered(): void { this.needsRender = false; }
|
|
221
235
|
|
|
222
|
-
protected markDirty(): void {
|
|
236
|
+
protected markDirty(): void {
|
|
237
|
+
// Request only on the false→true transition: a pending frame already
|
|
238
|
+
// covers repeat marks, and a panel that dirties itself mid-render must
|
|
239
|
+
// not schedule frames forever.
|
|
240
|
+
const wasDirty = this.needsRender;
|
|
241
|
+
this.needsRender = true;
|
|
242
|
+
if (!wasDirty) panelFrameRequester?.();
|
|
243
|
+
}
|
|
223
244
|
|
|
224
245
|
/**
|
|
225
246
|
* Check whether the panel is currently permitted to render.
|