@pellux/goodvibes-tui 0.19.24 → 0.19.25

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.
Files changed (68) hide show
  1. package/CHANGELOG.md +5 -0
  2. package/README.md +5 -5
  3. package/bin/goodvibes +5 -0
  4. package/bin/goodvibes-daemon +5 -0
  5. package/docs/foundation-artifacts/operator-contract.json +1 -1
  6. package/package.json +2 -2
  7. package/src/cli/completion.ts +89 -0
  8. package/src/cli/config-overrides.ts +159 -0
  9. package/src/cli/endpoints.ts +63 -0
  10. package/src/cli/entrypoint.ts +155 -0
  11. package/src/cli/help.ts +122 -0
  12. package/src/cli/index.ts +8 -0
  13. package/src/cli/management-commands.ts +576 -0
  14. package/src/cli/management.ts +693 -0
  15. package/src/cli/parser.ts +367 -0
  16. package/src/cli/status.ts +112 -0
  17. package/src/cli/tui-startup.ts +32 -0
  18. package/src/cli/types.ts +63 -0
  19. package/src/cli-flags.ts +17 -55
  20. package/src/config/index.ts +1 -1
  21. package/src/config/secrets.ts +44 -0
  22. package/src/daemon/cli.ts +62 -11
  23. package/src/input/command-registry.ts +3 -0
  24. package/src/input/commands/guidance-runtime.ts +9 -4
  25. package/src/input/commands/local-runtime.ts +21 -7
  26. package/src/input/commands/local-setup.ts +31 -38
  27. package/src/input/commands/onboarding-runtime.ts +14 -0
  28. package/src/input/commands/runtime-services.ts +9 -0
  29. package/src/input/commands.ts +2 -0
  30. package/src/input/feed-context-factory.ts +8 -1
  31. package/src/input/handler-feed.ts +13 -8
  32. package/src/input/handler-interactions.ts +266 -0
  33. package/src/input/handler-modal-stack.ts +23 -3
  34. package/src/input/handler-modal-token-routes.ts +23 -1
  35. package/src/input/handler-onboarding.ts +696 -0
  36. package/src/input/handler-picker-routes.ts +15 -7
  37. package/src/input/handler-ui-state.ts +58 -0
  38. package/src/input/handler.ts +120 -246
  39. package/src/input/onboarding/handler-onboarding-routes.ts +105 -0
  40. package/src/input/onboarding/onboarding-wizard-apply.ts +211 -0
  41. package/src/input/onboarding/onboarding-wizard-constants.ts +148 -0
  42. package/src/input/onboarding/onboarding-wizard-external-surfaces.ts +712 -0
  43. package/src/input/onboarding/onboarding-wizard-helpers.ts +218 -0
  44. package/src/input/onboarding/onboarding-wizard-rules.ts +224 -0
  45. package/src/input/onboarding/onboarding-wizard-state.ts +354 -0
  46. package/src/input/onboarding/onboarding-wizard-steps.ts +642 -0
  47. package/src/input/onboarding/onboarding-wizard-types.ts +170 -0
  48. package/src/input/onboarding/onboarding-wizard.ts +594 -0
  49. package/src/main.ts +32 -39
  50. package/src/panels/builtin/operations.ts +0 -10
  51. package/src/panels/index.ts +0 -1
  52. package/src/renderer/conversation-overlays.ts +6 -0
  53. package/src/renderer/help-overlay.ts +1 -1
  54. package/src/renderer/onboarding/onboarding-wizard.ts +533 -0
  55. package/src/runtime/bootstrap-core.ts +1 -0
  56. package/src/runtime/bootstrap.ts +123 -0
  57. package/src/runtime/onboarding/apply.ts +685 -0
  58. package/src/runtime/onboarding/derivation.ts +495 -0
  59. package/src/runtime/onboarding/index.ts +7 -0
  60. package/src/runtime/onboarding/markers.ts +161 -0
  61. package/src/runtime/onboarding/snapshot.ts +400 -0
  62. package/src/runtime/onboarding/state.ts +140 -0
  63. package/src/runtime/onboarding/types.ts +402 -0
  64. package/src/runtime/onboarding/verify.ts +233 -0
  65. package/src/runtime/ui-services.ts +16 -0
  66. package/src/shell/ui-openers.ts +12 -2
  67. package/src/version.ts +1 -1
  68. package/src/panels/welcome-panel.ts +0 -64
