@pellux/goodvibes-tui 1.16.0 → 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 +95 -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/onboarding/onboarding-wizard.ts +22 -8
- 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 +102 -24
- 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
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import type { ConfigSetting } from '@pellux/goodvibes-sdk/platform/config';
|
|
2
2
|
import type { ProviderAuthFreshness, ProviderAuthRoute } from '@/runtime/index.ts';
|
|
3
|
-
import type { FeatureFlag, FlagState } from '@/runtime/index.ts';
|
|
3
|
+
import type { FeatureFlag, FeatureSetting, FlagState } from '@/runtime/index.ts';
|
|
4
4
|
|
|
5
5
|
export type SettingsCategory =
|
|
6
6
|
| 'display'
|
|
7
7
|
| 'ui'
|
|
8
8
|
| 'provider'
|
|
9
|
+
| 'pricing'
|
|
9
10
|
| 'subscriptions'
|
|
10
11
|
| 'behavior'
|
|
11
12
|
| 'storage'
|
|
@@ -43,8 +44,8 @@ export type SettingsCategory =
|
|
|
43
44
|
| 'integrations'
|
|
44
45
|
| 'policy'
|
|
45
46
|
| 'notifications'
|
|
46
|
-
| 'flags'
|
|
47
47
|
| 'release'
|
|
48
|
+
| 'update'
|
|
48
49
|
| 'danger';
|
|
49
50
|
|
|
50
51
|
export type SettingsFocusPane = 'categories' | 'settings';
|
|
@@ -54,12 +55,12 @@ export const SETTINGS_CATEGORY_GROUPS: ReadonlyArray<{
|
|
|
54
55
|
readonly categories: readonly SettingsCategory[];
|
|
55
56
|
}> = [
|
|
56
57
|
{ label: 'Interface', categories: ['display', 'ui', 'behavior', 'notifications', 'permissions', 'policy', 'security'] },
|
|
57
|
-
{ label: 'AI Routing', categories: ['provider', 'subscriptions', 'helper', 'tools', 'tts'] },
|
|
58
|
+
{ label: 'AI Routing', categories: ['provider', 'pricing', 'subscriptions', 'helper', 'tools', 'tts'] },
|
|
58
59
|
{ label: 'Service & Network', categories: ['service', 'daemon', 'network', 'controlPlane', 'httpListener', 'web', 'relay'] },
|
|
59
60
|
{ label: 'Surfaces & Cloud', categories: ['surfaces', 'integrations', 'mcp', 'cloudflare'] },
|
|
60
61
|
{ label: 'Automation', categories: ['batch', 'automation', 'checkin', 'watchers', 'orchestration', 'planner', 'wrfc'] },
|
|
61
62
|
{ label: 'Runtime & Data', categories: ['storage', 'atRest', 'sandbox', 'fetch', 'agents', 'runtime', 'cache', 'telemetry', 'diagnostics', 'learning'] },
|
|
62
|
-
{ label: 'Advanced', categories: ['
|
|
63
|
+
{ label: 'Advanced', categories: ['release', 'update', 'danger'] },
|
|
63
64
|
];
|
|
64
65
|
|
|
65
66
|
export const SETTINGS_CATEGORIES: SettingsCategory[] = SETTINGS_CATEGORY_GROUPS.flatMap(group => group.categories);
|
|
@@ -74,34 +75,40 @@ export interface SettingEntry {
|
|
|
74
75
|
sourceLabel?: string;
|
|
75
76
|
lockReason?: string;
|
|
76
77
|
/**
|
|
77
|
-
* Present when this row is a feature-unit
|
|
78
|
-
* rendered as one unit with
|
|
79
|
-
*
|
|
80
|
-
*
|
|
78
|
+
* Present when this row is a feature-unit header (a platform capability
|
|
79
|
+
* rendered as one unit with the settings that tune it beneath). The row IS
|
|
80
|
+
* the real config row for the feature's enablement key — boolean headers
|
|
81
|
+
* toggle and enum headers cycle their mode choices through the ordinary
|
|
82
|
+
* settings write path (a plain config write; the settings bridge keeps the
|
|
83
|
+
* runtime gate in sync).
|
|
81
84
|
*/
|
|
82
85
|
flag?: FlagEntry;
|
|
83
86
|
/**
|
|
84
|
-
* Present when this row is a
|
|
85
|
-
* id of the owning
|
|
86
|
-
* marks the row as claimed so it does not double-list as an
|
|
87
|
+
* Present when this row is a settings sub-option OWNED by a feature unit —
|
|
88
|
+
* the id of the owning feature. Drives the indented rendering beneath its
|
|
89
|
+
* header and marks the row as claimed so it does not double-list as an
|
|
90
|
+
* orphan.
|
|
87
91
|
*/
|
|
88
92
|
ownerFlagId?: string;
|
|
89
93
|
}
|
|
90
94
|
|
|
91
95
|
export interface FlagEntry {
|
|
96
|
+
/** The per-feature settings metadata (domain, enablement shape, settings keys, real description). */
|
|
97
|
+
feature: FeatureSetting;
|
|
98
|
+
/** The internal gate declaration (kill state/reason live here). */
|
|
92
99
|
flag: FeatureFlag;
|
|
93
100
|
/** Live effective state as the runtime currently sees it. */
|
|
94
101
|
state: FlagState;
|
|
95
102
|
/**
|
|
96
103
|
* Last known config-layer (persisted) value. Equals `state` unless a
|
|
97
|
-
* startup-gated
|
|
98
|
-
* will take effect on the next launch.
|
|
104
|
+
* startup-gated feature was changed this session — then it holds the value
|
|
105
|
+
* that will take effect on the next launch.
|
|
99
106
|
*/
|
|
100
107
|
persistedState: FlagState;
|
|
101
108
|
/**
|
|
102
|
-
* True when a startup-gated
|
|
103
|
-
* state, i.e. a restart is required before the change takes
|
|
104
|
-
* from FeatureFlagManager.getAll(), never guessed.
|
|
109
|
+
* True when a startup-gated feature's persisted value differs from its
|
|
110
|
+
* effective state, i.e. a restart is required before the change takes
|
|
111
|
+
* effect. Sourced from FeatureFlagManager.getAll(), never guessed.
|
|
105
112
|
*/
|
|
106
113
|
pendingRestart: boolean;
|
|
107
114
|
}
|
|
@@ -5,9 +5,12 @@
|
|
|
5
5
|
* - Active category (Tab to cycle)
|
|
6
6
|
* - Selected setting index within category (↑↓)
|
|
7
7
|
* - Editing mode for inline string/number input
|
|
8
|
-
* - Feature
|
|
8
|
+
* - Feature-unit headers (each capability's real enablement row in its domain)
|
|
9
9
|
*
|
|
10
|
-
*
|
|
10
|
+
* Every save is a plain configManager.set(key, value) on a domain settings
|
|
11
|
+
* key; the SDK settings bridge keeps the runtime gate manager in sync, and
|
|
12
|
+
* this modal re-reads the manager after enablement writes so pending-restart
|
|
13
|
+
* markers are shown honestly at the point of change.
|
|
11
14
|
*
|
|
12
15
|
* Data assembly delegates to settings-modal-data.ts (buildSettingGroups, etc.).
|
|
13
16
|
* Mutation logic delegates to settings-modal-mutations.ts (applySettingValue, etc.).
|
|
@@ -41,7 +44,6 @@ import {
|
|
|
41
44
|
} from './settings-modal-types.ts';
|
|
42
45
|
import {
|
|
43
46
|
buildSettingGroups,
|
|
44
|
-
buildFlagEntries,
|
|
45
47
|
buildMcpEntries,
|
|
46
48
|
buildSubscriptionEntries,
|
|
47
49
|
buildNetworkFilteredItems,
|
|
@@ -51,9 +53,10 @@ import {
|
|
|
51
53
|
import { getSettingLabel } from '../renderer/settings-modal-helpers.ts';
|
|
52
54
|
import {
|
|
53
55
|
applySettingValue,
|
|
54
|
-
|
|
56
|
+
syncFlagEntryFromManager,
|
|
55
57
|
type SettingAppliedCallback,
|
|
56
58
|
} from './settings-modal-mutations.ts';
|
|
59
|
+
import { featureEnablementWrite, isFeatureValueEnabled } from '../runtime/feature-settings.ts';
|
|
57
60
|
import {
|
|
58
61
|
activateSelected as _activateSelected,
|
|
59
62
|
handleSubscriptionLogoutKey as _handleSubscriptionLogoutKey,
|
|
@@ -117,6 +120,13 @@ export class SettingsModal {
|
|
|
117
120
|
|
|
118
121
|
/** Current value of the inline edit buffer. */
|
|
119
122
|
public editBuffer = '';
|
|
123
|
+
/**
|
|
124
|
+
* Scroll offset (in wrapped lines) of the documentation pane for the
|
|
125
|
+
* selected row. The renderer clamps it to the pane's real content height
|
|
126
|
+
* and shows honest more-above/below markers — long feature documentation
|
|
127
|
+
* scrolls, it never clips. Reset whenever the selection changes.
|
|
128
|
+
*/
|
|
129
|
+
public contextScroll = 0;
|
|
120
130
|
/** Server awaiting explicit allow-all confirmation, if any. */
|
|
121
131
|
public mcpAllowAllConfirmationTarget: string | null = null;
|
|
122
132
|
/**
|
|
@@ -140,8 +150,6 @@ export class SettingsModal {
|
|
|
140
150
|
/** Settings grouped by category. */
|
|
141
151
|
public groups: Map<SettingsCategory, SettingEntry[]> = new Map();
|
|
142
152
|
|
|
143
|
-
/** Feature flag entries (populated when flags tab is active). */
|
|
144
|
-
public flagEntries: FlagEntry[] = [];
|
|
145
153
|
/** MCP server trust entries (populated when mcp tab is active). */
|
|
146
154
|
public mcpEntries: McpEntry[] = [];
|
|
147
155
|
/** Provider subscription entries (populated when subscriptions tab is active). */
|
|
@@ -207,7 +215,6 @@ export class SettingsModal {
|
|
|
207
215
|
this.mcpRegistry = mcpRegistry ?? null;
|
|
208
216
|
this.onSettingApplied = options?.onSettingApplied ?? null;
|
|
209
217
|
this.groups = buildSettingGroups(configManager, featureFlagManager);
|
|
210
|
-
this.flagEntries = buildFlagEntries(featureFlagManager);
|
|
211
218
|
this.mcpEntries = buildMcpEntries(this.mcpRegistry);
|
|
212
219
|
this.subscriptionEntries = buildSubscriptionEntries(subscriptionManager, serviceRegistry);
|
|
213
220
|
this.categoryIndex = 0;
|
|
@@ -225,9 +232,15 @@ export class SettingsModal {
|
|
|
225
232
|
this.searchQuery = '';
|
|
226
233
|
this.searchResults = [];
|
|
227
234
|
this.searchFocused = false;
|
|
235
|
+
this.contextScroll = 0;
|
|
228
236
|
this.active = true;
|
|
229
237
|
}
|
|
230
238
|
|
|
239
|
+
/** Scroll the documentation pane by `delta` wrapped lines (renderer clamps the top end). */
|
|
240
|
+
scrollContext(delta: number): void {
|
|
241
|
+
this.contextScroll = Math.max(0, this.contextScroll + delta);
|
|
242
|
+
}
|
|
243
|
+
|
|
231
244
|
close(): void {
|
|
232
245
|
this.active = false;
|
|
233
246
|
this.editingMode = false;
|
|
@@ -242,6 +255,7 @@ export class SettingsModal {
|
|
|
242
255
|
this.searchQuery = '';
|
|
243
256
|
this.searchResults = [];
|
|
244
257
|
this.searchFocused = false;
|
|
258
|
+
this.contextScroll = 0;
|
|
245
259
|
this.serviceRegistry = null;
|
|
246
260
|
this.secretsManager = null;
|
|
247
261
|
this.onSettingApplied = null;
|
|
@@ -266,6 +280,7 @@ export class SettingsModal {
|
|
|
266
280
|
setSearchQuery(query: string): void {
|
|
267
281
|
this.searchQuery = query;
|
|
268
282
|
this.searchFocused = true;
|
|
283
|
+
this.contextScroll = 0;
|
|
269
284
|
if (query.trim().length === 0) {
|
|
270
285
|
this.searchResults = [];
|
|
271
286
|
} else {
|
|
@@ -286,6 +301,7 @@ export class SettingsModal {
|
|
|
286
301
|
if (this.editingMode) return;
|
|
287
302
|
this.categoryIndex = (this.categoryIndex + 1) % SETTINGS_CATEGORIES.length;
|
|
288
303
|
this.selectedIndex = 0;
|
|
304
|
+
this.contextScroll = 0;
|
|
289
305
|
this.subscriptionLogoutConfirmationTarget = null;
|
|
290
306
|
this._reloadTabEntries();
|
|
291
307
|
}
|
|
@@ -295,6 +311,7 @@ export class SettingsModal {
|
|
|
295
311
|
if (this.editingMode) return;
|
|
296
312
|
this.categoryIndex = (this.categoryIndex - 1 + SETTINGS_CATEGORIES.length) % SETTINGS_CATEGORIES.length;
|
|
297
313
|
this.selectedIndex = 0;
|
|
314
|
+
this.contextScroll = 0;
|
|
298
315
|
this.subscriptionLogoutConfirmationTarget = null;
|
|
299
316
|
this._reloadTabEntries();
|
|
300
317
|
}
|
|
@@ -326,6 +343,7 @@ export class SettingsModal {
|
|
|
326
343
|
|
|
327
344
|
moveUp(): void {
|
|
328
345
|
if (this.editingMode) return;
|
|
346
|
+
this.contextScroll = 0;
|
|
329
347
|
if (this.searchFocused) {
|
|
330
348
|
if (this.searchResults.length > 0) {
|
|
331
349
|
this.selectedIndex = (this.selectedIndex - 1 + this.searchResults.length) % this.searchResults.length;
|
|
@@ -334,9 +352,7 @@ export class SettingsModal {
|
|
|
334
352
|
}
|
|
335
353
|
const items = this._currentItems();
|
|
336
354
|
if (items.length === 0) {
|
|
337
|
-
if (this.currentCategory === '
|
|
338
|
-
this.selectedIndex = (this.selectedIndex - 1 + this.flagEntries.length) % this.flagEntries.length;
|
|
339
|
-
} else if (this.currentCategory === 'mcp' && this.mcpEntries.length > 0) {
|
|
355
|
+
if (this.currentCategory === 'mcp' && this.mcpEntries.length > 0) {
|
|
340
356
|
this.selectedIndex = (this.selectedIndex - 1 + this.mcpEntries.length) % this.mcpEntries.length;
|
|
341
357
|
} else if (this.currentCategory === 'subscriptions' && this.subscriptionEntries.length > 0) {
|
|
342
358
|
this.selectedIndex = (this.selectedIndex - 1 + this.subscriptionEntries.length) % this.subscriptionEntries.length;
|
|
@@ -350,6 +366,7 @@ export class SettingsModal {
|
|
|
350
366
|
|
|
351
367
|
moveDown(): void {
|
|
352
368
|
if (this.editingMode) return;
|
|
369
|
+
this.contextScroll = 0;
|
|
353
370
|
if (this.searchFocused) {
|
|
354
371
|
if (this.searchResults.length > 0) {
|
|
355
372
|
this.selectedIndex = (this.selectedIndex + 1) % this.searchResults.length;
|
|
@@ -358,9 +375,7 @@ export class SettingsModal {
|
|
|
358
375
|
}
|
|
359
376
|
const items = this._currentItems();
|
|
360
377
|
if (items.length === 0) {
|
|
361
|
-
if (this.currentCategory === '
|
|
362
|
-
this.selectedIndex = (this.selectedIndex + 1) % this.flagEntries.length;
|
|
363
|
-
} else if (this.currentCategory === 'mcp' && this.mcpEntries.length > 0) {
|
|
378
|
+
if (this.currentCategory === 'mcp' && this.mcpEntries.length > 0) {
|
|
364
379
|
this.selectedIndex = (this.selectedIndex + 1) % this.mcpEntries.length;
|
|
365
380
|
} else if (this.currentCategory === 'subscriptions' && this.subscriptionEntries.length > 0) {
|
|
366
381
|
this.selectedIndex = (this.selectedIndex + 1) % this.subscriptionEntries.length;
|
|
@@ -414,6 +429,7 @@ export class SettingsModal {
|
|
|
414
429
|
selectTarget(target?: string): void {
|
|
415
430
|
const normalized = target?.trim();
|
|
416
431
|
if (!normalized) return;
|
|
432
|
+
this.contextScroll = 0;
|
|
417
433
|
|
|
418
434
|
const categoryIndex = SETTINGS_CATEGORIES.indexOf(normalized as SettingsCategory);
|
|
419
435
|
if (categoryIndex >= 0) {
|
|
@@ -493,38 +509,21 @@ export class SettingsModal {
|
|
|
493
509
|
}
|
|
494
510
|
|
|
495
511
|
/**
|
|
496
|
-
* Toggle the currently selected feature
|
|
497
|
-
*
|
|
498
|
-
*
|
|
499
|
-
*
|
|
512
|
+
* Toggle the currently selected feature unit through its real domain
|
|
513
|
+
* settings key: boolean/constant enablements flip the key, enum enablements
|
|
514
|
+
* jump between the feature's stock active mode and its off mode. Killed
|
|
515
|
+
* features cannot be toggled; constant capabilities with no off position
|
|
516
|
+
* (non-boolean keys) are left to their domain settings.
|
|
500
517
|
*/
|
|
501
518
|
toggleSelectedFlag(): void {
|
|
502
519
|
const entry = this.getSelected();
|
|
503
520
|
if (!entry?.flag) return;
|
|
504
521
|
if (entry.flag.state === 'killed') return;
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
* header entry, applies the desired state through the flag manager (runtime +
|
|
511
|
-
* persisted override), and refreshes the header row's value/default marker in
|
|
512
|
-
* place. Killed flags and no-op toggles are ignored.
|
|
513
|
-
*/
|
|
514
|
-
private _toggleFlagValue(key: ConfigKey, value: unknown): void {
|
|
515
|
-
if (!this.featureFlagManager || !this.configManager) return;
|
|
516
|
-
let target: SettingEntry | null = null;
|
|
517
|
-
for (const entries of this.groups.values()) {
|
|
518
|
-
const candidate = entries.find((e) => e.setting.key === key && e.flag);
|
|
519
|
-
if (candidate) { target = candidate; break; }
|
|
520
|
-
}
|
|
521
|
-
if (!target?.flag) return;
|
|
522
|
-
if (target.flag.state === 'killed') return;
|
|
523
|
-
const desired = value ? 'enabled' : 'disabled';
|
|
524
|
-
if (target.flag.state === desired) return;
|
|
525
|
-
applyFlagState(target.flag, desired, this.featureFlagManager, this.configManager);
|
|
526
|
-
target.currentValue = target.flag.state === 'enabled';
|
|
527
|
-
target.isDefault = target.flag.state === target.flag.flag.defaultState;
|
|
522
|
+
const feature = entry.flag.feature;
|
|
523
|
+
const currentlyOn = isFeatureValueEnabled(feature, entry.currentValue);
|
|
524
|
+
const write = featureEnablementWrite(feature.id, !currentlyOn);
|
|
525
|
+
if (!write) return;
|
|
526
|
+
this._setValue(write.key, write.value);
|
|
528
527
|
}
|
|
529
528
|
|
|
530
529
|
/**
|
|
@@ -586,6 +585,18 @@ export class SettingsModal {
|
|
|
586
585
|
}
|
|
587
586
|
}
|
|
588
587
|
|
|
588
|
+
if (setting.type === 'object') {
|
|
589
|
+
// Object-typed keys (e.g. pricing.modelPrices) edit as JSON; the
|
|
590
|
+
// schema's validate() below still rules on the parsed shape.
|
|
591
|
+
try {
|
|
592
|
+
parsed = JSON.parse(this.editBuffer);
|
|
593
|
+
} catch {
|
|
594
|
+
this.editingMode = false;
|
|
595
|
+
this.editBuffer = '';
|
|
596
|
+
return false;
|
|
597
|
+
}
|
|
598
|
+
}
|
|
599
|
+
|
|
589
600
|
if (setting.validate && !setting.validate(parsed)) {
|
|
590
601
|
this.editingMode = false;
|
|
591
602
|
this.editBuffer = '';
|
|
@@ -685,11 +696,9 @@ export class SettingsModal {
|
|
|
685
696
|
|
|
686
697
|
// ── Private helpers ────────────────────────────────────────────────────────────────
|
|
687
698
|
|
|
688
|
-
/** Reload
|
|
699
|
+
/** Reload mcp/subscription entries when the active tab changes. */
|
|
689
700
|
private _reloadTabEntries(): void {
|
|
690
|
-
if (this.currentCategory === '
|
|
691
|
-
this.flagEntries = buildFlagEntries(this.featureFlagManager);
|
|
692
|
-
} else if (this.currentCategory === 'mcp') {
|
|
701
|
+
if (this.currentCategory === 'mcp') {
|
|
693
702
|
this.mcpEntries = buildMcpEntries(this.mcpRegistry);
|
|
694
703
|
} else if (this.currentCategory === 'subscriptions') {
|
|
695
704
|
this.subscriptionEntries = buildSubscriptionEntries(this.subscriptionManager, this.serviceRegistry);
|
|
@@ -698,8 +707,8 @@ export class SettingsModal {
|
|
|
698
707
|
|
|
699
708
|
/**
|
|
700
709
|
* Returns [] for the mcp/subscriptions categories (which render their own
|
|
701
|
-
* entry types).
|
|
702
|
-
*
|
|
710
|
+
* entry types). Every other category flows through the normal group path,
|
|
711
|
+
* feature-unit headers included.
|
|
703
712
|
*/
|
|
704
713
|
private _currentItems(): SettingEntry[] {
|
|
705
714
|
if (
|
|
@@ -716,15 +725,6 @@ export class SettingsModal {
|
|
|
716
725
|
private _setValue(key: ConfigKey, value: unknown): void {
|
|
717
726
|
if (!this.configManager) return;
|
|
718
727
|
|
|
719
|
-
// Feature-unit toggle headers write to `featureFlags.<id>`; route those to
|
|
720
|
-
// the feature-flag manager (runtime toggle + persisted override) instead of
|
|
721
|
-
// a plain config write. Reset/adjust/activate all funnel a boolean value
|
|
722
|
-
// here (true = enabled), so this is the single flag-toggle chokepoint.
|
|
723
|
-
if (typeof key === 'string' && key.startsWith('featureFlags.')) {
|
|
724
|
-
this._toggleFlagValue(key, value);
|
|
725
|
-
return;
|
|
726
|
-
}
|
|
727
|
-
|
|
728
728
|
const callback: SettingAppliedCallback | null = this.onSettingApplied
|
|
729
729
|
? (change) => this.onSettingApplied!(change)
|
|
730
730
|
: null;
|
|
@@ -740,6 +740,13 @@ export class SettingsModal {
|
|
|
740
740
|
},
|
|
741
741
|
});
|
|
742
742
|
|
|
743
|
+
// Feature-unit headers write plain domain keys; the SDK settings bridge
|
|
744
|
+
// has already applied the change to the gate manager (live flip for
|
|
745
|
+
// runtime-toggleable features, pending-restart marker for startup-gated
|
|
746
|
+
// ones). Re-read the manager for every header bound to this key so the
|
|
747
|
+
// rows and the context pane show the honest state at the point of change.
|
|
748
|
+
this._syncFeatureHeadersForKey(key);
|
|
749
|
+
|
|
743
750
|
if (result.restartDomain !== null) {
|
|
744
751
|
this.lastSaveTriggeredRestart = result.restartDomain;
|
|
745
752
|
}
|
|
@@ -749,4 +756,16 @@ export class SettingsModal {
|
|
|
749
756
|
// No-op (result.changed === false, effectMessage === null): leave lastSettingEffectMessage untouched.
|
|
750
757
|
}
|
|
751
758
|
|
|
759
|
+
/** Refresh live/persisted/pending state for every feature header bound to `key`. */
|
|
760
|
+
private _syncFeatureHeadersForKey(key: ConfigKey): void {
|
|
761
|
+
if (!this.featureFlagManager) return;
|
|
762
|
+
for (const entries of this.groups.values()) {
|
|
763
|
+
for (const entry of entries) {
|
|
764
|
+
if (entry.flag && entry.flag.feature.enablement.key === key) {
|
|
765
|
+
syncFlagEntryFromManager(entry.flag, this.featureFlagManager);
|
|
766
|
+
}
|
|
767
|
+
}
|
|
768
|
+
}
|
|
769
|
+
}
|
|
770
|
+
|
|
752
771
|
}
|
package/src/main.ts
CHANGED
|
@@ -13,6 +13,7 @@ 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
15
|
import { PermissionPromptUI, buildPendingPermissionExtras } from './permissions/prompt.ts';
|
|
16
|
+
import { handleBrokerApprovalChange, buildFixSessionAffordance, buildFixSessionErrorNotice, handleFixSessionAttachKey, refreshFixSessionsFromApprovals } from './permissions/broker-approval-card.ts';
|
|
16
17
|
import { CommandRegistry } from './input/command-registry.ts';
|
|
17
18
|
import type { CommandContext } from './input/command-registry.ts';
|
|
18
19
|
import { renderProcessIndicator } from './renderer/process-indicator.ts';
|
|
@@ -32,8 +33,10 @@ import { logger } from '@pellux/goodvibes-sdk/platform/utils';
|
|
|
32
33
|
import { registerBuiltinPanels } from './panels/builtin-panels.ts';
|
|
33
34
|
import { bootstrapRuntime } from './runtime/bootstrap.ts';
|
|
34
35
|
import type { BootstrapContext } from './runtime/bootstrap.ts';
|
|
36
|
+
import { selfUpdateAtLaunch } from './cli/launch-auto-update.ts';
|
|
35
37
|
import { buildSharedOrchestratorCoreServices, refreshMemoryRecallSnapshot } from './runtime/orchestrator-core-services.ts';
|
|
36
|
-
import
|
|
38
|
+
import { createSessionContinuityHintsBuilder } from './runtime/session-continuity-hints.ts';
|
|
39
|
+
import { resolveWebSurfaceUrl } from '@pellux/goodvibes-sdk/platform/runtime/feature-announcements';
|
|
37
40
|
import { readLastSessionPointer, writeRecoveryFile } from '@/runtime/index.ts';
|
|
38
41
|
import { handleBlockingShellInput, type PendingPermissionState } from './shell/blocking-input.ts';
|
|
39
42
|
import { createPersistRecoverySnapshot, createRecoveryFileOps, createReopenRecoveryPanels, handleErrorAffordanceKey, resolveStartupRecoveryInfo } from './shell/recovery-input-helpers.ts';
|
|
@@ -43,7 +46,7 @@ import { buildPersistedSessionContext, formatReturnContextForDisplay, getReturnC
|
|
|
43
46
|
import { summarizeError } from '@pellux/goodvibes-sdk/platform/utils';
|
|
44
47
|
import { prepareShellCliRuntime } from './cli/entrypoint.ts';
|
|
45
48
|
import { applyInitialTuiCliState } from './cli/tui-startup.ts';
|
|
46
|
-
import {
|
|
49
|
+
import { applyConfiguredHitlMode, applyRuntimeConfigValue, applyTuiRuntimeConfigDefaults } from './cli/config-overrides.ts';
|
|
47
50
|
import { renderToolCallBlock } from './renderer/tool-call.ts';
|
|
48
51
|
import { wireSpokenTurnRuntime } from './audio/spoken-turn-wiring.ts';
|
|
49
52
|
import { attachSpokenTurnModelRouting, createSpokenTurnInputOptions } from './audio/spoken-turn-model-routing.ts';
|
|
@@ -79,6 +82,10 @@ async function main() {
|
|
|
79
82
|
homeDirectory: homedir(),
|
|
80
83
|
}, 'goodvibes');
|
|
81
84
|
|
|
85
|
+
// Launch-time self-update, before any bootstrap or terminal mode change; on
|
|
86
|
+
// an installed update this restarts onto the swapped binary and never returns.
|
|
87
|
+
const launchUpdateLines = await selfUpdateAtLaunch({ configManager, stdout });
|
|
88
|
+
|
|
82
89
|
const ctx: BootstrapContext = await bootstrapRuntime(stdout, {
|
|
83
90
|
configManager,
|
|
84
91
|
workingDir: bootstrapWorkingDir,
|
|
@@ -102,7 +109,7 @@ async function main() {
|
|
|
102
109
|
agentStatusIntervalRef,
|
|
103
110
|
orchestratorRefs,
|
|
104
111
|
setRenderRequest,
|
|
105
|
-
permissionPromptRef,
|
|
112
|
+
permissionPromptRef, trustPromptRef,
|
|
106
113
|
_writeLastSessionPointer: writeLastSessionPointer,
|
|
107
114
|
systemMessageRouter,
|
|
108
115
|
} = ctx;
|
|
@@ -111,7 +118,7 @@ async function main() {
|
|
|
111
118
|
const { approvalBroker, agentManager, modeManager, processManager, providerRegistry, secretsManager, subscriptionManager } = ctx.services;
|
|
112
119
|
conversation.setSessionMemoryStore(ctx.services.sessionMemoryStore);
|
|
113
120
|
conversation.setSessionLineageTracker(ctx.services.sessionLineageTracker);
|
|
114
|
-
// Shared payload (single source of truth, includes
|
|
121
|
+
// Shared payload (single source of truth, includes the memoryRegistry —
|
|
115
122
|
// see orchestrator-core-services.ts) plus this site's favoritesStore.
|
|
116
123
|
orchestrator.setCoreServices({
|
|
117
124
|
...buildSharedOrchestratorCoreServices({ services: ctx.services, configManager, providerRegistry }),
|
|
@@ -120,49 +127,37 @@ async function main() {
|
|
|
120
127
|
ctx.services.wrfcController.setPlanManager(ctx.services.planManager);
|
|
121
128
|
let activeConversationWidth = stdout.columns || 80;
|
|
122
129
|
conversation.setWidthProvider(() => activeConversationWidth);
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
modeManager.setHITLMode(hitlMode);
|
|
127
|
-
}
|
|
128
|
-
}
|
|
130
|
+
// Persisted HITL mode + TUI-side config defaults (doc'd at their definitions).
|
|
131
|
+
applyConfiguredHitlMode(configManager, modeManager);
|
|
132
|
+
applyTuiRuntimeConfigDefaults(configManager);
|
|
129
133
|
|
|
130
|
-
// TUI
|
|
131
|
-
|
|
132
|
-
// key is absent from both (e.g. a new install); an explicit user value is respected. No disk write.
|
|
133
|
-
applyRuntimeConfigDefault(configManager, 'display.showTokenSpeed', true);
|
|
134
|
+
// Re-surface pre-TUI launch-update lines in-session (the alt screen wipes stdout).
|
|
135
|
+
for (const line of launchUpdateLines) systemMessageRouter.high(`[Update] ${line}`);
|
|
134
136
|
|
|
135
137
|
const panelManager = ctx.services.panelManager;
|
|
136
|
-
const buildSessionContinuityHints = (
|
|
137
|
-
const sessionSnapshot = uiServices.readModels.session.getSnapshot();
|
|
138
|
-
const tasksSnapshot = uiServices.readModels.tasks.getSnapshot();
|
|
139
|
-
const remoteSnapshot = uiServices.readModels.remote.getSnapshot();
|
|
140
|
-
const worktreeSnapshot = uiServices.readModels.worktrees.getSnapshot();
|
|
141
|
-
return {
|
|
142
|
-
pendingApprovals: sessionSnapshot.pendingApproval ? 1 : 0,
|
|
143
|
-
activeTasks: tasksSnapshot.tasks.filter((task) => task.status === 'running' || task.status === 'queued').length,
|
|
144
|
-
blockedTasks: tasksSnapshot.tasks.filter((task) => task.status === 'blocked').length,
|
|
145
|
-
remoteContracts: remoteSnapshot.contracts.length,
|
|
146
|
-
remoteRunners: remoteSnapshot.contracts.slice(0, 4).map((contract) => contract.runnerId),
|
|
147
|
-
worktreeCount: worktreeSnapshot.records.length,
|
|
148
|
-
worktreePaths: worktreeSnapshot.records.slice(0, 3).map((record) => record.path),
|
|
149
|
-
openPanels: panelManager.getAllOpen().map((panel) => panel.id),
|
|
150
|
-
};
|
|
151
|
-
};
|
|
138
|
+
const buildSessionContinuityHints = createSessionContinuityHintsBuilder({ readModels: uiServices.readModels, panelManager });
|
|
152
139
|
|
|
153
140
|
let pendingPermission: PendingPermissionState | null = null;
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
141
|
+
// One-key jump/attach to a spawned CI fix-session: the affordance ARMS the id; the next 'j' runs the resume so the user never retypes it.
|
|
142
|
+
let fixSessionAttachArmed: string | null = null;
|
|
143
|
+
const attachToFixSession = (fixSessionId: string) => { void commandContext.executeCommand?.('session', ['resume', fixSessionId]); };
|
|
144
|
+
const onFixSessionStarted = buildFixSessionAffordance({
|
|
145
|
+
notify: (message) => { systemMessageRouter.high(message); render(); },
|
|
146
|
+
arm: (fixSessionId) => { fixSessionAttachArmed = fixSessionId; },
|
|
160
147
|
});
|
|
148
|
+
// A failed fix-session spawn stamps an error instead of a dead id: render it honestly, no jump armed.
|
|
149
|
+
const onFixSessionError = buildFixSessionErrorNotice((message) => { systemMessageRouter.high(message); render(); });
|
|
150
|
+
commandContext.armFixSessionAttach = onFixSessionStarted;
|
|
151
|
+
approvalBroker.subscribe((approval) => handleBrokerApprovalChange({
|
|
152
|
+
approval, broker: approvalBroker, render, onFixSessionStarted, onFixSessionError,
|
|
153
|
+
getPending: () => pendingPermission,
|
|
154
|
+
setPending: (next) => { pendingPermission = next; },
|
|
155
|
+
}));
|
|
156
|
+
refreshFixSessionsFromApprovals(() => approvalBroker.listApprovals(), onFixSessionStarted, onFixSessionError); // catch pre-subscription stamps
|
|
161
157
|
|
|
162
158
|
let scrollTop = 0;
|
|
163
159
|
let scrollLocked = true;
|
|
164
|
-
// Cached from the overlay-aware clamp the renderer computes (conversation-layout) each
|
|
165
|
-
// frame so scroll() clamps against exactly what is displayed, not a footer estimate.
|
|
160
|
+
// Cached from the overlay-aware clamp the renderer computes (conversation-layout) each frame so scroll() clamps against exactly what is displayed, not a footer estimate.
|
|
166
161
|
let lastMaxScroll: number | null = null;
|
|
167
162
|
// Stream and tool-timer state; mutated by wireStreamEventMetrics handlers, read during render.
|
|
168
163
|
const streamMetrics: StreamMetrics = {
|
|
@@ -185,8 +180,7 @@ async function main() {
|
|
|
185
180
|
const currentModel = providerRegistry.getCurrentModel();
|
|
186
181
|
const contextWindow = providerRegistry.getContextWindowForModel(currentModel);
|
|
187
182
|
const rows = stdout.rows || 24;
|
|
188
|
-
// Compact threshold must match buildShellFooter's `compact: height < 30` posture below,
|
|
189
|
-
// else estimateShellFooterHeight's cached-height fast path answers with the wrong mode.
|
|
183
|
+
// Compact threshold must match buildShellFooter's `compact: height < 30` posture below, else estimateShellFooterHeight's cached-height fast path answers with the wrong mode.
|
|
190
184
|
return rows - 2 - estimateShellFooterHeight(promptLines, contextWindow, rows < 30);
|
|
191
185
|
};
|
|
192
186
|
|
|
@@ -414,7 +408,7 @@ async function main() {
|
|
|
414
408
|
input.setConversationManager(conversation);
|
|
415
409
|
input.setContentWidth(getPromptContentWidth());
|
|
416
410
|
input.filePicker.setOnUpdate(() => render());
|
|
417
|
-
//
|
|
411
|
+
// retirement: agentDetailModal/processModal setOnRefresh wiring removed —
|
|
418
412
|
// those modals were deleted (Fleet subsumes the live process tree via F2).
|
|
419
413
|
|
|
420
414
|
// Model picker callback is handled in bootstrap.ts — do not duplicate here.
|
|
@@ -501,6 +495,7 @@ async function main() {
|
|
|
501
495
|
hitlMode: modeManager.getHITLMode(),
|
|
502
496
|
// Cross-surface spine posture segment (adopted-daemon mode only).
|
|
503
497
|
sessionSpineStatus: (() => { const s = uiServices.platform.externalServices?.inspect(); return s?.sessionSpineActive && s.sessionSpineStatus && s.sessionSpineStatus !== 'unknown' ? s.sessionSpineStatus : undefined; })(), runningAgentCount, runningProcessCount,
|
|
498
|
+
webSurfaceUrl: configManager.get('web.enabled') ? resolveWebSurfaceUrl(configManager) : undefined,
|
|
504
499
|
// Composer must not read as focused while the panel/process indicator owns keyboard focus.
|
|
505
500
|
promptFocused: !input.panelFocused && !input.indicatorFocused,
|
|
506
501
|
indicatorFocused: input.indicatorFocused,
|
|
@@ -547,7 +542,7 @@ async function main() {
|
|
|
547
542
|
// Calculate how many rows are consumed by overlays (thinking, permissions, queue, file picker)
|
|
548
543
|
let overlayRows = 0;
|
|
549
544
|
if (orchestrator.isThinking) overlayRows += 2; // spinner + blank
|
|
550
|
-
if (pendingPermission) overlayRows += PermissionPromptUI.getPromptHeight(pendingPermission, pendingPermission.hunkState, pendingPermission.detailsExpanded, pendingPermission.requestedBy);
|
|
545
|
+
if (pendingPermission) overlayRows += PermissionPromptUI.getPromptHeight(pendingPermission, pendingPermission.hunkState, pendingPermission.detailsExpanded, pendingPermission.requestedBy, PermissionPromptUI.promptViewState(pendingPermission, conversationWidth, approvalBroker));
|
|
551
546
|
overlayRows += orchestrator.messageQueue.length * 3; // queued messages
|
|
552
547
|
// File picker and model picker overlay rows computed from actual rendered line count below
|
|
553
548
|
// Selection modal overlay rows are computed from actual rendered line count below
|
|
@@ -597,7 +592,7 @@ async function main() {
|
|
|
597
592
|
}
|
|
598
593
|
|
|
599
594
|
if (pendingPermission) {
|
|
600
|
-
viewport.push(...PermissionPromptUI.createPromptLines(conversationWidth, pendingPermission, pendingPermission.hunkState, pendingPermission.detailsExpanded, pendingPermission.requestedBy));
|
|
595
|
+
viewport.push(...PermissionPromptUI.createPromptLines(conversationWidth, pendingPermission, pendingPermission.hunkState, pendingPermission.detailsExpanded, pendingPermission.requestedBy, PermissionPromptUI.promptViewState(pendingPermission, conversationWidth, approvalBroker)));
|
|
601
596
|
}
|
|
602
597
|
|
|
603
598
|
orchestrator.messageQueue.forEach(msg => {
|
|
@@ -643,8 +638,8 @@ async function main() {
|
|
|
643
638
|
panelWidth: panelComposite.panelWidth,
|
|
644
639
|
});
|
|
645
640
|
};
|
|
646
|
-
const renderScheduler = createRenderScheduler(renderNow, undefined, () => lifecycle.isTerminalRestored()); //
|
|
647
|
-
const render = (): void => renderScheduler.schedule(); // captured direct writes → activity log + quiet /debug counter, not repeated transcript lines (
|
|
641
|
+
const renderScheduler = createRenderScheduler(renderNow, undefined, () => lifecycle.isTerminalRestored()); // coalescer; no frames after terminal restore
|
|
642
|
+
const render = (): void => renderScheduler.schedule(); // captured direct writes → activity log + quiet /debug counter, not repeated transcript lines (1a)
|
|
648
643
|
const terminalOutputGuard = installTuiTerminalOutputGuard({ stdout, stderr: process.stderr, onCapture: (total) => { commandContext.session.runtime.terminalWritesIntercepted = total; render(); } });
|
|
649
644
|
|
|
650
645
|
setRenderRequest(() => renderScheduler.flushNow()); // bootstrap's 16ms coalescer composites via the (restore-gated) scheduler
|
|
@@ -669,7 +664,7 @@ async function main() {
|
|
|
669
664
|
homeDirectory,
|
|
670
665
|
getConfiguredProviderIds: ctx._getConfiguredProviderIds,
|
|
671
666
|
getPinned: ctx._getPinned,
|
|
672
|
-
render,
|
|
667
|
+
render, trustPromptRef,
|
|
673
668
|
});
|
|
674
669
|
|
|
675
670
|
const { refreshGit, unsubs: turnUnsubs } = wireTurnEventHandlers({
|
|
@@ -731,7 +726,7 @@ async function main() {
|
|
|
731
726
|
stdin.resume();
|
|
732
727
|
stdin.setEncoding('utf8');
|
|
733
728
|
allowTerminalWrite(() => stdout.write((cli.flags.noAltScreen ? '' : ALT_SCREEN_ENTER) + CLEAR_SCREEN + CURSOR_HIDE + MOUSE_ENABLE + KEYBOARD_EXT_ENABLE + PASTE_ENABLE + FOCUS_ENABLE));
|
|
734
|
-
//
|
|
729
|
+
// forced dark/light applies before first paint; auto (TTY only) fires the
|
|
735
730
|
// OSC 11 probe and repaints once if light wins. filterInput strips the reply from stdin.
|
|
736
731
|
const themeProbe = installBackgroundThemeProbe({ configManager, isTTY: Boolean(stdout.isTTY), env: process.env, writeQuery: (b) => allowTerminalWrite(() => stdout.write(b)), requestRepaint: () => { compositor.resetDiff(); render(); } });
|
|
737
732
|
|
|
@@ -752,12 +747,17 @@ async function main() {
|
|
|
752
747
|
if (blocking.handled) {
|
|
753
748
|
return;
|
|
754
749
|
}
|
|
755
|
-
|
|
756
750
|
// One-key retry affordance: armed after a user-visible TURN_ERROR; any key other than r/m dismisses it and routes normally.
|
|
757
751
|
if (errorAffordanceActive) {
|
|
758
752
|
errorAffordanceActive = false;
|
|
759
753
|
if (handleErrorAffordanceKey(data, { retryArmed: retryCtx !== null, retry: retryTurn, openModelPicker: () => commandContext.openModelPicker?.(), render })) return;
|
|
760
754
|
}
|
|
755
|
+
// One-key jump to a spawned CI fix-session: 'j' attaches, any other key dismisses and routes normally.
|
|
756
|
+
if (fixSessionAttachArmed !== null) {
|
|
757
|
+
const armedFixSessionId = fixSessionAttachArmed;
|
|
758
|
+
fixSessionAttachArmed = null;
|
|
759
|
+
if (handleFixSessionAttachKey(data, { armedFixSessionId, attach: attachToFixSession, render })) return;
|
|
760
|
+
}
|
|
761
761
|
|
|
762
762
|
input.feed(data);
|
|
763
763
|
});
|
|
@@ -8,7 +8,7 @@ export type AgentInspectorEntryKind = 'user' | 'assistant' | 'tool_call' | 'tool
|
|
|
8
8
|
// ---------------------------------------------------------------------------
|
|
9
9
|
// Shared agent status / stall constants
|
|
10
10
|
// Used by the Fleet panel's agent views and read-model consumers (formerly also
|
|
11
|
-
// AgentDetailModal, which was retired in
|
|
11
|
+
// AgentDetailModal, which was retired in).
|
|
12
12
|
// ---------------------------------------------------------------------------
|
|
13
13
|
|
|
14
14
|
/** Terminal statuses — cancel not offered; stall check skipped. */
|
|
@@ -127,7 +127,7 @@ export function agentKindStyle(kind: AgentInspectorEntryKind, colors: Record<str
|
|
|
127
127
|
}
|
|
128
128
|
|
|
129
129
|
// ---------------------------------------------------------------------------
|
|
130
|
-
// Inspector filter modes (ported from the merged agent-logs console —
|
|
130
|
+
// Inspector filter modes (ported from the merged agent-logs console —)
|
|
131
131
|
// ---------------------------------------------------------------------------
|
|
132
132
|
|
|
133
133
|
export type AgentInspectorFilterType = 'all' | 'assistant' | 'tool' | 'error';
|
|
@@ -188,7 +188,7 @@ interface AgentUsageLike {
|
|
|
188
188
|
|
|
189
189
|
/**
|
|
190
190
|
* True when a usage object carries actual reported token data rather than
|
|
191
|
-
* being present-but-empty
|
|
191
|
+
* being present-but-empty.: the SDK's AgentRecord.usage is initialised
|
|
192
192
|
* to an all-zero object at agent spawn (platform/tools/agent/manager.ts) and
|
|
193
193
|
* — in the currently pinned SDK version — is never updated past that, so a
|
|
194
194
|
* plain truthiness check on `rec.usage` treats every agent, including ones
|
|
@@ -256,7 +256,7 @@ export function buildWrfcCostSegments(
|
|
|
256
256
|
}
|
|
257
257
|
|
|
258
258
|
// ---------------------------------------------------------------------------
|
|
259
|
-
// Agent lifecycle event subscriptions (ported from agent-logs —
|
|
259
|
+
// Agent lifecycle event subscriptions (ported from agent-logs —)
|
|
260
260
|
// ---------------------------------------------------------------------------
|
|
261
261
|
|
|
262
262
|
export interface AgentActivityHooks {
|
package/src/panels/base-panel.ts
CHANGED
|
@@ -227,10 +227,10 @@ export abstract class BasePanel implements Panel {
|
|
|
227
227
|
return consumed;
|
|
228
228
|
}
|
|
229
229
|
|
|
230
|
-
/**
|
|
230
|
+
/** Mark this panel dirty — it will be re-rendered on the next compositor frame. */
|
|
231
231
|
public invalidate(): void { this.needsRender = true; }
|
|
232
232
|
|
|
233
|
-
/**
|
|
233
|
+
/** Called by the compositor after a successful render to clear the dirty flag. */
|
|
234
234
|
public markRendered(): void { this.needsRender = false; }
|
|
235
235
|
|
|
236
236
|
protected markDirty(): void {
|