Run version check in parallel with TUI startup

Instead of blocking startup for up to 1 second waiting for the version check,
run it in the background and insert the notification into chat when it completes.
This commit is contained in:
Mario Zechner 2025-12-05 23:36:06 +01:00
parent c423734e36
commit 7352072bc2
2 changed files with 29 additions and 32 deletions

View file

@ -717,7 +717,7 @@ async function runInteractiveMode(
version: string, version: string,
changelogMarkdown: string | null = null, changelogMarkdown: string | null = null,
modelFallbackMessage: string | null = null, modelFallbackMessage: string | null = null,
newVersion: string | null = null, versionCheckPromise: Promise<string | null>,
scopedModels: Array<{ model: Model<Api>; thinkingLevel: ThinkingLevel }> = [], scopedModels: Array<{ model: Model<Api>; thinkingLevel: ThinkingLevel }> = [],
initialMessages: string[] = [], initialMessages: string[] = [],
initialMessage?: string, initialMessage?: string,
@ -730,7 +730,6 @@ async function runInteractiveMode(
settingsManager, settingsManager,
version, version,
changelogMarkdown, changelogMarkdown,
newVersion,
scopedModels, scopedModels,
fdPath, fdPath,
); );
@ -738,6 +737,13 @@ async function runInteractiveMode(
// Initialize TUI (subscribes to agent events internally) // Initialize TUI (subscribes to agent events internally)
await renderer.init(); await renderer.init();
// Handle version check result when it completes (don't block)
versionCheckPromise.then((newVersion) => {
if (newVersion) {
renderer.showNewVersionNotification(newVersion);
}
});
// Render any existing messages (from --continue mode) // Render any existing messages (from --continue mode)
renderer.renderInitialMessages(agent.state); renderer.renderInitialMessages(agent.state);
@ -1334,16 +1340,8 @@ export async function main(args: string[]) {
// RPC mode - headless operation // RPC mode - headless operation
await runRpcMode(agent, sessionManager, settingsManager); await runRpcMode(agent, sessionManager, settingsManager);
} else if (isInteractive) { } else if (isInteractive) {
// Check for new version (don't block startup if it takes too long) // Check for new version in the background (don't block startup)
let newVersion: string | null = null; const versionCheckPromise = checkForNewVersion(VERSION).catch(() => null);
try {
newVersion = await Promise.race([
checkForNewVersion(VERSION),
new Promise<null>((resolve) => setTimeout(() => resolve(null), 1000)), // 1 second timeout
]);
} catch (e) {
// Ignore errors
}
// Check if we should show changelog (only in interactive mode, only for new sessions) // Check if we should show changelog (only in interactive mode, only for new sessions)
let changelogMarkdown: string | null = null; let changelogMarkdown: string | null = null;
@ -1394,7 +1392,7 @@ export async function main(args: string[]) {
VERSION, VERSION,
changelogMarkdown, changelogMarkdown,
modelFallbackMessage, modelFallbackMessage,
newVersion, versionCheckPromise,
scopedModels, scopedModels,
parsed.messages, parsed.messages,
initialMessage, initialMessage,

View file

@ -70,7 +70,6 @@ export class TuiRenderer {
private lastSigintTime = 0; private lastSigintTime = 0;
private changelogMarkdown: string | null = null; private changelogMarkdown: string | null = null;
private newVersion: string | null = null;
// Message queueing // Message queueing
private queuedMessages: string[] = []; private queuedMessages: string[] = [];
@ -126,7 +125,6 @@ export class TuiRenderer {
settingsManager: SettingsManager, settingsManager: SettingsManager,
version: string, version: string,
changelogMarkdown: string | null = null, changelogMarkdown: string | null = null,
newVersion: string | null = null,
scopedModels: Array<{ model: Model<any>; thinkingLevel: ThinkingLevel }> = [], scopedModels: Array<{ model: Model<any>; thinkingLevel: ThinkingLevel }> = [],
fdPath: string | null = null, fdPath: string | null = null,
) { ) {
@ -134,7 +132,6 @@ export class TuiRenderer {
this.sessionManager = sessionManager; this.sessionManager = sessionManager;
this.settingsManager = settingsManager; this.settingsManager = settingsManager;
this.version = version; this.version = version;
this.newVersion = newVersion;
this.changelogMarkdown = changelogMarkdown; this.changelogMarkdown = changelogMarkdown;
this.scopedModels = scopedModels; this.scopedModels = scopedModels;
this.ui = new TUI(new ProcessTerminal()); this.ui = new TUI(new ProcessTerminal());
@ -303,22 +300,6 @@ export class TuiRenderer {
this.ui.addChild(header); this.ui.addChild(header);
this.ui.addChild(new Spacer(1)); this.ui.addChild(new Spacer(1));
// Add new version notification if available
if (this.newVersion) {
this.ui.addChild(new DynamicBorder((text) => theme.fg("warning", text)));
this.ui.addChild(
new Text(
theme.bold(theme.fg("warning", "Update Available")) +
"\n" +
theme.fg("muted", `New version ${this.newVersion} is available. Run: `) +
theme.fg("accent", "npm install -g @mariozechner/pi-coding-agent"),
1,
0,
),
);
this.ui.addChild(new DynamicBorder((text) => theme.fg("warning", text)));
}
// Add changelog if provided // Add changelog if provided
if (this.changelogMarkdown) { if (this.changelogMarkdown) {
this.ui.addChild(new DynamicBorder()); this.ui.addChild(new DynamicBorder());
@ -1214,6 +1195,24 @@ export class TuiRenderer {
this.ui.requestRender(); this.ui.requestRender();
} }
showNewVersionNotification(newVersion: string): void {
// Show new version notification in the chat
this.chatContainer.addChild(new Spacer(1));
this.chatContainer.addChild(new DynamicBorder((text) => theme.fg("warning", text)));
this.chatContainer.addChild(
new Text(
theme.bold(theme.fg("warning", "Update Available")) +
"\n" +
theme.fg("muted", `New version ${newVersion} is available. Run: `) +
theme.fg("accent", "npm install -g @mariozechner/pi-coding-agent"),
1,
0,
),
);
this.chatContainer.addChild(new DynamicBorder((text) => theme.fg("warning", text)));
this.ui.requestRender();
}
private showThinkingSelector(): void { private showThinkingSelector(): void {
// Create thinking selector with current level // Create thinking selector with current level
this.thinkingSelector = new ThinkingSelectorComponent( this.thinkingSelector = new ThinkingSelectorComponent(