@pellux/goodvibes-tui 1.16.1 → 1.18.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 +138 -9
- package/README.md +1 -1
- package/docs/foundation-artifacts/operator-contract.json +5490 -1806
- 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/management-commands.ts +82 -15
- package/src/cli/management.ts +0 -2
- 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/consolidation-receipt.ts +41 -0
- package/src/core/conversation-line-cache.ts +3 -3
- package/src/core/conversation-rendering.ts +18 -9
- package/src/core/conversation.ts +2 -2
- package/src/core/long-task-notifier.ts +7 -7
- package/src/core/memory-provenance.ts +67 -0
- package/src/core/pairing-devices.ts +49 -0
- package/src/core/pairing-handoff.ts +101 -0
- package/src/core/pairing-offers.ts +83 -0
- package/src/core/pairing-origin.ts +78 -0
- package/src/core/pairing-tailscale-gateway.ts +56 -0
- package/src/core/power-chip-source.ts +91 -0
- package/src/core/power-status.ts +95 -0
- package/src/core/stable-host.ts +171 -0
- package/src/core/stream-event-wiring.ts +29 -6
- package/src/core/system-message-noise.ts +1 -1
- package/src/core/system-message-router.ts +6 -6
- package/src/core/turn-budget-outcome.ts +60 -0
- package/src/core/turn-cancellation.ts +40 -1
- package/src/core/turn-event-wiring.ts +14 -8
- package/src/daemon/cli.ts +64 -58
- 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 +55 -9
- package/src/input/commands/acp-runtime.ts +72 -0
- 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/devices-runtime.ts +109 -0
- package/src/input/commands/git-runtime.ts +28 -4
- package/src/input/commands/graph-runtime.ts +45 -0
- 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/operator-rpc.ts +32 -11
- 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/power-runtime.ts +56 -0
- package/src/input/commands/provider-accounts-runtime.ts +71 -6
- package/src/input/commands/provider.ts +7 -5
- package/src/input/commands/qrcode-runtime.ts +14 -19
- package/src/input/commands/queue-runtime.ts +70 -0
- 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-attempts.ts +6 -1
- package/src/input/commands/workstream-runtime.ts +46 -27
- package/src/input/commands/worktree-runtime.ts +35 -8
- package/src/input/commands.ts +10 -2
- package/src/input/config-modal-types.ts +14 -3
- package/src/input/config-modal.ts +75 -13
- 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 +29 -6
- package/src/input/handler.ts +2 -2
- package/src/input/keybindings.ts +21 -3
- 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 +52 -57
- package/src/input/settings-modal-mutations.ts +12 -68
- package/src/input/settings-modal-types.ts +27 -17
- package/src/input/settings-modal.ts +74 -55
- package/src/main.ts +90 -90
- 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 +83 -11
- package/src/panels/builtin/session.ts +5 -5
- package/src/panels/builtin/shared.ts +16 -4
- package/src/panels/builtin-modals.ts +59 -42
- package/src/panels/builtin-panels.ts +1 -1
- package/src/panels/cost-tracker-panel.ts +124 -7
- package/src/panels/diff-panel.ts +39 -6
- package/src/panels/eval-registry.ts +2 -2
- package/src/panels/fleet-acts.ts +446 -0
- package/src/panels/fleet-deep-link.ts +1 -1
- package/src/panels/fleet-gateway.ts +117 -0
- package/src/panels/fleet-observed-render.ts +128 -0
- package/src/panels/fleet-panel-format.ts +163 -11
- package/src/panels/fleet-panel-worktree-detail.ts +14 -0
- package/src/panels/fleet-panel.ts +73 -57
- package/src/panels/fleet-read-model.ts +84 -14
- package/src/panels/fleet-spawn.ts +266 -0
- package/src/panels/fleet-steer.ts +2 -2
- package/src/panels/fleet-stop.ts +41 -5
- package/src/panels/fleet-tabs.ts +15 -16
- 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/memory-consolidation-gateway.ts +94 -0
- package/src/panels/modals/devices-modal.ts +161 -0
- 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 +153 -9
- 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 +198 -68
- 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 +7 -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/panels/workstream-graph-render.ts +123 -0
- 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/composer-fragments.ts +69 -0
- package/src/renderer/compositor.ts +2 -2
- package/src/renderer/config-modal.ts +1 -1
- package/src/renderer/conversation-overlays.ts +1 -1
- package/src/renderer/footer-tips.ts +3 -3
- package/src/renderer/hint-grammar.ts +1 -1
- package/src/renderer/modal-factory.ts +1 -1
- package/src/renderer/model-picker-overlay.ts +1 -1
- package/src/renderer/model-workspace.ts +7 -2
- package/src/renderer/panel-composite.ts +2 -2
- package/src/renderer/panel-workspace-bar.ts +1 -1
- package/src/renderer/process-indicator.ts +1 -1
- package/src/renderer/selection-modal-overlay.ts +1 -1
- package/src/renderer/session-picker-modal.ts +2 -2
- package/src/renderer/settings-modal-helpers.ts +23 -5
- package/src/renderer/settings-modal.ts +129 -39
- package/src/renderer/shell-surface.ts +6 -0
- package/src/renderer/terminal-bg-probe.ts +1 -1
- package/src/renderer/theme.ts +3 -3
- package/src/renderer/tool-call.ts +13 -7
- package/src/renderer/tool-result-summary.ts +1 -1
- package/src/renderer/turn-injection.ts +7 -6
- package/src/renderer/ui-factory.ts +38 -1
- package/src/runtime/bootstrap-command-context.ts +7 -2
- package/src/runtime/bootstrap-command-parts.ts +9 -5
- package/src/runtime/bootstrap-core.ts +41 -41
- package/src/runtime/bootstrap-hook-bridge.ts +1 -1
- package/src/runtime/bootstrap-shell.ts +13 -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 +90 -0
- package/src/runtime/embedded-service-factories.ts +40 -0
- package/src/runtime/feature-settings.ts +147 -0
- package/src/runtime/fleet-services.ts +41 -0
- package/src/runtime/idle-power-services.ts +85 -0
- package/src/runtime/index.ts +6 -21
- package/src/runtime/interaction-seams.ts +109 -0
- 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/power-keepawake-remote.ts +75 -0
- package/src/runtime/process-lifecycle.ts +3 -1
- 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 +113 -113
- 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 +6 -1
- package/src/runtime/update-check.ts +15 -69
- package/src/runtime/workstream-services.ts +2 -2
- package/src/shell/blocking-input.ts +108 -132
- package/src/shell/recovery-input-helpers.ts +53 -166
- 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
|
@@ -95,7 +95,7 @@ export function handleSelectionModalToken(state: SelectionRouteState, token: Inp
|
|
|
95
95
|
dispatchSelectionAction(action, selected);
|
|
96
96
|
}
|
|
97
97
|
} else if (state.selectionModal.allowSearch) {
|
|
98
|
-
//
|
|
98
|
+
// instant filter: an unclaimed keystroke arms search AND starts
|
|
99
99
|
// the query immediately, instead of silently doing nothing until the
|
|
100
100
|
// user discovers '/' first (the "help search needs '/' arming while
|
|
101
101
|
// the palette filters instantly" evaluator finding — /help has no
|
|
@@ -108,7 +108,7 @@ export function handleSelectionModalToken(state: SelectionRouteState, token: Inp
|
|
|
108
108
|
}
|
|
109
109
|
} else if (token.type === 'key') {
|
|
110
110
|
if (token.logicalName === 'escape') {
|
|
111
|
-
//
|
|
111
|
+
// ONE Escape always closes the modal, regardless of search state.
|
|
112
112
|
// Clearing an in-progress query is Backspace's job, not Esc's — the old
|
|
113
113
|
// two-stage contract (1st Esc clears query, 2nd blurs search, 3rd
|
|
114
114
|
// finally closes) was the "help modal took 3 Escapes after searching"
|
|
@@ -234,6 +234,8 @@ type SettingsRouteState = {
|
|
|
234
234
|
searchQuery: string;
|
|
235
235
|
commitEdit: () => void;
|
|
236
236
|
toggleSelectedFlag: () => void;
|
|
237
|
+
/** Scroll the documentation pane (PgUp/PgDn); the renderer clamps and shows honest markers. */
|
|
238
|
+
scrollContext?: (delta: number) => void;
|
|
237
239
|
activateSelected: () => void;
|
|
238
240
|
handleSubscriptionLogoutKey?: (key: string) => 'confirmed' | 'cancelled' | 'absorbed' | 'inactive';
|
|
239
241
|
adjustSelected: (direction: 'left' | 'right', step?: number) => void;
|
|
@@ -415,6 +417,12 @@ export function handleSettingsModalToken(state: SettingsRouteState, token: Input
|
|
|
415
417
|
const reset = state.settingsModal.resetSelected?.();
|
|
416
418
|
if (reset) syncRuntimeAfterSettingReset(state.commandContext, reset.key, reset.value);
|
|
417
419
|
}
|
|
420
|
+
else if (token.logicalName === 'pageup' && !state.settingsModal.editingMode) {
|
|
421
|
+
state.settingsModal.scrollContext?.(-3);
|
|
422
|
+
}
|
|
423
|
+
else if (token.logicalName === 'pagedown' && !state.settingsModal.editingMode) {
|
|
424
|
+
state.settingsModal.scrollContext?.(3);
|
|
425
|
+
}
|
|
418
426
|
else if (token.logicalName === 'tab' && !state.settingsModal.searchFocused) {
|
|
419
427
|
if (state.settingsModal.toggleFocusPane) state.settingsModal.toggleFocusPane();
|
|
420
428
|
else if (focusPane === 'categories') state.settingsModal.focusSettings?.();
|
|
@@ -556,9 +564,9 @@ type ConfigModalRouteState = {
|
|
|
556
564
|
};
|
|
557
565
|
|
|
558
566
|
/**
|
|
559
|
-
* Route a key to the generic config-modal host (
|
|
567
|
+
* Route a key to the generic config-modal host (MIGRATE-TO-MODAL
|
|
560
568
|
* surfaces). The host owns the reserved navigation keys (Esc, up/down/j/k tab
|
|
561
|
-
* left/right, and now '/' —
|
|
569
|
+
* left/right, and now '/' — item 1's type-to-filter, armed the same
|
|
562
570
|
* way as scrollable-list-panel.ts's opt-in filter and SettingsModal's own
|
|
563
571
|
* '/'-armed search); every other key is offered to the active surface's
|
|
564
572
|
* declarative action table (fireAction handles the two-press confirm for
|
|
@@ -227,7 +227,7 @@ export function handleModalTokenRoutes(state: ModalTokenRouteState, token: Input
|
|
|
227
227
|
return withState(state, true);
|
|
228
228
|
}
|
|
229
229
|
|
|
230
|
-
//
|
|
230
|
+
// retirement: the live-tail, agent-detail, and process-modal token
|
|
231
231
|
// routes were removed with those modals (F2 now opens Fleet, which subsumes
|
|
232
232
|
// the live process tree; the three modals were structurally unreachable after
|
|
233
233
|
// the F2 repoint and were deleted).
|
|
@@ -138,10 +138,10 @@ export async function handleMigrateLegacyDaemonServiceForHandler(handler: InputH
|
|
|
138
138
|
legacy,
|
|
139
139
|
);
|
|
140
140
|
handler.commandContext?.print?.(
|
|
141
|
-
['Migrate
|
|
141
|
+
['Migrate install-script daemon service:', ...result.lines.map((line: string) => ` ${line}`)].join('\n'),
|
|
142
142
|
);
|
|
143
143
|
} catch (error) {
|
|
144
|
-
handler.commandContext?.print?.(`Migrate
|
|
144
|
+
handler.commandContext?.print?.(`Migrate install-script daemon service failed: ${summarizeError(error)}`);
|
|
145
145
|
} finally {
|
|
146
146
|
handler.onboardingApplyPending = false;
|
|
147
147
|
handler.requestRender();
|
|
@@ -3,6 +3,7 @@ import { beginOpenAICodexLogin, exchangeOpenAICodexCode } from '@pellux/goodvibe
|
|
|
3
3
|
import { summarizeError, openExternalUrl } from '@pellux/goodvibes-sdk/platform/utils';
|
|
4
4
|
import { getProviderIdFromModel } from '../config/provider-model.ts';
|
|
5
5
|
import { buildProviderAccountSnapshot } from '@/runtime/index.ts';
|
|
6
|
+
import { enrichProviderAccountsSnapshot } from '../runtime/onboarding/provider-key-capture.ts';
|
|
6
7
|
import { handleConnectExistingDaemonForHandler, handleMigrateLegacyDaemonServiceForHandler } from './handler-onboarding-daemon-adopt.ts';
|
|
7
8
|
import { detectLegacyUnit, resolveConfiguredServiceName } from '../runtime/legacy-daemon-migration.ts';
|
|
8
9
|
import { OnboardingWizardController, type OnboardingWizardAction, type OnboardingWizardApplyFeedback } from './onboarding/onboarding-wizard.ts';
|
|
@@ -376,12 +377,12 @@ export async function refreshOnboardingHydrationForHandler(handler: InputHandler
|
|
|
376
377
|
list: () => handler.uiServices.platform.surfaceRegistry.syncConfiguredSurfaces(),
|
|
377
378
|
},
|
|
378
379
|
providerAccounts: {
|
|
379
|
-
loadSnapshot: () => buildProviderAccountSnapshot({
|
|
380
|
+
loadSnapshot: async () => enrichProviderAccountsSnapshot(await buildProviderAccountSnapshot({
|
|
380
381
|
providerRegistry: handler.uiServices.providers.providerRegistry,
|
|
381
382
|
serviceRegistry: handler.uiServices.platform.serviceRegistry,
|
|
382
383
|
subscriptionManager: handler.uiServices.platform.subscriptionManager,
|
|
383
384
|
secretsManager: handler.uiServices.platform.secretsManager,
|
|
384
|
-
}),
|
|
385
|
+
}), handler.uiServices.providers.providerRegistry),
|
|
385
386
|
},
|
|
386
387
|
legacyDaemon: {
|
|
387
388
|
detect: () => ({ ...detectLegacyUnit({ homeDir: handler.uiServices.environment.homeDirectory }), trackedServiceName: resolveConfiguredServiceName(handler.uiServices.platform.configManager) }),
|
|
@@ -3,6 +3,7 @@ import type { InputToken } from '@pellux/goodvibes-sdk/platform/core';
|
|
|
3
3
|
import type { CommandContext } from './command-registry.ts';
|
|
4
4
|
import type { CapabilityFilter, CategoryFilter, ModelPickerModal } from './model-picker.ts';
|
|
5
5
|
import { MODEL_PICKER_CHROME_LINES } from '../renderer/model-picker-overlay.ts';
|
|
6
|
+
import { ensureProviderKeyThenSelect } from './provider-key-intake.ts';
|
|
6
7
|
import { resolveAndValidatePath } from '@pellux/goodvibes-sdk/platform/utils';
|
|
7
8
|
import { logger } from '@pellux/goodvibes-sdk/platform/utils';
|
|
8
9
|
import type { BlockActionId } from '../renderer/block-actions.ts';
|
|
@@ -66,15 +67,17 @@ export function handleModelPickerToken(state: ModelPickerRouteState, token: Inpu
|
|
|
66
67
|
} else {
|
|
67
68
|
const target = state.modelPicker.target;
|
|
68
69
|
const handled = state.onModelPickerCommit?.() ?? false;
|
|
69
|
-
if (!handled) {
|
|
70
|
-
state.commandContext?.completeModelSelection?.({
|
|
71
|
-
model: selected,
|
|
72
|
-
effort: currentEffort,
|
|
73
|
-
target,
|
|
74
|
-
});
|
|
75
|
-
}
|
|
76
70
|
state.modelPicker.close();
|
|
77
71
|
if (state.modalStack[state.modalStack.length - 1] === 'modelPicker') state.modalStack.pop();
|
|
72
|
+
const ctx = state.commandContext;
|
|
73
|
+
if (!handled && ctx) {
|
|
74
|
+
// Selecting an unconfigured provider captures its key inline
|
|
75
|
+
// (concealed prompt → secrets manager → live re-register) and
|
|
76
|
+
// only then completes the original selection.
|
|
77
|
+
ensureProviderKeyThenSelect(ctx, selected.provider, () => {
|
|
78
|
+
ctx.completeModelSelection?.({ model: selected, effort: currentEffort, target });
|
|
79
|
+
});
|
|
80
|
+
}
|
|
78
81
|
}
|
|
79
82
|
}
|
|
80
83
|
} else if (mode === 'provider') {
|
|
@@ -88,12 +91,16 @@ export function handleModelPickerToken(state: ModelPickerRouteState, token: Inpu
|
|
|
88
91
|
} else if (mode === 'effort') {
|
|
89
92
|
const model = state.modelPicker.pendingModel;
|
|
90
93
|
const effort = state.modelPicker.effortLevels[idx];
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
if (!handled) state.commandContext?.completeModelSelection?.({ model, effort, target: state.modelPicker.target });
|
|
94
|
-
}
|
|
94
|
+
const target = state.modelPicker.target;
|
|
95
|
+
const handled = model && effort ? (state.onModelPickerCommit?.() ?? false) : false;
|
|
95
96
|
state.modelPicker.close();
|
|
96
97
|
if (state.modalStack[state.modalStack.length - 1] === 'modelPicker') state.modalStack.pop();
|
|
98
|
+
const ctx = state.commandContext;
|
|
99
|
+
if (model && effort && !handled && ctx) {
|
|
100
|
+
ensureProviderKeyThenSelect(ctx, model.provider, () => {
|
|
101
|
+
ctx.completeModelSelection?.({ model, effort, target });
|
|
102
|
+
});
|
|
103
|
+
}
|
|
97
104
|
} else if (mode === 'embeddingProvider') {
|
|
98
105
|
const selectedProvider = state.modelPicker.embeddingProviders[idx];
|
|
99
106
|
if (selectedProvider) {
|
|
@@ -113,10 +120,16 @@ export function handleModelPickerToken(state: ModelPickerRouteState, token: Inpu
|
|
|
113
120
|
state.modelPicker.contextCapError = null;
|
|
114
121
|
const validCap = parsedCap !== null && parsedCap > 0 && parsedCap <= 10_000_000 ? parsedCap : null;
|
|
115
122
|
const effort = state.commandContext?.session.runtime.reasoningEffort ?? 'medium';
|
|
123
|
+
const target = state.modelPicker.target;
|
|
116
124
|
const handled = state.onModelPickerCommit?.() ?? false;
|
|
117
|
-
if (!handled) state.commandContext?.completeModelSelection?.({ model: capModel, effort, contextCap: validCap, target: state.modelPicker.target });
|
|
118
125
|
state.modelPicker.close();
|
|
119
126
|
if (state.modalStack[state.modalStack.length - 1] === 'modelPicker') state.modalStack.pop();
|
|
127
|
+
const ctx = state.commandContext;
|
|
128
|
+
if (!handled && ctx) {
|
|
129
|
+
ensureProviderKeyThenSelect(ctx, capModel.provider, () => {
|
|
130
|
+
ctx.completeModelSelection?.({ model: capModel, effort, contextCap: validCap, target });
|
|
131
|
+
});
|
|
132
|
+
}
|
|
120
133
|
}
|
|
121
134
|
} else {
|
|
122
135
|
state.modelPicker.close();
|
|
@@ -214,7 +227,7 @@ function cycleCapabilityFilter(modelPicker: ModelPickerModal): void {
|
|
|
214
227
|
modelPicker.setCapabilityFilter(cycle[(cur + 1) % cycle.length]!);
|
|
215
228
|
}
|
|
216
229
|
|
|
217
|
-
//
|
|
230
|
+
// e / retirement: the process-modal and live-tail-modal token routes
|
|
218
231
|
// were removed with those modals (F2 now opens Fleet, which subsumes the live
|
|
219
232
|
// process tree). ProcessModal/AgentDetailModal/LiveTailModal were structurally
|
|
220
233
|
// unreachable after the F2 repoint and were deleted.
|
|
@@ -84,7 +84,7 @@ export function handleGlobalShortcutToken(
|
|
|
84
84
|
// unclaimed key once panelFocused is true and reports it handled). The
|
|
85
85
|
// practical effect was that F2 could OPEN+focus the Fleet panel exactly
|
|
86
86
|
// once; every subsequent press while already focused vanished silently —
|
|
87
|
-
// "F2 pressed 4x never closed the panel" (
|
|
87
|
+
// "F2 pressed 4x never closed the panel" (evaluator finding). Routing
|
|
88
88
|
// it here, before handlePanelFocusToken ever sees the token, gives F2 the
|
|
89
89
|
// same toggle semantics as Ctrl+O below, matching how Ctrl+P/panel-picker
|
|
90
90
|
// was already reachable regardless of panelFocused.
|
|
@@ -119,6 +119,29 @@ export function handleGlobalShortcutToken(
|
|
|
119
119
|
state.handleCtrlC();
|
|
120
120
|
return true;
|
|
121
121
|
|
|
122
|
+
case 'cancel-tool-call':
|
|
123
|
+
// Cancel JUST the currently-running tool call (the live transcript row).
|
|
124
|
+
// No-op (still consumed) when nothing is running — the composer keeps the
|
|
125
|
+
// keystroke rather than letting it fall through as text.
|
|
126
|
+
state.commandContext?.cancelToolCall?.();
|
|
127
|
+
return true;
|
|
128
|
+
|
|
129
|
+
case 'toggle-memory-provenance':
|
|
130
|
+
// Expand/collapse the provenance chip's drill-in. No-op when the chip is
|
|
131
|
+
// not showing; still consumed so the composer never sees a stray Alt+M.
|
|
132
|
+
state.commandContext?.toggleMemoryProvenance?.();
|
|
133
|
+
return true;
|
|
134
|
+
|
|
135
|
+
case 'toggle-keep-awake': {
|
|
136
|
+
// Flip the daemon-held keep-awake toggle; the always-visible "sleep
|
|
137
|
+
// disabled" chip is the confirmation. Fire-and-forget (the seam renders on
|
|
138
|
+
// completion); errors are swallowed so a keystroke never throws.
|
|
139
|
+
const power = state.commandContext;
|
|
140
|
+
const current = power?.getPowerState?.().keepAwake ?? false;
|
|
141
|
+
void power?.setKeepAwake?.(!current);
|
|
142
|
+
return true;
|
|
143
|
+
}
|
|
144
|
+
|
|
122
145
|
case 'screen-clear':
|
|
123
146
|
state.commandContext?.clearScreen?.();
|
|
124
147
|
return true;
|
|
@@ -139,7 +162,7 @@ export function handleGlobalShortcutToken(
|
|
|
139
162
|
// in-panel action (FleetPanel session-tab detach) before it closes the
|
|
140
163
|
// panel outright — see Panel.interceptPanelClose's doc comment.
|
|
141
164
|
if (active?.interceptPanelClose?.()) {
|
|
142
|
-
//
|
|
165
|
+
// fix: a consumed Ctrl+X (the Fleet panel's session-tab detach)
|
|
143
166
|
// used to leave panelFocused untouched, so focus stayed on the panel
|
|
144
167
|
// — the evaluator's "Ctrl+X detach landed focus in the panel and a
|
|
145
168
|
// typed question became nav keys". Detach is a leave-taking action:
|
|
@@ -203,7 +226,7 @@ export function handleGlobalShortcutToken(
|
|
|
203
226
|
// Alt+1..9: jump directly to the Nth workspace tab. Routed globally (like
|
|
204
227
|
// panel-tab-next/prev) so the jump works whether focus is on the prompt or
|
|
205
228
|
// the workspace; gated on visibility, matching cyclePanelTab semantics.
|
|
206
|
-
//
|
|
229
|
+
// a chord jump is "I'm going panel-driving" (focus rule 1a) — the
|
|
207
230
|
// jump now also grabs keyboard focus, matching F2/Ctrl+O/Ctrl+P, so j/k
|
|
208
231
|
// land in the newly-active tab immediately instead of the composer.
|
|
209
232
|
const pm = state.panelManager;
|
|
@@ -218,9 +241,9 @@ export function handleGlobalShortcutToken(
|
|
|
218
241
|
}
|
|
219
242
|
|
|
220
243
|
case 'panel-ops': {
|
|
221
|
-
// Ctrl+O: TOGGLE the Fleet panel (
|
|
244
|
+
// Ctrl+O: TOGGLE the Fleet panel (— same semantics as F2 above; see
|
|
222
245
|
// toggleFleetPanel's doc comment). The former Ops Control panel was
|
|
223
|
-
// retired to an 'ops-control' -> 'fleet' alias
|
|
246
|
+
// retired to an 'ops-control' -> 'fleet' alias; rather than route
|
|
224
247
|
// through the now-aliased openOpsPanel callback (which opens without
|
|
225
248
|
// transferring focus), this operates on 'fleet' directly.
|
|
226
249
|
toggleFleetPanel(state);
|
|
@@ -403,7 +426,7 @@ export function handleGlobalShortcutToken(
|
|
|
403
426
|
}
|
|
404
427
|
|
|
405
428
|
/**
|
|
406
|
-
* toggleFleetPanel — the shared F2 / Ctrl+O TOGGLE (
|
|
429
|
+
* toggleFleetPanel — the shared F2 / Ctrl+O TOGGLE (item 2): if the
|
|
407
430
|
* Fleet panel is open AND the panel workspace currently owns keyboard focus
|
|
408
431
|
* with Fleet as the active tab, the chord CLOSES it and returns focus to the
|
|
409
432
|
* composer; if Fleet is open but not the focused/active tab, the chord brings
|
package/src/input/handler.ts
CHANGED
|
@@ -194,7 +194,7 @@ export class InputHandler implements InputHandlerLike {
|
|
|
194
194
|
public blockActionsMenu = new BlockActionsMenu();
|
|
195
195
|
public settingsModal = new SettingsModal();
|
|
196
196
|
public configModal = new ConfigModal();
|
|
197
|
-
/**
|
|
197
|
+
/** item 5 — paste-flood guard state, mutated in place across tokens (never reallocated). */
|
|
198
198
|
private panelBurstGuard: PanelBurstGuardState = { timestamps: [], suspended: false, hintShown: false };
|
|
199
199
|
public mcpWorkspace = new McpWorkspace();
|
|
200
200
|
public onboardingWizard = new OnboardingWizardController();
|
|
@@ -277,7 +277,7 @@ export class InputHandler implements InputHandlerLike {
|
|
|
277
277
|
uiServices.providers.benchmarkStore,
|
|
278
278
|
uiServices.providers.providerRegistry,
|
|
279
279
|
);
|
|
280
|
-
//
|
|
280
|
+
// retirement: ProcessModal/LiveTailModal/AgentDetailModal were removed
|
|
281
281
|
// — F2 now opens the Fleet panel, which subsumes the live process tree.
|
|
282
282
|
this.bookmarkModal = new BookmarkModal(uiServices.shell.bookmarkManager);
|
|
283
283
|
this.sessionPickerModal = new SessionPickerModal(uiServices.sessions.sessionManager, uiServices.sessions.sessionBroker);
|
package/src/input/keybindings.ts
CHANGED
|
@@ -68,7 +68,10 @@ export type KeyAction =
|
|
|
68
68
|
| 'kill-to-start'
|
|
69
69
|
| 'kill-word-forward'
|
|
70
70
|
| 'yank'
|
|
71
|
-
| 'yank-pop'
|
|
71
|
+
| 'yank-pop'
|
|
72
|
+
| 'cancel-tool-call'
|
|
73
|
+
| 'toggle-keep-awake'
|
|
74
|
+
| 'toggle-memory-provenance';
|
|
72
75
|
|
|
73
76
|
/** Human-readable description for each action (used in /keybindings display). */
|
|
74
77
|
export const ACTION_DESCRIPTIONS: Record<KeyAction, string> = {
|
|
@@ -111,6 +114,9 @@ export const ACTION_DESCRIPTIONS: Record<KeyAction, string> = {
|
|
|
111
114
|
'kill-word-forward': 'Kill word forward into kill ring (Alt+D)',
|
|
112
115
|
'yank': 'Yank (paste) from kill ring (Ctrl+Shift+Y)',
|
|
113
116
|
'yank-pop': 'Rotate kill ring and yank next entry (Alt+Y)',
|
|
117
|
+
'cancel-tool-call': 'Cancel the running tool call (the turn continues)',
|
|
118
|
+
'toggle-keep-awake': 'Toggle keep-awake (the "sleep disabled" chip)',
|
|
119
|
+
'toggle-memory-provenance': 'List/hide the memories a turn used (provenance chip drill-in)',
|
|
114
120
|
};
|
|
115
121
|
|
|
116
122
|
/** Default key bindings for all actions. */
|
|
@@ -147,8 +153,8 @@ export const DEFAULT_KEYBINDINGS: Record<KeyAction, KeyCombo[]> = {
|
|
|
147
153
|
'panel-tab-8': [{ key: '8', alt: true }],
|
|
148
154
|
'panel-tab-9': [{ key: '9', alt: true }],
|
|
149
155
|
// Ctrl+O: TOGGLE the Fleet panel (open+focus / bring-to-front+focus / close
|
|
150
|
-
// — see toggleFleetPanel in handler-shortcuts.ts
|
|
151
|
-
// Control panel was retired to an 'ops-control' -> 'fleet' alias
|
|
156
|
+
// — see toggleFleetPanel in handler-shortcuts.ts). The former Ops
|
|
157
|
+
// Control panel was retired to an 'ops-control' -> 'fleet' alias; the
|
|
152
158
|
// binding is KEPT (repointed, not removed) so the Ctrl+O muscle memory still
|
|
153
159
|
// lands somewhere useful. Routed globally in handleGlobalShortcutToken.
|
|
154
160
|
'panel-ops': [{ key: 'o', ctrl: true }],
|
|
@@ -189,6 +195,18 @@ export const DEFAULT_KEYBINDINGS: Record<KeyAction, KeyCombo[]> = {
|
|
|
189
195
|
'yank': [{ key: 'y', ctrl: true, shift: true }],
|
|
190
196
|
// Alt+Y: yank-pop (rotate ring after yank)
|
|
191
197
|
'yank-pop': [{ key: 'y', alt: true }],
|
|
198
|
+
// Alt+C: cancel the currently-running tool call (the turn continues). Alt+C
|
|
199
|
+
// is unused by any other default and representable by the tokenizer's
|
|
200
|
+
// { key, alt } combo form. Ctrl+C stays whole-input clear/cancel; this is the
|
|
201
|
+
// narrower per-tool stop.
|
|
202
|
+
'cancel-tool-call': [{ key: 'c', alt: true }],
|
|
203
|
+
// Alt+A: toggle keep-awake (the one-key affordance for the "sleep disabled"
|
|
204
|
+
// chip). Alt+A is unused by any other default. The always-visible chip is the
|
|
205
|
+
// confirmation of the toggle's state.
|
|
206
|
+
'toggle-keep-awake': [{ key: 'a', alt: true }],
|
|
207
|
+
// Alt+M: expand/collapse the "used N memories" provenance chip's drill-in.
|
|
208
|
+
// Alt+M is unused by any other default. A no-op when the chip is not showing.
|
|
209
|
+
'toggle-memory-provenance': [{ key: 'm', alt: true }],
|
|
192
210
|
};
|
|
193
211
|
|
|
194
212
|
/** Resolved overrides type: each key can be a single combo or array. */
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
*/
|
|
16
16
|
|
|
17
17
|
import type { ConfigKey } from '../../config/index.ts';
|
|
18
|
-
import {
|
|
18
|
+
import { getFeatureSetting, isFeatureDefaultEnabled } from '../../runtime/feature-settings.ts';
|
|
19
19
|
import type {
|
|
20
20
|
OnboardingWizardControllerLike,
|
|
21
21
|
OnboardingWizardFieldDefinition,
|
|
@@ -94,12 +94,13 @@ export const FEATURE_ONBOARDING_SECTIONS: readonly FeatureSection[] = [
|
|
|
94
94
|
hint: 'When the exec sandbox leaves a command on ask, a model pass annotates the ask with stated reasons. Never converts allow to deny; never touches the frozen catastrophic block.',
|
|
95
95
|
subOptions: [
|
|
96
96
|
{
|
|
97
|
-
key: '
|
|
98
|
-
configKey: 'sandbox.
|
|
99
|
-
label: '
|
|
100
|
-
hint: '
|
|
101
|
-
valueType: '
|
|
102
|
-
|
|
97
|
+
key: 'mode',
|
|
98
|
+
configKey: 'sandbox.judgment' as ConfigKey,
|
|
99
|
+
label: 'Judgment mode',
|
|
100
|
+
hint: 'annotate (default) proposes a verdict with stated reasons and the human still decides; auto-approve additionally auto-approves looks-safe verdicts; off keeps plain asks.',
|
|
101
|
+
valueType: 'enum',
|
|
102
|
+
options: ['off', 'annotate', 'auto-approve'],
|
|
103
|
+
defaultValue: 'annotate',
|
|
103
104
|
},
|
|
104
105
|
],
|
|
105
106
|
},
|
|
@@ -283,23 +284,18 @@ export const FEATURE_ONBOARDING_SECTIONS: readonly FeatureSection[] = [
|
|
|
283
284
|
{
|
|
284
285
|
flagId: 'integration-delivery-slo',
|
|
285
286
|
label: 'Integration delivery SLO',
|
|
286
|
-
hint: 'Retry with backoff and a dead-letter queue for Slack/Discord/webhook delivery.
|
|
287
|
-
subOptions: [
|
|
288
|
-
{
|
|
289
|
-
key: 'slo',
|
|
290
|
-
configKey: 'integrations.delivery.sloEnforced' as ConfigKey,
|
|
291
|
-
label: 'Enforce delivery SLO',
|
|
292
|
-
hint: 'Yes logs dead-letter events at error level and surfaces them in diagnostics; No is warn-level only (default).',
|
|
293
|
-
valueType: 'boolean',
|
|
294
|
-
defaultValue: 'no',
|
|
295
|
-
},
|
|
296
|
-
],
|
|
287
|
+
hint: 'Retry with backoff and a dead-letter queue for Slack/Discord/webhook delivery, with dead-letter events surfaced in diagnostics. On by default; exposes /notify dlq and /notify replay. Retry bounds are tuned in /settings.',
|
|
297
288
|
},
|
|
298
289
|
{
|
|
299
290
|
flagId: 'adaptive-execution-planner',
|
|
300
291
|
label: 'Adaptive execution planner',
|
|
301
292
|
hint: 'Score single/cohort/background/remote strategies each turn and pick the best. Exposes /plan commands.',
|
|
302
293
|
},
|
|
294
|
+
{
|
|
295
|
+
flagId: 'daemon-auto-update',
|
|
296
|
+
label: 'Daemon auto-update',
|
|
297
|
+
hint: 'The daemon checks for a new release hourly, checksum-verifies it, swaps binaries only at a no-active-work moment, and keeps the previous binary for one-command rollback. On by default; update.auto turns it off and update.intervalMinutes tunes the cadence in /settings.',
|
|
298
|
+
},
|
|
303
299
|
{
|
|
304
300
|
flagId: 'relay-connect',
|
|
305
301
|
label: 'Outbound zero-knowledge relay',
|
|
@@ -355,10 +351,10 @@ export const FEATURE_ONBOARDING_SECTIONS: readonly FeatureSection[] = [
|
|
|
355
351
|
key: 'backend',
|
|
356
352
|
configKey: 'tools.overflowSpillBackend' as ConfigKey,
|
|
357
353
|
label: 'Spill backend',
|
|
358
|
-
hint: '
|
|
354
|
+
hint: 'ledger (default when this feature is on) or diagnostics; file is the baseline used while the feature is off.',
|
|
359
355
|
valueType: 'enum',
|
|
360
356
|
options: ['file', 'ledger', 'diagnostics'],
|
|
361
|
-
defaultValue: '
|
|
357
|
+
defaultValue: 'ledger',
|
|
362
358
|
},
|
|
363
359
|
],
|
|
364
360
|
},
|
|
@@ -398,9 +394,9 @@ export function featureSubOptionFieldId(flagId: string, key: string): string {
|
|
|
398
394
|
return `feature.${flagId}.${key}`;
|
|
399
395
|
}
|
|
400
396
|
|
|
401
|
-
/** Whether a
|
|
397
|
+
/** Whether a feature defaults to enabled (drives the toggle's default-checked state). */
|
|
402
398
|
export function isFeatureDefaultOn(flagId: string): boolean {
|
|
403
|
-
return
|
|
399
|
+
return isFeatureDefaultEnabled(flagId);
|
|
404
400
|
}
|
|
405
401
|
|
|
406
402
|
/** Every flag id reachable through the guided feature steps. */
|
|
@@ -498,10 +494,12 @@ export function buildFeatureUnitSteps(
|
|
|
498
494
|
|
|
499
495
|
/**
|
|
500
496
|
* Fold the guided feature selections into apply operations: for each unit, when
|
|
501
|
-
* the user's choice differs from the
|
|
502
|
-
* when the feature is enabled, write its implied config, prerequisite
|
|
503
|
-
* chosen sub-options.
|
|
504
|
-
*
|
|
497
|
+
* the user's choice differs from the feature's default, record the desired
|
|
498
|
+
* state; when the feature is enabled, write its implied config, prerequisite
|
|
499
|
+
* features, and chosen sub-options. Desired states are collected into
|
|
500
|
+
* `overrides` (flushed by the caller as plain domain-settings writes on each
|
|
501
|
+
* feature's enablement key, together with the surface capabilities); other
|
|
502
|
+
* config writes go straight to setConfig.
|
|
505
503
|
*/
|
|
506
504
|
export function applyFeatureUnitOperations(
|
|
507
505
|
controller: OnboardingWizardControllerLike,
|
|
@@ -521,15 +519,24 @@ export function applyFeatureUnitOperations(
|
|
|
521
519
|
for (const prerequisite of unit.requiresFlags ?? []) overrides.set(prerequisite, 'enabled');
|
|
522
520
|
for (const implied of unit.impliedConfig ?? []) setConfig(implied.key, implied.value);
|
|
523
521
|
|
|
522
|
+
const enablementKey = getFeatureSetting(unit.flagId)?.enablement.key;
|
|
524
523
|
for (const sub of unit.subOptions ?? []) {
|
|
525
524
|
const raw = controller.getStringFieldValue(featureSubOptionFieldId(unit.flagId, sub.key), sub.defaultValue);
|
|
525
|
+
let wrote = true;
|
|
526
526
|
if (sub.valueType === 'boolean') {
|
|
527
527
|
setConfig(sub.configKey, raw === 'yes');
|
|
528
528
|
} else if (sub.valueType === 'text') {
|
|
529
|
-
|
|
529
|
+
wrote = raw.length > 0;
|
|
530
|
+
if (wrote) setConfig(sub.configKey, raw);
|
|
530
531
|
} else {
|
|
531
532
|
setConfig(sub.configKey, raw);
|
|
532
533
|
}
|
|
534
|
+
// A sub-option that writes the unit's OWN enablement key (e.g. the
|
|
535
|
+
// provider-optimizer routing mode) is the authoritative enablement
|
|
536
|
+
// value — drop the pending override so the flush cannot clobber it.
|
|
537
|
+
if (wrote && enablementKey !== undefined && sub.configKey === enablementKey) {
|
|
538
|
+
overrides.delete(unit.flagId);
|
|
539
|
+
}
|
|
533
540
|
}
|
|
534
541
|
}
|
|
535
542
|
}
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Provider-agnostic API-key fields for the onboarding provider step.
|
|
3
|
+
*
|
|
4
|
+
* Kept out of onboarding-wizard-steps.ts so the step file stays focused on step
|
|
5
|
+
* assembly. Both pieces read from the same registration-truth targets so the
|
|
6
|
+
* inventory line and the per-provider masked fields never disagree.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import type { ProviderKeyCaptureTarget, SubscriptionStatusTarget } from '../../runtime/onboarding/provider-key-capture.ts';
|
|
10
|
+
import { deriveSubscriptionStatusTargets, providerKeyFieldId, subscriptionStatusFieldId } from '../../runtime/onboarding/provider-key-capture.ts';
|
|
11
|
+
import type { OnboardingProviderAccountRecord } from '../../runtime/onboarding/types.ts';
|
|
12
|
+
import type {
|
|
13
|
+
OnboardingWizardMaskedFieldDefinition,
|
|
14
|
+
OnboardingWizardStatusFieldDefinition,
|
|
15
|
+
} from './onboarding-wizard-types.ts';
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Providers with a wired in-wizard subscription sign-in flow. This is DATA — the
|
|
19
|
+
* set of providers whose OAuth login the wizard can start/finish itself — not an
|
|
20
|
+
* assumption that any one vendor is the only subscription option.
|
|
21
|
+
*/
|
|
22
|
+
const SUBSCRIPTION_SIGN_IN_PROVIDER_IDS = new Set<string>(['openai']);
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* One subscription-status row per subscription-capable provider. The row's copy
|
|
26
|
+
* names the provider from data — never a vendor hardcoded in the field set — so
|
|
27
|
+
* the status surface tracks whatever subscription-capable providers the runtime
|
|
28
|
+
* reports, not a single assumed one.
|
|
29
|
+
*/
|
|
30
|
+
export function buildSubscriptionStatusFields(
|
|
31
|
+
targets: readonly SubscriptionStatusTarget[],
|
|
32
|
+
): OnboardingWizardStatusFieldDefinition[] {
|
|
33
|
+
return targets.map((target) => ({
|
|
34
|
+
kind: 'status',
|
|
35
|
+
id: subscriptionStatusFieldId(target.providerId),
|
|
36
|
+
label: `${target.providerId} subscription status`,
|
|
37
|
+
hint: target.active
|
|
38
|
+
? `A ${target.providerId} subscription session is already available.`
|
|
39
|
+
: target.pending
|
|
40
|
+
? `A ${target.providerId} subscription login is pending.`
|
|
41
|
+
: `No ${target.providerId} subscription session was found in the current runtime state.`,
|
|
42
|
+
defaultValue: target.active ? 'Active' : target.pending ? 'Pending' : 'Not detected',
|
|
43
|
+
}));
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/** The provider-agnostic subscription posture the provider step renders. */
|
|
47
|
+
export interface SubscriptionPosture {
|
|
48
|
+
readonly statusFields: OnboardingWizardStatusFieldDefinition[];
|
|
49
|
+
/** The provider whose in-wizard sign-in flow applies now, or null. */
|
|
50
|
+
readonly signInProviderId: string | null;
|
|
51
|
+
readonly signInPending: boolean;
|
|
52
|
+
readonly summaryLine: string;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Derive the subscription posture for the provider step: one status row per
|
|
57
|
+
* subscription-capable provider, plus which (if any) provider's in-wizard OAuth
|
|
58
|
+
* sign-in flow applies right now (a subscription-capable, sign-in-wired provider
|
|
59
|
+
* that is not already active).
|
|
60
|
+
*/
|
|
61
|
+
export function buildSubscriptionPosture(
|
|
62
|
+
activeProviderIds: readonly string[],
|
|
63
|
+
pendingProviderIds: readonly string[],
|
|
64
|
+
providers: readonly OnboardingProviderAccountRecord[] | undefined,
|
|
65
|
+
): SubscriptionPosture {
|
|
66
|
+
const targets = deriveSubscriptionStatusTargets(providers, activeProviderIds, pendingProviderIds);
|
|
67
|
+
const signIn = targets.find((target) => SUBSCRIPTION_SIGN_IN_PROVIDER_IDS.has(target.providerId) && !target.active);
|
|
68
|
+
const activeCount = targets.filter((target) => target.active).length;
|
|
69
|
+
const pendingCount = targets.filter((target) => target.pending).length;
|
|
70
|
+
return {
|
|
71
|
+
statusFields: buildSubscriptionStatusFields(targets),
|
|
72
|
+
signInProviderId: signIn?.providerId ?? null,
|
|
73
|
+
signInPending: signIn?.pending ?? false,
|
|
74
|
+
summaryLine: targets.length === 0
|
|
75
|
+
? 'Subscriptions: none detected'
|
|
76
|
+
: `Subscriptions: ${activeCount} active, ${pendingCount} pending across ${targets.length} provider(s)`,
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/** The status line summarizing how many key-accepting providers are configured. */
|
|
81
|
+
export function buildProviderKeyInventoryField(
|
|
82
|
+
targets: readonly ProviderKeyCaptureTarget[],
|
|
83
|
+
): OnboardingWizardStatusFieldDefinition {
|
|
84
|
+
const configuredCount = targets.filter((target) => target.configured).length;
|
|
85
|
+
return {
|
|
86
|
+
kind: 'status',
|
|
87
|
+
id: 'providers.api-key-inventory',
|
|
88
|
+
label: 'Provider API keys',
|
|
89
|
+
hint: targets.length === 0
|
|
90
|
+
? 'No key-accepting providers are registered yet in the current runtime state.'
|
|
91
|
+
: `${targets.length} provider(s) accept an API key; ${configuredCount} already configured. Values stay masked.`,
|
|
92
|
+
defaultValue: targets.length === 0
|
|
93
|
+
? 'None detected'
|
|
94
|
+
: `${configuredCount}/${targets.length} configured`,
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/** One masked field per key-accepting provider, keyed off registration truth. */
|
|
99
|
+
export function buildProviderKeyMaskedFields(
|
|
100
|
+
targets: readonly ProviderKeyCaptureTarget[],
|
|
101
|
+
): OnboardingWizardMaskedFieldDefinition[] {
|
|
102
|
+
return targets.map((target) => ({
|
|
103
|
+
kind: 'masked',
|
|
104
|
+
id: providerKeyFieldId(target.providerId),
|
|
105
|
+
label: `${target.providerId} API key`,
|
|
106
|
+
hint: target.configured
|
|
107
|
+
? `A key for ${target.providerId} is already stored. Leave blank to keep it; enter a new key to replace it through the secret manager.`
|
|
108
|
+
: `Optional: enter an API key for ${target.providerId} now. The value is stored through the secret manager, not in config.`,
|
|
109
|
+
placeholder: target.configured ? 'already configured' : 'paste key',
|
|
110
|
+
defaultValue: '',
|
|
111
|
+
}));
|
|
112
|
+
}
|