@@ -171,6 +171,12 @@ import {
171171 type NativeControlState ,
172172 type PreparedDraftLaunch ,
173173} from "../../lib/draftLaunchJobs" ;
174+ import {
175+ buildHandoffLaunchJobsScopeKey ,
176+ createHandoffLaunchJobId ,
177+ type HandoffLaunchJob ,
178+ type HandoffLaunchJobStatus ,
179+ } from "../../lib/handoffLaunchJobs" ;
174180import {
175181 createAppControlContextInstanceId ,
176182 createBuiltInBrowserContextInstanceId ,
@@ -2924,6 +2930,15 @@ export function AgentChatPane({
29242930 ) => {
29252931 rootAppStoreApi . getState ( ) . setDraftLaunchJobs ( draftLaunchJobsScopeKey , next ) ;
29262932 } , [ draftLaunchJobsScopeKey ] ) ;
2933+ const handoffLaunchJobsScopeKey = useMemo (
2934+ ( ) => buildHandoffLaunchJobsScopeKey ( { projectBinding, projectRoot } ) ,
2935+ [ projectBinding , projectRoot ] ,
2936+ ) ;
2937+ const setHandoffLaunchJobs = useCallback ( (
2938+ next : HandoffLaunchJob [ ] | ( ( prev : HandoffLaunchJob [ ] ) => HandoffLaunchJob [ ] ) ,
2939+ ) => {
2940+ rootAppStoreApi . getState ( ) . setHandoffLaunchJobs ( handoffLaunchJobsScopeKey , next ) ;
2941+ } , [ handoffLaunchJobsScopeKey ] ) ;
29272942 const hasActiveDraftLaunchJobs = useMemo (
29282943 ( ) => draftLaunchJobs . some ( ( job ) => ! isDraftLaunchJobTerminal ( job . status ) ) ,
29292944 [ draftLaunchJobs ] ,
@@ -3033,6 +3048,7 @@ export function AgentChatPane({
30333048 const [ loading , setLoading ] = useState ( false ) ;
30343049 const [ preferencesReady , setPreferencesReady ] = useState ( false ) ;
30353050 const [ error , setError ] = useState < string | null > ( null ) ;
3051+ const handoffErrorClearTimerRef = useRef < number | null > ( null ) ;
30363052 const [ deletingChatSessionId , setDeletingChatSessionId ] = useState < string | null > ( null ) ;
30373053 const [ computerUseSnapshot , setComputerUseSnapshot ] = useState < ComputerUseOwnerSnapshot | null > ( null ) ;
30383054 const [ chatActionsOpen , setChatActionsOpen ] = useState (
@@ -3064,6 +3080,12 @@ export function AgentChatPane({
30643080 } ) ;
30653081 return ( ) => { cancelled = true ; unsubscribe ( ) ; } ;
30663082 } , [ laneId ] ) ;
3083+ useEffect ( ( ) => ( ) => {
3084+ if ( handoffErrorClearTimerRef . current != null ) {
3085+ window . clearTimeout ( handoffErrorClearTimerRef . current ) ;
3086+ handoffErrorClearTimerRef . current = null ;
3087+ }
3088+ } , [ ] ) ;
30673089 const [ chatActionsTab , setChatActionsTab ] = useState < ChatActionsTab > (
30683090 ( ) => readChatCompanionUiState ( initialCompanionStateKey ) . chatActionsTab ,
30693091 ) ;
@@ -7465,8 +7487,36 @@ export function AgentChatPane({
74657487
74667488 const handoffSession = useCallback ( async ( mode : "brief" | "fork" = "brief" ) => {
74677489 if ( ! canShowHandoff || ! selectedSessionId || ! handoffModelId || handoffBlocked ) return ;
7490+ const sourceLaneId = selectedSession ?. laneId ?? laneId ;
7491+ if ( ! sourceLaneId ) return ;
7492+ const jobId = createHandoffLaunchJobId ( ) ;
7493+ const stageTimerIds : number [ ] = [ ] ;
7494+ const patchHandoffJob = ( status : HandoffLaunchJobStatus ) => {
7495+ setHandoffLaunchJobs ( ( current ) => current . map ( ( job ) => (
7496+ job . id === jobId ? { ...job , status } : job
7497+ ) ) ) ;
7498+ } ;
7499+ const targetModelLabel = handoffTargetDescriptor ?. displayName
7500+ ?? formatLocalModelLabel ( handoffModelId ) ;
7501+ setHandoffLaunchJobs ( ( current ) => [
7502+ {
7503+ id : jobId ,
7504+ sourceSessionId : selectedSessionId ,
7505+ laneId : sourceLaneId ,
7506+ laneName : laneDisplayLabel ?? sourceLaneId ,
7507+ targetModelId : handoffModelId ,
7508+ targetModelLabel,
7509+ targetToolType : handoffTargetProvider ? chatToolTypeForProvider ( handoffTargetProvider ) : "other" ,
7510+ status : "preparing-summary" ,
7511+ createdAtMs : Date . now ( ) ,
7512+ } ,
7513+ ...current . filter ( ( job ) => job . sourceSessionId !== selectedSessionId ) ,
7514+ ] ) ;
74687515 setError ( null ) ;
74697516 setHandoffBusy ( true ) ;
7517+ setChatActionsOpen ( false ) ;
7518+ stageTimerIds . push ( window . setTimeout ( ( ) => patchHandoffJob ( "creating-chat" ) , 700 ) ) ;
7519+ stageTimerIds . push ( window . setTimeout ( ( ) => patchHandoffJob ( "sending-handoff" ) , 1500 ) ) ;
74707520 try {
74717521 const resolvedHandoffPermissionMode = handoffNativePermissionMode ?? selectedSession ?. permissionMode ;
74727522 const result = await window . ade . agentChat . handoff ( {
@@ -7487,13 +7537,22 @@ export function AgentChatPane({
74877537 cursorModeId : handoffCursorModeId ,
74887538 cursorConfigValues : handoffCursorConfigValues ,
74897539 } ) ;
7490- setChatActionsOpen ( false ) ;
7491- notifySessionCreated ( result . session ) ;
7540+ notifySessionCreated ( result . session , { source : "handoff" } ) ;
74927541 invalidateCurrentChatSessionList ( ) ;
74937542 void refreshSessions ( { force : true } ) . catch ( ( ) => { } ) ;
74947543 } catch ( handoffError ) {
7495- setError ( handoffError instanceof Error ? handoffError . message : String ( handoffError ) ) ;
7544+ const message = handoffError instanceof Error ? handoffError . message : String ( handoffError ) ;
7545+ setError ( message ) ;
7546+ if ( handoffErrorClearTimerRef . current != null ) {
7547+ window . clearTimeout ( handoffErrorClearTimerRef . current ) ;
7548+ }
7549+ handoffErrorClearTimerRef . current = window . setTimeout ( ( ) => {
7550+ handoffErrorClearTimerRef . current = null ;
7551+ setError ( ( current ) => ( current === message ? null : current ) ) ;
7552+ } , 6000 ) ;
74967553 } finally {
7554+ stageTimerIds . forEach ( ( timerId ) => window . clearTimeout ( timerId ) ) ;
7555+ setHandoffLaunchJobs ( ( current ) => current . filter ( ( job ) => job . id !== jobId ) ) ;
74977556 setHandoffBusy ( false ) ;
74987557 }
74997558 } , [
@@ -7502,6 +7561,7 @@ export function AgentChatPane({
75027561 handoffClaudePermissionMode ,
75037562 handoffCodexApprovalPolicy ,
75047563 handoffCodexConfigSource ,
7564+ handoffTargetDescriptor ,
75057565 handoffFastMode ,
75067566 handoffCodexSandbox ,
75077567 handoffCursorConfigValues ,
@@ -7513,10 +7573,14 @@ export function AgentChatPane({
75137573 handoffReasoningEffort ,
75147574 handoffTargetProvider ,
75157575 invalidateCurrentChatSessionList ,
7576+ laneDisplayLabel ,
7577+ laneId ,
75167578 notifySessionCreated ,
75177579 refreshSessions ,
75187580 selectedSession ?. permissionMode ,
7581+ selectedSession ?. laneId ,
75197582 selectedSessionId ,
7583+ setHandoffLaunchJobs ,
75207584 ] ) ;
75217585
75227586 const handleDeleteSelectedChat = useCallback ( ( ) => {
0 commit comments