@@ -0,0 +1,266 @@
1
+ import { buildProviderAccountSnapshot } from '@pellux/goodvibes-sdk/platform/runtime/provider-accounts/registry';
2
+ import type { OnboardingWizardMode } from './onboarding/onboarding-wizard.ts';
3
+ import { collectOnboardingSnapshot } from '../runtime/onboarding/index.ts';
4
+ import { cleanupMarkerRegistry, expandPrompt, findMarkerAtPos, handleBlockCopy, handleBlockRerun, handleBlockSave, handleBlockToggle, handleBookmark, handleClipboardPaste, handleCopy, handleCtrlC, handleDiffApply, registerPaste } from './handler-content-actions.ts';
5
+ import { clearModalStack, handleEscape, modalOpened } from './handler-modal-stack.ts';
6
+ import { openOnboardingWizardState, type OpenOnboardingWizardOptions } from './handler-ui-state.ts';
7
+ import type { InputHandler } from './handler.ts';
8
+
9
+ export function openOnboardingWizardForHandler(
10
+ handler: InputHandler,
11
+ modeOrOptions: OnboardingWizardMode | OpenOnboardingWizardOptions = 'new',
12
+ ): void {
13
+ const options = typeof modeOrOptions === 'string' ? { mode: modeOrOptions } : modeOrOptions;
14
+ if (!handler.modalStack.includes('onboarding')) handler.modalOpened('onboarding');
15
+ handler.clearOnboardingModelPickerCancelState();
16
+ openOnboardingWizardState(handler.onboardingWizard, options);
17
+ const hydrationSerial = ++handler.onboardingHydrationSerial;
18
+ if (options.preload === undefined) {
19
+ handler.onboardingWizard.beginRuntimeHydration();
20
+ void handler.hydrateOnboardingWizardFromRuntime(hydrationSerial);
21
+ }
22
+ handler.requestRender();
23
+ }
24
+
25
+ export async function hydrateOnboardingWizardFromRuntimeForHandler(handler: InputHandler, hydrationSerial: number): Promise<void> {
26
+ try {
27
+ const snapshot = await collectOnboardingSnapshot({
28
+ config: handler.uiServices.platform.configManager,
29
+ shellPaths: handler.uiServices.environment.shellPaths,
30
+ acknowledgementScope: 'project',
31
+ subscriptions: handler.uiServices.platform.subscriptionManager,
32
+ secrets: handler.uiServices.platform.secretsManager,
33
+ auth: handler.uiServices.platform.localUserAuthManager,
34
+ services: handler.uiServices.platform.serviceRegistry,
35
+ surfaces: {
36
+ list: () => handler.uiServices.platform.surfaceRegistry.syncConfiguredSurfaces(),
37
+ },
38
+ providerAccounts: {
39
+ loadSnapshot: () => buildProviderAccountSnapshot({
40
+ providerRegistry: handler.uiServices.providers.providerRegistry,
41
+ serviceRegistry: handler.uiServices.platform.serviceRegistry,
42
+ subscriptionManager: handler.uiServices.platform.subscriptionManager,
43
+ secretsManager: handler.uiServices.platform.secretsManager,
44
+ }),
45
+ },
46
+ });
47
+ if (!handler.onboardingWizard.active || hydrationSerial !== handler.onboardingHydrationSerial) return;
48
+ handler.onboardingWizard.hydrateRuntimeState({ snapshot }, { resetValues: true });
49
+ handler.requestRender();
50
+ } catch (error) {
51
+ if (!handler.onboardingWizard.active || hydrationSerial !== handler.onboardingHydrationSerial) return;
52
+ const message = error instanceof Error ? error.message : String(error);
53
+ handler.onboardingWizard.failRuntimeHydration(message);
54
+ handler.commandContext?.print?.(`Onboarding runtime snapshot failed: ${message}`);
55
+ handler.requestRender();
56
+ }
57
+ }
58
+
59
+ export function registerPasteForHandler(handler: InputHandler, content: string): string {
60
+ const result = registerPaste({
61
+ pasteRegistry: handler.pasteRegistry,
62
+ nextPasteId: handler.nextPasteId,
63
+ imageRegistry: handler.imageRegistry,
64
+ nextImageId: handler.nextImageId,
65
+ }, content, handler.uiServices.environment.shellPaths.workingDirectory);
66
+ handler.nextPasteId = result.nextPasteId;
67
+ handler.nextImageId = result.nextImageId;
68
+ return result.marker;
69
+ }
70
+
71
+ /**
72
+ * expandPrompt - Replaces paste markers with actual content.
73
+ * If image markers are present, returns ContentPart[] for multimodal delivery.
74
+ * Otherwise returns a plain string.
75
+ */
76
+ export function expandPromptForHandler(handler: InputHandler, text: string) {
77
+ return expandPrompt(handler.pasteRegistry, handler.imageRegistry, text, handler.uiServices.environment.shellPaths.workingDirectory);
78
+ }
79
+
80
+ /**
81
+ * getImageAttachments - Returns a copy of the current image registry.
82
+ * Callers can use this to attach images when building LLM messages.
83
+ */
84
+ export function getImageAttachmentsForHandler(handler: InputHandler): Map<string, { data: string; mediaType: string }> {
85
+ return new Map(handler.imageRegistry);
86
+ }
87
+
88
+ /**
89
+ * findMarkerAtPos - Returns the start/end of an atomic marker if pos is inside one.
90
+ * Used to make backspace/delete/arrow treat markers as single units.
91
+ */
92
+ /**
93
+ * cleanupMarkerRegistry - If the given marker text is an IMAGE marker,
94
+ * parses its ID and removes it from imageRegistry.
95
+ */
96
+ export function cleanupMarkerRegistryForHandler(handler: InputHandler, markerText: string): void {
97
+ cleanupMarkerRegistry(handler.imageRegistry, markerText);
98
+ }
99
+
100
+ export function findMarkerAtPosForHandler(handler: InputHandler, pos: number): { start: number; end: number } | null {
101
+ return findMarkerAtPos(handler.prompt, pos);
102
+ }
103
+
104
+ export function handleCopyForHandler(handler: InputHandler): void {
105
+ handleCopy(handler.selection, handler.getHistory, handler.requestRender, () => {
106
+ handler.lastCopyTime = Date.now();
107
+ });
108
+ }
109
+
110
+ /**
111
+ * handleBlockCopy - Ctrl+Y: Copy the content of the nearest code/tool block.
112
+ */
113
+ export function handleBlockCopyForHandler(handler: InputHandler): void {
114
+ handleBlockCopy(handler.conversationManager, handler.getScrollTop, handler.requestRender, () => {
115
+ handler.lastBlockCopyTime = Date.now();
116
+ });
117
+ }
118
+
119
+ /**
120
+ * handleBookmark - Ctrl+B: Toggle bookmark on the nearest block.
121
+ */
122
+ export function handleBookmarkForHandler(handler: InputHandler): void {
123
+ handleBookmark(handler.conversationManager, handler.getScrollTop, handler.requestRender, handler.uiServices.shell.bookmarkManager);
124
+ }
125
+
126
+ /**
127
+ * handleBlockSave - Ctrl+S: Save nearest block content to a file.
128
+ */
129
+ export function handleBlockSaveForHandler(handler: InputHandler): void {
130
+ handleBlockSave(handler.conversationManager, handler.getScrollTop, handler.requestRender, handler.uiServices.shell.bookmarkManager);
131
+ }
132
+
133
+ /**
134
+ * executeBlockAction - Execute a block action ID on the nearest block.
135
+ * Called when the user selects an action from the BlockActionsMenu.
136
+ */
137
+ export function executeBlockActionForHandler(handler: InputHandler, actionId: string): void {
138
+ switch (actionId) {
139
+ case 'copy': handler.handleBlockCopy(); break;
140
+ case 'bookmark': handler.handleBookmark(); break;
141
+ case 'toggle': handler.handleBlockToggle(); break;
142
+ case 'apply': handler.handleDiffApply(); break;
143
+ case 'rerun': handler.handleBlockRerun(); break;
144
+ }
145
+ }
146
+
147
+ /**
148
+ * handleBlockRerun - Re-run the tool call for the nearest tool block.
149
+ * Emits a tool-rerun event for the orchestrator to handle.
150
+ */
151
+ export function handleBlockRerunForHandler(handler: InputHandler): void {
152
+ handleBlockRerun(handler.conversationManager, handler.getScrollTop, handler.requestRender);
153
+ }
154
+
155
+ /**
156
+ * handleBlockToggle - Tab (non-command mode): Toggle collapse of nearest block.
157
+ */
158
+ export function handleBlockToggleForHandler(handler: InputHandler): void {
159
+ handleBlockToggle(handler.conversationManager, handler.getScrollTop, handler.requestRender);
160
+ }
161
+
162
+ /**
163
+ * handleDiffApply - Ctrl+A when a diff block is nearest: request approval and apply the diff.
164
+ * Returns true if a diff was found and applied (so caller can skip default Ctrl+A).
165
+ */
166
+ export function handleDiffApplyForHandler(handler: InputHandler): boolean {
167
+ return handleDiffApply(
168
+ handler.conversationManager,
169
+ handler.getScrollTop,
170
+ handler.commandContext,
171
+ handler.requestRender,
172
+ () => `diff-apply-${Date.now()}`,
173
+ 'write',
174
+ );
175
+ }
176
+
177
+ /**
178
+ * Handle Ctrl+C:
179
+ * - If prompt has text: clear it
180
+ * - If prompt is empty and LLM is thinking: cancel generation
181
+ * - If prompt is empty and idle: show exit notice (double = exit)
182
+ */
183
+ export function handleCtrlCForHandler(handler: InputHandler): void {
184
+ handleCtrlC(
185
+ handler.prompt,
186
+ () => handler.saveUndoState(),
187
+ (value) => { handler.prompt = value; },
188
+ (value) => { handler.cursorPos = value; },
189
+ handler.commandContext?.cancelGeneration,
190
+ handler.exitApp,
191
+ handler.requestRender,
192
+ handler.lastCtrlCTime,
193
+ (value) => { handler.lastCtrlCTime = value; },
194
+ (value) => { handler.showExitNotice = value; },
195
+ );
196
+ }
197
+
198
+ /**
199
+ * Handle Escape:
200
+ * - If prompt has text: clear it
201
+ * - If prompt is empty: cancel generation (double-tap not needed)
202
+ */
203
+ /**
204
+ * Record that a modal has been opened and push it onto the navigation stack.
205
+ * Call this EVERY time a modal opens (except inside openModal()).
206
+ *
207
+ * @param name - The modal identifier (e.g. 'settings', 'help', 'process').
208
+ */
209
+ export function modalOpenedForHandler(handler: InputHandler, name: string): void {
210
+ modalOpened(handler, name);
211
+ }
212
+
213
+ /**
214
+ * Clear the modal navigation stack on non-modal user input (e.g. submit).
215
+ */
216
+ export function clearModalStackForHandler(handler: InputHandler): void {
217
+ clearModalStack(handler.modalStack);
218
+ }
219
+
220
+ export function handleEscapeForHandler(handler: InputHandler): void {
221
+ const result = handleEscape({
222
+ helpOverlayActive: handler.helpOverlayActive,
223
+ shortcutsOverlayActive: handler.shortcutsOverlayActive,
224
+ bookmarkModal: handler.bookmarkModal,
225
+ agentDetailModal: handler.agentDetailModal,
226
+ liveTailModal: handler.liveTailModal,
227
+ settingsModal: handler.settingsModal,
228
+ sessionPickerModal: handler.sessionPickerModal,
229
+ profilePickerModal: handler.profilePickerModal,
230
+ contextInspectorModal: handler.contextInspectorModal,
231
+ processModal: handler.processModal,
232
+ modelPicker: handler.modelPicker,
233
+ filePicker: handler.filePicker,
234
+ blockActionsMenu: handler.blockActionsMenu,
235
+ selectionModal: handler.selectionModal,
236
+ onboardingWizard: handler.onboardingWizard,
237
+ commandMode: handler.commandMode,
238
+ modalStack: handler.modalStack,
239
+ modalReturnFocus: handler.modalReturnFocus,
240
+ panelFocused: handler.panelFocused,
241
+ indicatorFocused: handler.indicatorFocused,
242
+ prompt: handler.prompt,
243
+ cursorPos: handler.cursorPos,
244
+ requestRender: handler.requestRender,
245
+ saveUndoState: () => handler.saveUndoState(),
246
+ cancelGeneration: handler.commandContext?.cancelGeneration,
247
+ selectionCallback: handler.selectionCallback,
248
+ autocompleteReset: () => handler.autocomplete?.reset(),
249
+ autocompleteUpdate: (query: string) => handler.autocomplete?.update(query),
250
+ helpScrollOffset: handler.helpScrollOffset,
251
+ shortcutsScrollOffset: handler.shortcutsScrollOffset,
252
+ clearOnboardingModelPickerCancelState: () => handler.clearOnboardingModelPickerCancelState(),
253
+ restoreOnboardingModelPickerCancelState: () => handler.restoreOnboardingModelPickerCancelState(),
254
+ });
255
+ handler.prompt = result.prompt;
256
+ handler.cursorPos = result.cursorPos;
257
+ handler.commandMode = result.commandMode;
258
+ handler.helpOverlayActive = result.helpOverlayActive;
259
+ handler.helpScrollOffset = result.helpScrollOffset;
260
+ handler.shortcutsOverlayActive = result.shortcutsOverlayActive;
261
+ handler.shortcutsScrollOffset = result.shortcutsScrollOffset;
262
+ handler.selectionCallback = result.selectionCallback;
263
+ handler.panelFocused = result.panelFocused;
264
+ handler.indicatorFocused = result.indicatorFocused;
265
+ handler.modalReturnFocus = result.modalReturnFocus;
266
+ }
@@ -17,6 +17,8 @@ export function modalOpened(state: ModalStackState, name: string): void {
17
17
  if (getActiveModalName(state) === null && state.modalStack.length > 0) {
18
18
  state.modalStack.length = 0;
19
19
  }
20
+ if (state.modalStack[state.modalStack.length - 1] === name) return;
21
+ if (state.modalStack.includes(name)) return;
20
22
  if (state.modalStack.length === 0) {
21
23
  state.modalReturnFocus = state.indicatorFocused ? 'indicator' : state.panelFocused ? 'panel' : 'prompt';
22
24
  }
@@ -46,6 +48,8 @@ export type EscapeState = ModalStackState & {
46
48
  selectionModal: ModalStackState['selectionModal'];
47
49
  autocompleteReset: () => void;
48
50
  autocompleteUpdate?: (query: string) => void;
51
+ clearOnboardingModelPickerCancelState?: () => void;
52
+ restoreOnboardingModelPickerCancelState?: () => void;
49
53
  };
50
54
 
51
55
  export function handleEscape(state: EscapeState): {
@@ -59,6 +63,7 @@ export function handleEscape(state: EscapeState): {
59
63
  selectionCallback: ((result: SelectionResult | null) => void) | null;
60
64
  panelFocused: boolean;
61
65
  indicatorFocused: boolean;
66
+ modalReturnFocus: NonNullable<ModalStackState['modalReturnFocus']>;
62
67
  } {
63
68
  let prompt = state.prompt;
64
69
  let cursorPos = state.cursorPos;
@@ -70,6 +75,7 @@ export function handleEscape(state: EscapeState): {
70
75
  let selectionCallback = state.selectionCallback;
71
76
  let panelFocused = state.panelFocused;
72
77
  let indicatorFocused = state.indicatorFocused;
78
+ let modalReturnFocus: NonNullable<ModalStackState['modalReturnFocus']> = state.modalReturnFocus ?? 'prompt';
73
79
 
74
80
  const restoreFocus = (): void => {
75
81
  if (state.modalStack.length > 0 || getActiveModalName({
@@ -78,8 +84,9 @@ export function handleEscape(state: EscapeState): {
78
84
  shortcutsOverlayActive,
79
85
  commandMode,
80
86
  }) !== null) return;
81
- panelFocused = state.modalReturnFocus === 'panel';
82
- indicatorFocused = state.modalReturnFocus === 'indicator';
87
+ panelFocused = modalReturnFocus === 'panel';
88
+ indicatorFocused = modalReturnFocus === 'indicator';
89
+ modalReturnFocus = 'prompt';
83
90
  state.modalReturnFocus = 'prompt';
84
91
  };
85
92
 
@@ -97,6 +104,7 @@ export function handleEscape(state: EscapeState): {
97
104
  selectionCallback,
98
105
  panelFocused,
99
106
  indicatorFocused,
107
+ modalReturnFocus,
100
108
  };
101
109
  }
102
110
 
@@ -118,7 +126,10 @@ export function handleEscape(state: EscapeState): {
118
126
  closeProfilePicker: () => state.profilePickerModal.close(),
119
127
  closeContextInspector: () => state.contextInspectorModal.close(),
120
128
  closeProcess: () => state.processModal.close(),
121
- closeModelPicker: () => state.modelPicker.close(),
129
+ closeModelPicker: () => {
130
+ state.modelPicker.close();
131
+ state.restoreOnboardingModelPickerCancelState?.();
132
+ },
122
133
  closeFilePicker: () => state.filePicker.close(),
123
134
  closeBlockActions: () => state.blockActionsMenu.close(),
124
135
  closeSelection: () => {
@@ -127,6 +138,10 @@ export function handleEscape(state: EscapeState): {
127
138
  state.selectionModal.close();
128
139
  cb?.(null);
129
140
  },
141
+ closeOnboarding: () => {
142
+ state.onboardingWizard?.close();
143
+ state.clearOnboardingModelPickerCancelState?.();
144
+ },
130
145
  closeCommandMode: () => {
131
146
  commandMode = false;
132
147
  for (let i = state.modalStack.length - 1; i >= 0; i--) {
@@ -146,6 +161,7 @@ export function handleEscape(state: EscapeState): {
146
161
  openBookmark: () => state.bookmarkModal.open(),
147
162
  openProcess: () => state.processModal.open(),
148
163
  openContextInspector: () => state.contextInspectorModal.open(),
164
+ openOnboarding: () => state.onboardingWizard?.reopen(),
149
165
  openCommandMode: () => {
150
166
  commandMode = true;
151
167
  prompt = '/';
@@ -176,6 +192,7 @@ export function handleEscape(state: EscapeState): {
176
192
  selectionCallback,
177
193
  panelFocused,
178
194
  indicatorFocused,
195
+ modalReturnFocus,
179
196
  };
180
197
  }
181
198
 
@@ -200,6 +217,7 @@ export function handleEscape(state: EscapeState): {
200
217
  selectionCallback,
201
218
  panelFocused,
202
219
  indicatorFocused,
220
+ modalReturnFocus,
203
221
  };
204
222
  }
205
223
 
@@ -218,6 +236,7 @@ export function handleEscape(state: EscapeState): {
218
236
  selectionCallback,
219
237
  panelFocused,
220
238
  indicatorFocused,
239
+ modalReturnFocus,
221
240
  };
222
241
  }
223
242
 
@@ -233,5 +252,6 @@ export function handleEscape(state: EscapeState): {
233
252
  selectionCallback,
234
253
  panelFocused,
235
254
  indicatorFocused,
255
+ modalReturnFocus,
236
256
  };
237
257
  }
@@ -15,7 +15,9 @@ import type { ContextInspectorModal } from '../renderer/context-inspector.ts';
15
15
  import type { FilePickerModal } from './file-picker.ts';
16
16
  import type { BlockActionsMenu, BlockActionId } from '../renderer/block-actions.ts';
17
17
  import type { SearchManager } from './search.ts';
18
+ import type { OnboardingWizardAction, OnboardingWizardController } from './onboarding/onboarding-wizard.ts';
18
19
  import { handleHistorySearchToken, handleOverlayToken, handleSearchModeToken } from './handler-ui-state.ts';
20
+ import { handleOnboardingWizardToken } from './onboarding/handler-onboarding-routes.ts';
19
21
  import {
20
22
  handleBookmarkModalToken,
21
23
  handleProfilePickerToken,
@@ -41,6 +43,7 @@ export type ModalTokenRouteState = {
41
43
  settingsModal: SettingsModal;
42
44
  sessionPickerModal: SessionPickerModal;
43
45
  profilePickerModal: ProfilePickerModal;
46
+ onboardingWizard: OnboardingWizardController;
44
47
  helpOverlayActive: boolean;
45
48
  helpScrollOffset: number;
46
49
  shortcutsOverlayActive: boolean;
@@ -73,7 +76,14 @@ export type ModalTokenRouteState = {
73
76
  scroll: (delta: number) => void;
74
77
  getScrollTop: () => number;
75
78
  /** Callback to open the model picker with a specific target (helper or tool). Optional — only wired when available. */
76
- openModelPickerWithTarget?: (target: import('./model-picker.ts').ModelPickerTarget) => void;
79
+ openModelPickerWithTarget?: (
80
+ target: import('./model-picker.ts').ModelPickerTarget,
81
+ source?: 'settings' | 'onboarding',
82
+ ) => boolean;
83
+ clearOnboardingModelPickerCancelState?: () => void;
84
+ restoreOnboardingModelPickerCancelState?: () => void;
85
+ onModelPickerCommit?: () => boolean;
86
+ onOnboardingAction?: (action: OnboardingWizardAction) => void;
77
87
  };
78
88
 
79
89
  export function handleModalTokenRoutes(state: ModalTokenRouteState, token: InputToken): {
@@ -173,6 +183,18 @@ export function handleModalTokenRoutes(state: ModalTokenRouteState, token: Input
173
183
  getViewportHeight: state.getViewportHeight,
174
184
  requestRender: state.requestRender,
175
185
  handleEscape: state.handleEscape,
186
+ onModelPickerCommit: state.onModelPickerCommit,
187
+ }, token)) {
188
+ return withState(state, true);
189
+ }
190
+
191
+ if (handleOnboardingWizardToken({
192
+ onboardingWizard: state.onboardingWizard,
193
+ getViewportHeight: state.getViewportHeight,
194
+ requestRender: state.requestRender,
195
+ handleEscape: state.handleEscape,
196
+ openModelPickerWithTarget: state.openModelPickerWithTarget,
197
+ onAction: state.onOnboardingAction,
176
198
  }, token)) {
177
199
  return withState(state, true);
178
200
  }