@@ -14,7 +14,7 @@ import type { InitStateManager } from "@/node/services/initStateManager";
1414import type { ExtensionMetadataService } from "@/node/services/ExtensionMetadataService" ;
1515import { listLocalBranches , detectDefaultTrunkBranch } from "@/node/git" ;
1616import { createRuntime , IncompatibleRuntimeError } from "@/node/runtime/runtimeFactory" ;
17- import { generateWorkspaceName } from "./workspaceTitleGenerator" ;
17+ import { generateWorkspaceName , generatePlaceholderName } from "./workspaceTitleGenerator" ;
1818import { validateWorkspaceName } from "@/common/utils/validation/workspaceValidation" ;
1919
2020import type {
@@ -415,23 +415,9 @@ export class WorkspaceService extends EventEmitter {
415415 | { success : false ; error : string }
416416 > {
417417 try {
418- const branchNameResult = await generateWorkspaceName ( message , options . model , this . aiService ) ;
419- if ( ! branchNameResult . success ) {
420- const err = branchNameResult . error ;
421- const errorMessage =
422- "message" in err
423- ? err . message
424- : err . type === "api_key_not_found"
425- ? `API key not found for ${ err . provider } `
426- : err . type === "provider_not_supported"
427- ? `Provider not supported: ${ err . provider } `
428- : "raw" in err
429- ? err . raw
430- : "Unknown error" ;
431- return { success : false , error : errorMessage } ;
432- }
433- const branchName = branchNameResult . data ;
434- log . debug ( "Generated workspace name" , { branchName } ) ;
418+ // Use placeholder name for immediate workspace creation (non-blocking)
419+ const placeholderName = generatePlaceholderName ( message ) ;
420+ log . debug ( "Using placeholder name for immediate creation" , { placeholderName } ) ;
435421
436422 const branches = await listLocalBranches ( projectPath ) ;
437423 const recommendedTrunk =
@@ -470,7 +456,7 @@ export class WorkspaceService extends EventEmitter {
470456 const initLogger = this . createInitLogger ( workspaceId ) ;
471457
472458 // Create workspace with automatic collision retry
473- let finalBranchName = branchName ;
459+ let finalBranchName = placeholderName ;
474460 let createResult : { success : boolean ; workspacePath ?: string ; error ?: string } ;
475461
476462 for ( let attempt = 0 ; attempt <= MAX_WORKSPACE_NAME_COLLISION_RETRIES ; attempt ++ ) {
@@ -490,7 +476,7 @@ export class WorkspaceService extends EventEmitter {
490476 attempt < MAX_WORKSPACE_NAME_COLLISION_RETRIES
491477 ) {
492478 log . debug ( `Workspace name collision for "${ finalBranchName } ", retrying with suffix` ) ;
493- finalBranchName = appendCollisionSuffix ( branchName ) ;
479+ finalBranchName = appendCollisionSuffix ( placeholderName ) ;
494480 continue ;
495481 }
496482 break ;
@@ -558,6 +544,9 @@ export class WorkspaceService extends EventEmitter {
558544
559545 void session . sendMessage ( message , options ) ;
560546
547+ // Generate AI name asynchronously and rename if successful
548+ void this . generateAndApplyAIName ( workspaceId , message , finalBranchName , options . model ) ;
549+
561550 return {
562551 success : true ,
563552 workspaceId,
@@ -570,6 +559,78 @@ export class WorkspaceService extends EventEmitter {
570559 }
571560 }
572561
562+ /**
563+ * Asynchronously generates an AI workspace name and renames the workspace if successful.
564+ * This runs in the background after workspace creation to avoid blocking the UX.
565+ */
566+ private async generateAndApplyAIName (
567+ workspaceId : string ,
568+ message : string ,
569+ currentName : string ,
570+ model : string
571+ ) : Promise < void > {
572+ try {
573+ log . debug ( "Starting async AI name generation" , { workspaceId, currentName } ) ;
574+
575+ const branchNameResult = await generateWorkspaceName ( message , model , this . aiService ) ;
576+
577+ if ( ! branchNameResult . success ) {
578+ // AI name generation failed - keep the placeholder name
579+ const err = branchNameResult . error ;
580+ const errorMessage =
581+ "message" in err
582+ ? err . message
583+ : err . type === "api_key_not_found"
584+ ? `API key not found for ${ err . provider } `
585+ : err . type === "provider_not_supported"
586+ ? `Provider not supported: ${ err . provider } `
587+ : "raw" in err
588+ ? err . raw
589+ : "Unknown error" ;
590+ log . info ( "AI name generation failed, keeping placeholder name" , {
591+ workspaceId,
592+ currentName,
593+ error : errorMessage ,
594+ } ) ;
595+ return ;
596+ }
597+
598+ const aiGeneratedName = branchNameResult . data ;
599+ log . debug ( "AI generated workspace name" , { workspaceId, aiGeneratedName, currentName } ) ;
600+
601+ // Only rename if the AI name is different from current name
602+ if ( aiGeneratedName === currentName ) {
603+ log . debug ( "AI name matches placeholder, no rename needed" , { workspaceId } ) ;
604+ return ;
605+ }
606+
607+ // Attempt to rename the workspace
608+ const renameResult = await this . rename ( workspaceId , aiGeneratedName ) ;
609+
610+ if ( ! renameResult . success ) {
611+ // Rename failed (e.g., collision) - keep the placeholder name
612+ log . info ( "Failed to rename workspace to AI-generated name" , {
613+ workspaceId,
614+ aiGeneratedName,
615+ error : renameResult . error ,
616+ } ) ;
617+ return ;
618+ }
619+
620+ log . info ( "Successfully renamed workspace to AI-generated name" , {
621+ workspaceId,
622+ oldName : currentName ,
623+ newName : aiGeneratedName ,
624+ } ) ;
625+ } catch ( error ) {
626+ const errorMessage = error instanceof Error ? error . message : String ( error ) ;
627+ log . error ( "Unexpected error in async AI name generation" , {
628+ workspaceId,
629+ error : errorMessage ,
630+ } ) ;
631+ }
632+ }
633+
573634 async remove ( workspaceId : string , force = false ) : Promise < Result < void > > {
574635 // Try to remove from runtime (filesystem)
575636 try {
0 commit comments