@@ -27,6 +27,7 @@ export class ChatPanelProvider
2727 private view ?: vscode . WebviewView ;
2828 private disposables : vscode . Disposable [ ] = [ ] ;
2929 private chatId : string | undefined ;
30+ private authRetryTimer : ReturnType < typeof setTimeout > | undefined ;
3031
3132 constructor (
3233 private readonly client : CoderApi ,
@@ -94,19 +95,50 @@ export class ChatPanelProvider
9495 }
9596 const msg = message as { type ?: string } ;
9697 if ( msg . type === "coder:vscode-ready" ) {
97- const token = this . client . getSessionToken ( ) ;
98- if ( ! token ) {
99- this . logger . warn (
100- "Chat iframe requested auth but no session token available" ,
98+ this . sendAuthToken ( ) ;
99+ }
100+ }
101+
102+ /**
103+ * Attempt to forward the session token to the chat iframe.
104+ * The token may not be available immediately after a reload
105+ * (e.g. deployment setup is still in progress), so we retry
106+ * with exponential backoff before giving up.
107+ */
108+ private static readonly MAX_AUTH_RETRIES = 5 ;
109+ private static readonly AUTH_RETRY_BASE_MS = 500 ;
110+
111+ private sendAuthToken ( attempt = 0 ) : void {
112+ clearTimeout ( this . authRetryTimer ) ;
113+ const token = this . client . getSessionToken ( ) ;
114+ if ( ! token ) {
115+ if ( attempt < ChatPanelProvider . MAX_AUTH_RETRIES ) {
116+ const delay = ChatPanelProvider . AUTH_RETRY_BASE_MS * 2 ** attempt ;
117+ this . logger . info (
118+ `Chat: no session token yet, retrying in ${ delay } ms ` +
119+ `(attempt ${ attempt + 1 } /${ ChatPanelProvider . MAX_AUTH_RETRIES } )` ,
120+ ) ;
121+ this . authRetryTimer = setTimeout (
122+ ( ) => this . sendAuthToken ( attempt + 1 ) ,
123+ delay ,
101124 ) ;
102125 return ;
103126 }
104- this . logger . info ( "Chat: forwarding token to iframe" ) ;
127+ this . logger . warn (
128+ "Chat iframe requested auth but no session token available " +
129+ "after all retries" ,
130+ ) ;
105131 this . view ?. webview . postMessage ( {
106- type : "coder:auth-bootstrap-token " ,
107- token,
132+ type : "coder:auth-error " ,
133+ error : "No session token available. Please sign in and retry." ,
108134 } ) ;
135+ return ;
109136 }
137+ this . logger . info ( "Chat: forwarding token to iframe" ) ;
138+ this . view ?. webview . postMessage ( {
139+ type : "coder:auth-bootstrap-token" ,
140+ token,
141+ } ) ;
110142 }
111143
112144 private getIframeHtml ( embedUrl : string , allowedOrigin : string ) : string {
@@ -136,6 +168,17 @@ export class ChatPanelProvider
136168 font-family: var(--vscode-font-family, sans-serif);
137169 font-size: 13px; padding: 16px; text-align: center;
138170 }
171+ #retry-btn {
172+ margin-top: 12px; padding: 6px 16px;
173+ background: var(--vscode-button-background, #0e639c);
174+ color: var(--vscode-button-foreground, #fff);
175+ border: none; border-radius: 2px; cursor: pointer;
176+ font-family: var(--vscode-font-family, sans-serif);
177+ font-size: 13px;
178+ }
179+ #retry-btn:hover {
180+ background: var(--vscode-button-hoverBackground, #1177bb);
181+ }
139182 </style>
140183</head>
141184<body>
@@ -170,7 +213,23 @@ export class ChatPanelProvider
170213 iframe.contentWindow.postMessage({
171214 type: 'coder:vscode-auth-bootstrap',
172215 payload: { token: data.token },
173- }, '${ allowedOrigin } ');
216+ }, '${ allowedOrigin } ');
217+ }
218+
219+ if (data.type === 'coder:auth-error') {
220+ status.textContent = '';
221+ status.appendChild(document.createTextNode(data.error || 'Authentication failed.'));
222+ const btn = document.createElement('button');
223+ btn.id = 'retry-btn';
224+ btn.textContent = 'Retry';
225+ btn.addEventListener('click', () => {
226+ status.textContent = 'Authenticating…';
227+ vscode.postMessage({ type: 'coder:vscode-ready' });
228+ });
229+ status.appendChild(document.createElement('br'));
230+ status.appendChild(btn);
231+ status.style.display = 'block';
232+ iframe.style.display = 'none';
174233 }
175234 });
176235 })();
@@ -190,6 +249,7 @@ text-align:center;}</style></head>
190249 }
191250
192251 dispose ( ) : void {
252+ clearTimeout ( this . authRetryTimer ) ;
193253 for ( const d of this . disposables ) {
194254 d . dispose ( ) ;
195255 }
0 commit comments