@pellux/goodvibes-tui 0.19.31 → 0.19.33
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 +30 -0
- package/README.md +4 -2
- package/docs/foundation-artifacts/operator-contract.json +778 -494
- package/package.json +2 -2
- package/src/audio/player.ts +156 -0
- package/src/audio/spoken-turn-controller.ts +200 -0
- package/src/audio/spoken-turn-model-routing.ts +117 -0
- package/src/audio/spoken-turn-wiring.ts +44 -0
- package/src/audio/text-chunker.ts +110 -0
- package/src/cli/management.ts +26 -3
- package/src/cli/provider-auth-routes.ts +22 -0
- package/src/input/command-registry.ts +6 -0
- package/src/input/commands/tts-runtime.ts +334 -0
- package/src/input/commands.ts +2 -0
- package/src/input/handler-onboarding.ts +12 -0
- package/src/input/model-picker.ts +2 -1
- package/src/input/onboarding/onboarding-wizard-steps.ts +25 -2
- package/src/input/onboarding/onboarding-wizard-types.ts +2 -0
- package/src/main.ts +31 -30
- package/src/renderer/onboarding/onboarding-wizard.ts +38 -14
- package/src/renderer/ui-factory.ts +1 -1
- package/src/runtime/bootstrap-command-context.ts +6 -1
- package/src/runtime/bootstrap-command-parts.ts +10 -1
- package/src/runtime/bootstrap-shell.ts +2 -0
- package/src/shell/ui-openers.ts +11 -1
- package/src/version.ts +1 -1
package/src/main.ts
CHANGED
|
@@ -51,6 +51,11 @@ import { buildPersistedSessionContext, formatReturnContextForDisplay, getReturnC
|
|
|
51
51
|
import { summarizeError } from '@pellux/goodvibes-sdk/platform/utils/error-display';
|
|
52
52
|
import { prepareShellCliRuntime } from './cli/entrypoint.ts';
|
|
53
53
|
import { applyInitialTuiCliState } from './cli/tui-startup.ts';
|
|
54
|
+
import { wireSpokenTurnRuntime } from './audio/spoken-turn-wiring.ts';
|
|
55
|
+
import {
|
|
56
|
+
attachSpokenTurnModelRouting,
|
|
57
|
+
createSpokenTurnInputOptions,
|
|
58
|
+
} from './audio/spoken-turn-model-routing.ts';
|
|
54
59
|
|
|
55
60
|
const ALT_SCREEN_ENTER = '\x1b[?1049h';
|
|
56
61
|
const ALT_SCREEN_EXIT = '\x1b[?1049l';
|
|
@@ -72,7 +77,6 @@ async function main() {
|
|
|
72
77
|
homeDirectory: homedir(),
|
|
73
78
|
}, 'goodvibes');
|
|
74
79
|
|
|
75
|
-
// ── Bootstrap runtime subsystems via bootstrapRuntime.
|
|
76
80
|
const ctx: BootstrapContext = await bootstrapRuntime(stdout, {
|
|
77
81
|
configManager,
|
|
78
82
|
workingDir: bootstrapWorkingDir,
|
|
@@ -118,7 +122,6 @@ async function main() {
|
|
|
118
122
|
ctx.services.wrfcController.setPlanManager(ctx.services.planManager);
|
|
119
123
|
let activeConversationWidth = stdout.columns || 80;
|
|
120
124
|
conversation.setWidthProvider(() => activeConversationWidth);
|
|
121
|
-
// ── HITL UX mode — read from config and apply at startup ─────────────────
|
|
122
125
|
{
|
|
123
126
|
const hitlMode = configManager.get('behavior.hitlMode') as HITLMode | undefined;
|
|
124
127
|
if (hitlMode && (hitlMode === 'quiet' || hitlMode === 'balanced' || hitlMode === 'operator')) {
|
|
@@ -126,7 +129,6 @@ async function main() {
|
|
|
126
129
|
}
|
|
127
130
|
}
|
|
128
131
|
|
|
129
|
-
// Use the panel manager owned by the runtime service graph.
|
|
130
132
|
const panelManager = ctx.services.panelManager;
|
|
131
133
|
const buildSessionContinuityHints = () => {
|
|
132
134
|
const sessionSnapshot = uiServices.readModels.session.getSnapshot();
|
|
@@ -145,7 +147,6 @@ async function main() {
|
|
|
145
147
|
};
|
|
146
148
|
};
|
|
147
149
|
|
|
148
|
-
// Permission state — set while a permission prompt is blocking the orchestrator
|
|
149
150
|
let pendingPermission: PendingPermissionState | null = null;
|
|
150
151
|
approvalBroker.subscribe((approval) => {
|
|
151
152
|
if (!pendingPermission) return;
|
|
@@ -155,20 +156,13 @@ async function main() {
|
|
|
155
156
|
render();
|
|
156
157
|
});
|
|
157
158
|
|
|
158
|
-
// --- Streaming speed tracking (B2) ---
|
|
159
159
|
let streamStartTime = 0;
|
|
160
160
|
let streamDeltaCount = 0;
|
|
161
161
|
let streamTokenSpeed = 0;
|
|
162
162
|
|
|
163
163
|
let scrollTop = 0;
|
|
164
|
-
/** When true, view auto-scrolls to bottom on every render.
|
|
165
|
-
* False when user manually scrolls up. Reset on user input. */
|
|
166
164
|
let scrollLocked = true;
|
|
167
165
|
|
|
168
|
-
// lastGitInfo is a mutable ref provided by bootstrap (updated asynchronously)
|
|
169
|
-
// Use lastGitInfoRef.value inside render to get the current value.
|
|
170
|
-
|
|
171
|
-
/** Content width inside the prompt box (box width minus padding). */
|
|
172
166
|
const getPromptContentWidth = () => {
|
|
173
167
|
const w = stdout.columns || 80;
|
|
174
168
|
const boxMargin = 2;
|
|
@@ -195,23 +189,12 @@ async function main() {
|
|
|
195
189
|
scrollTop = Math.max(0, conversation.history.getLineCount() - vHeight);
|
|
196
190
|
};
|
|
197
191
|
|
|
198
|
-
// main.ts-owned unsub functions for shell-owned typed runtime subscriptions
|
|
199
|
-
// Bootstrap-owned unsubs are in ctx.bootstrapUnsubs and cleared by ctx.shutdown().
|
|
200
192
|
const unsubs: Array<() => void> = [];
|
|
201
|
-
|
|
202
|
-
// Crash recovery interval handle — cleared on exit
|
|
203
193
|
let recoveryInterval: ReturnType<typeof setInterval> | null = null;
|
|
204
|
-
|
|
205
|
-
// Recovery flow state
|
|
194
|
+
let stopSpokenOutputForExit: (() => void) | null = null;
|
|
206
195
|
let recoveryPending = false;
|
|
207
196
|
|
|
208
|
-
/**
|
|
209
|
-
* Full application teardown.
|
|
210
|
-
* Clears main.ts-owned listeners, calls ctx.shutdown() for logical teardown,
|
|
211
|
-
* then tears down the terminal and exits the process.
|
|
212
|
-
*/
|
|
213
197
|
const sigintHandler = (): void => input.feed('\x03');
|
|
214
|
-
// Track unhandled rejections to detect cascading failures
|
|
215
198
|
let _unhandledRejectionCount = 0;
|
|
216
199
|
let _unhandledRejectionWindowStart = Date.now();
|
|
217
200
|
const unhandledRejectionHandler = (reason: unknown): void => {
|
|
@@ -244,17 +227,14 @@ async function main() {
|
|
|
244
227
|
};
|
|
245
228
|
|
|
246
229
|
const exitApp = (): void => {
|
|
247
|
-
|
|
230
|
+
stopSpokenOutputForExit?.();
|
|
248
231
|
unsubs.forEach(fn => fn());
|
|
249
|
-
// Clear bootstrap-owned unsubs + interval via ctx.shutdown()
|
|
250
232
|
const snapshot = conversation.toJSON() as { messages: Array<import('./core/conversation.ts').ConversationMessageSnapshot>; timestamp?: number };
|
|
251
233
|
ctx.shutdown({ ...snapshot, ...buildPersistedSessionContext(snapshot.messages, conversation.getTitleSource(), buildSessionContinuityHints()) }).catch((err) => {
|
|
252
234
|
logger.debug('ctx.shutdown error during exitApp (non-fatal)', { error: summarizeError(err) });
|
|
253
235
|
});
|
|
254
|
-
// Clear recovery interval
|
|
255
236
|
if (recoveryInterval !== null) { clearInterval(recoveryInterval); recoveryInterval = null; }
|
|
256
237
|
deleteRecoveryFile({ homeDirectory });
|
|
257
|
-
// Terminal teardown — main.ts exclusively owns these
|
|
258
238
|
stdin.removeAllListeners('data');
|
|
259
239
|
stdout.removeListener('resize', resizeHandler);
|
|
260
240
|
process.removeListener('SIGINT', sigintHandler);
|
|
@@ -265,10 +245,24 @@ async function main() {
|
|
|
265
245
|
process.exit(0);
|
|
266
246
|
};
|
|
267
247
|
|
|
268
|
-
// main.ts owns terminal teardown, so it binds the shell exit bridge here.
|
|
269
248
|
commandContext.exit = exitApp;
|
|
270
249
|
|
|
271
|
-
const
|
|
250
|
+
const spokenTurns = wireSpokenTurnRuntime({
|
|
251
|
+
voiceService: ctx.services.voiceService,
|
|
252
|
+
configManager,
|
|
253
|
+
events: uiServices.events,
|
|
254
|
+
notify: (message) => { systemMessageRouter.high(message); render(); },
|
|
255
|
+
});
|
|
256
|
+
stopSpokenOutputForExit = () => spokenTurns.stop();
|
|
257
|
+
unsubs.push(...spokenTurns.unsubs);
|
|
258
|
+
unsubs.push(attachSpokenTurnModelRouting({
|
|
259
|
+
orchestrator,
|
|
260
|
+
providerRegistry,
|
|
261
|
+
configManager,
|
|
262
|
+
notify: (message) => { systemMessageRouter.high(message); render(); },
|
|
263
|
+
}));
|
|
264
|
+
|
|
265
|
+
const submitInput = (text: string, content?: ContentPart[], options: { readonly spokenOutput?: boolean } = {}) => {
|
|
272
266
|
input.clearModalStack();
|
|
273
267
|
scrollLocked = true; // Re-lock on any user input
|
|
274
268
|
const AT_MODEL_RE = /@model:([^\s]+)/g;
|
|
@@ -302,7 +296,11 @@ async function main() {
|
|
|
302
296
|
}
|
|
303
297
|
}
|
|
304
298
|
if (processedText || content) {
|
|
305
|
-
|
|
299
|
+
if (options.spokenOutput && processedText) {
|
|
300
|
+
spokenTurns.submitNextTurn(processedText);
|
|
301
|
+
}
|
|
302
|
+
const inputOptions = options.spokenOutput ? createSpokenTurnInputOptions() : undefined;
|
|
303
|
+
orchestrator.handleUserInput(processedText, content, inputOptions).catch((err: unknown) => {
|
|
306
304
|
logger.debug('handleUserInput safety catch (already handled by runTurn)', { error: summarizeError(err) });
|
|
307
305
|
});
|
|
308
306
|
} else {
|
|
@@ -311,6 +309,7 @@ async function main() {
|
|
|
311
309
|
};
|
|
312
310
|
|
|
313
311
|
const cancelGeneration = () => {
|
|
312
|
+
spokenTurns.stop('Spoken output stopped.');
|
|
314
313
|
if (orchestrator.isThinking) {
|
|
315
314
|
orchestrator.abort();
|
|
316
315
|
}
|
|
@@ -338,6 +337,8 @@ async function main() {
|
|
|
338
337
|
};
|
|
339
338
|
|
|
340
339
|
commandContext.submitInput = submitInput;
|
|
340
|
+
commandContext.submitSpokenInput = (text, content) => submitInput(text, content, { spokenOutput: true });
|
|
341
|
+
commandContext.stopSpokenOutput = () => spokenTurns.stop();
|
|
341
342
|
commandContext.executeCommand = (name, args) => commandRegistry.execute(name, args, commandContext);
|
|
342
343
|
commandContext.cancelGeneration = cancelGeneration;
|
|
343
344
|
commandContext.jumpToBookmark = jumpToBookmark;
|
|
@@ -113,26 +113,50 @@ function buildFieldRows(
|
|
|
113
113
|
visibleFields: number,
|
|
114
114
|
capacity: number,
|
|
115
115
|
): readonly RenderedFieldRow[] {
|
|
116
|
-
|
|
116
|
+
wizard.ensureSelectionVisible(visibleFields);
|
|
117
|
+
const fields = wizard.currentStep.fields;
|
|
117
118
|
const rows: RenderedFieldRow[] = [];
|
|
119
|
+
if (fields.length === 0 || capacity <= 0) return rows;
|
|
118
120
|
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
121
|
+
const allRows: RenderedFieldRow[] = [];
|
|
122
|
+
fields.forEach((field, absoluteIndex) => {
|
|
123
|
+
const spacerRows = Math.max(0, field.spacerBeforeRows ?? 0);
|
|
124
|
+
for (let index = 0; index < spacerRows; index += 1) {
|
|
125
|
+
allRows.push({ kind: 'empty' });
|
|
126
|
+
}
|
|
127
|
+
allRows.push({ kind: 'field', field, absoluteIndex });
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
const selectedFieldIndex = wizard.getSelectedFieldIndex();
|
|
131
|
+
const selectedRowIndex = Math.max(0, allRows.findIndex((row) => row.kind === 'field' && row.absoluteIndex === selectedFieldIndex));
|
|
132
|
+
const scrollFieldIndex = wizard.scrollOffsets[wizard.stepIndex] ?? 0;
|
|
133
|
+
const scrollRowIndex = allRows.findIndex((row) => row.kind === 'field' && row.absoluteIndex === scrollFieldIndex);
|
|
134
|
+
const maxStart = Math.max(0, allRows.length - capacity);
|
|
135
|
+
let start = clamp(scrollRowIndex >= 0 ? scrollRowIndex : 0, 0, maxStart);
|
|
136
|
+
|
|
137
|
+
if (selectedRowIndex < start) start = selectedRowIndex;
|
|
138
|
+
if (selectedRowIndex >= start + capacity) start = selectedRowIndex - capacity + 1;
|
|
139
|
+
start = clamp(start, 0, maxStart);
|
|
140
|
+
|
|
141
|
+
if (start > 0 && selectedRowIndex === start) start = Math.max(0, start - 1);
|
|
142
|
+
if (start + capacity < allRows.length && selectedRowIndex === start + capacity - 1) {
|
|
143
|
+
start = Math.min(maxStart, start + 1);
|
|
124
144
|
}
|
|
125
145
|
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
rows
|
|
129
|
-
|
|
146
|
+
rows.push(...allRows.slice(start, start + capacity));
|
|
147
|
+
if (start > 0 && rows.length > 0) {
|
|
148
|
+
rows[0] = {
|
|
149
|
+
kind: 'moreAbove',
|
|
150
|
+
text: `${OVERLAY_GLYPHS.moreAbove} ${start} more above`,
|
|
151
|
+
};
|
|
152
|
+
}
|
|
130
153
|
|
|
131
|
-
|
|
132
|
-
|
|
154
|
+
const hiddenBelow = Math.max(0, allRows.length - (start + capacity));
|
|
155
|
+
if (hiddenBelow > 0 && rows.length > 0) {
|
|
156
|
+
rows[rows.length - 1] = {
|
|
133
157
|
kind: 'moreBelow',
|
|
134
|
-
text: `${OVERLAY_GLYPHS.moreBelow} ${
|
|
135
|
-
}
|
|
158
|
+
text: `${OVERLAY_GLYPHS.moreBelow} ${hiddenBelow} more below`,
|
|
159
|
+
};
|
|
136
160
|
}
|
|
137
161
|
|
|
138
162
|
while (rows.length < capacity) rows.push({ kind: 'empty' });
|
|
@@ -154,7 +154,7 @@ export class UIFactory {
|
|
|
154
154
|
const promptLines = prompt.split('\n');
|
|
155
155
|
const TEXT_COLOR = promptFocused ? '252' : '246';
|
|
156
156
|
const BG_COLOR = promptFocused ? '#2a2a2a' : '#1f2430';
|
|
157
|
-
const BORDER_COLOR =
|
|
157
|
+
const BORDER_COLOR = BG_COLOR;
|
|
158
158
|
const boxMargin = 2; const boxWidth = width - (boxMargin * 2); const boxStartX = boxMargin;
|
|
159
159
|
const createBaseLine = () => {
|
|
160
160
|
const l = createEmptyLine(width);
|
|
@@ -37,6 +37,7 @@ import { createBootstrapCommandShellServices, type PlanRuntimeService, type Remo
|
|
|
37
37
|
import type { OperatorClient } from '@pellux/goodvibes-sdk/platform/runtime/operator-client';
|
|
38
38
|
import type { PeerClient } from '@pellux/goodvibes-sdk/platform/runtime/peer-client';
|
|
39
39
|
import type { DirectTransport } from '@pellux/goodvibes-sdk/platform/runtime/transports/direct';
|
|
40
|
+
import type { VoiceProviderRegistry, VoiceService } from '@pellux/goodvibes-sdk/platform/voice/index';
|
|
40
41
|
import {
|
|
41
42
|
createBootstrapCommandActions,
|
|
42
43
|
createBootstrapCommandClientsSection,
|
|
@@ -58,6 +59,8 @@ export type CreateBootstrapCommandContextOptions = {
|
|
|
58
59
|
requestPermission: PermissionRequestHandler;
|
|
59
60
|
toolRegistry: ToolRegistry;
|
|
60
61
|
mcpRegistry: McpRegistry;
|
|
62
|
+
voiceProviderRegistry?: VoiceProviderRegistry;
|
|
63
|
+
voiceService?: VoiceService;
|
|
61
64
|
forensicsRegistry: ForensicsRegistry;
|
|
62
65
|
policyRuntimeState: PolicyRuntimeState;
|
|
63
66
|
readModels: UiReadModels;
|
|
@@ -122,6 +125,8 @@ export function createBootstrapCommandContext(
|
|
|
122
125
|
requestPermission,
|
|
123
126
|
toolRegistry,
|
|
124
127
|
mcpRegistry,
|
|
128
|
+
voiceProviderRegistry,
|
|
129
|
+
voiceService,
|
|
125
130
|
forensicsRegistry,
|
|
126
131
|
policyRuntimeState,
|
|
127
132
|
readModels,
|
|
@@ -223,7 +228,7 @@ export function createBootstrapCommandContext(
|
|
|
223
228
|
profileManager,
|
|
224
229
|
bookmarkManager,
|
|
225
230
|
}, shellServices);
|
|
226
|
-
const platform = createBootstrapCommandPlatformSection({ configManager }, shellServices);
|
|
231
|
+
const platform = createBootstrapCommandPlatformSection({ configManager, voiceProviderRegistry, voiceService }, shellServices);
|
|
227
232
|
const extensions = createBootstrapCommandExtensionsSection({
|
|
228
233
|
toolRegistry,
|
|
229
234
|
mcpRegistry,
|
|
@@ -40,6 +40,7 @@ import type { BootstrapCommandShellServices } from '@pellux/goodvibes-sdk/platfo
|
|
|
40
40
|
import type { OperatorClient } from '@pellux/goodvibes-sdk/platform/runtime/operator-client';
|
|
41
41
|
import type { PeerClient } from '@pellux/goodvibes-sdk/platform/runtime/peer-client';
|
|
42
42
|
import type { DirectTransport } from '@pellux/goodvibes-sdk/platform/runtime/transports/direct';
|
|
43
|
+
import type { VoiceProviderRegistry, VoiceService } from '@pellux/goodvibes-sdk/platform/voice/index';
|
|
43
44
|
import { summarizeError } from '@pellux/goodvibes-sdk/platform/utils/error-display';
|
|
44
45
|
|
|
45
46
|
export type BootstrapCommandSessionSection = CommandContext['session'];
|
|
@@ -74,6 +75,8 @@ export interface BootstrapCommandSectionOptions {
|
|
|
74
75
|
readonly requestPermission: PermissionRequestHandler;
|
|
75
76
|
readonly toolRegistry: ToolRegistry;
|
|
76
77
|
readonly mcpRegistry: McpRegistry;
|
|
78
|
+
readonly voiceProviderRegistry?: VoiceProviderRegistry;
|
|
79
|
+
readonly voiceService?: VoiceService;
|
|
77
80
|
readonly forensicsRegistry: ForensicsRegistry;
|
|
78
81
|
readonly policyRuntimeState: PolicyRuntimeState;
|
|
79
82
|
readonly readModels: UiReadModels;
|
|
@@ -200,6 +203,10 @@ export function createBootstrapCommandActions(
|
|
|
200
203
|
configManager.set('tools.llmModel', key);
|
|
201
204
|
configManager.setDynamic('tools.llmEnabled' as never, true);
|
|
202
205
|
conversation.log(`Tool LLM set to: ${def.displayName} (${def.provider})`, { fg: '135' });
|
|
206
|
+
} else if (resolvedTarget === 'tts') {
|
|
207
|
+
configManager.set('tts.llmProvider', def.provider);
|
|
208
|
+
configManager.set('tts.llmModel', key);
|
|
209
|
+
conversation.log(`TTS LLM set to: ${def.displayName} (${def.provider})`, { fg: '135' });
|
|
203
210
|
} else {
|
|
204
211
|
// Default: main provider/model
|
|
205
212
|
if (contextCap != null && contextCap > 0) {
|
|
@@ -321,13 +328,15 @@ export function createBootstrapCommandWorkspaceSection(
|
|
|
321
328
|
export function createBootstrapCommandPlatformSection(
|
|
322
329
|
options: Pick<
|
|
323
330
|
BootstrapCommandSectionOptions,
|
|
324
|
-
'configManager'
|
|
331
|
+
'configManager' | 'voiceProviderRegistry' | 'voiceService'
|
|
325
332
|
>,
|
|
326
333
|
shellServices: BootstrapCommandShellServices,
|
|
327
334
|
): BootstrapCommandPlatformSection {
|
|
328
335
|
return {
|
|
329
336
|
config: getConfigSnapshot(options.configManager),
|
|
330
337
|
configManager: options.configManager,
|
|
338
|
+
voiceProviderRegistry: options.voiceProviderRegistry,
|
|
339
|
+
voiceService: options.voiceService,
|
|
331
340
|
...shellServices.platform,
|
|
332
341
|
};
|
|
333
342
|
}
|
|
@@ -183,6 +183,8 @@ export function createBootstrapShell(options: BootstrapShellOptions): BootstrapS
|
|
|
183
183
|
requestPermission: (request) => permissionPromptRef.requestPermission(request),
|
|
184
184
|
toolRegistry,
|
|
185
185
|
mcpRegistry: services.mcpRegistry,
|
|
186
|
+
voiceProviderRegistry: services.voiceProviders,
|
|
187
|
+
voiceService: services.voiceService,
|
|
186
188
|
forensicsRegistry,
|
|
187
189
|
policyRuntimeState,
|
|
188
190
|
readModels: uiServices.readModels,
|
package/src/shell/ui-openers.ts
CHANGED
|
@@ -111,6 +111,14 @@ export function wireShellUiOpeners(options: WireShellUiOpenersOptions): void {
|
|
|
111
111
|
return new Set(results.filter((v): v is string => v !== null));
|
|
112
112
|
}
|
|
113
113
|
|
|
114
|
+
const getCurrentModelForPickerTarget = (): string => {
|
|
115
|
+
const target = input.modelPicker.target;
|
|
116
|
+
if (target === 'helper') return String(configManager.get('helper.globalModel') || runtime.model);
|
|
117
|
+
if (target === 'tool') return String(configManager.get('tools.llmModel') || runtime.model);
|
|
118
|
+
if (target === 'tts') return String(configManager.get('tts.llmModel') || runtime.model);
|
|
119
|
+
return runtime.model;
|
|
120
|
+
};
|
|
121
|
+
|
|
114
122
|
commandContext.openModelPicker = () => {
|
|
115
123
|
void (async () => {
|
|
116
124
|
const models = providerRegistry.getSelectableModels();
|
|
@@ -124,7 +132,7 @@ export function wireShellUiOpeners(options: WireShellUiOpenersOptions): void {
|
|
|
124
132
|
});
|
|
125
133
|
void input.modelPicker.loadRecentModels().catch(() => {}); // best-effort: prefetch for UI, failure is non-visible
|
|
126
134
|
input.modalOpened('modelPicker');
|
|
127
|
-
input.modelPicker.openAllModels(models,
|
|
135
|
+
input.modelPicker.openAllModels(models, getCurrentModelForPickerTarget());
|
|
128
136
|
render();
|
|
129
137
|
})().catch((error: unknown) => {
|
|
130
138
|
commandContext.print?.(`Model picker failed to open: ${error instanceof Error ? error.message : String(error)}`);
|
|
@@ -132,6 +140,8 @@ export function wireShellUiOpeners(options: WireShellUiOpenersOptions): void {
|
|
|
132
140
|
});
|
|
133
141
|
};
|
|
134
142
|
|
|
143
|
+
commandContext.openModelPickerWithTarget = (target) => input.openModelPickerWithTarget(target);
|
|
144
|
+
|
|
135
145
|
commandContext.openProviderPicker = () => {
|
|
136
146
|
void (async () => {
|
|
137
147
|
const providers = [...new Set(providerRegistry.listModels().map((model) => model.provider))];
|
package/src/version.ts
CHANGED
|
@@ -6,7 +6,7 @@ import { join } from 'node:path';
|
|
|
6
6
|
// The prebuild script updates the fallback value before compilation.
|
|
7
7
|
// Uses import.meta.dir (Bun) to locate package.json relative to this file,
|
|
8
8
|
// which is correct regardless of the process working directory.
|
|
9
|
-
let _version = '0.19.
|
|
9
|
+
let _version = '0.19.33';
|
|
10
10
|
try {
|
|
11
11
|
const pkg = JSON.parse(readFileSync(join(import.meta.dir, '..', 'package.json'), 'utf-8'));
|
|
12
12
|
_version = pkg.version ?? _version;
|