@theia/ai-ide 1.74.0-next.8 → 1.74.0
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/lib/browser/ai-allow-all-mode-chat-banner.d.ts +68 -0
- package/lib/browser/ai-allow-all-mode-chat-banner.d.ts.map +1 -0
- package/lib/browser/ai-allow-all-mode-chat-banner.js +184 -0
- package/lib/browser/ai-allow-all-mode-chat-banner.js.map +1 -0
- package/lib/browser/ai-allow-all-mode-chat-banner.spec.d.ts +2 -0
- package/lib/browser/ai-allow-all-mode-chat-banner.spec.d.ts.map +1 -0
- package/lib/browser/ai-allow-all-mode-chat-banner.spec.js +170 -0
- package/lib/browser/ai-allow-all-mode-chat-banner.spec.js.map +1 -0
- package/lib/browser/ai-configuration/tools-configuration-widget.d.ts +3 -4
- package/lib/browser/ai-configuration/tools-configuration-widget.d.ts.map +1 -1
- package/lib/browser/ai-configuration/tools-configuration-widget.js +17 -10
- package/lib/browser/ai-configuration/tools-configuration-widget.js.map +1 -1
- package/lib/browser/ai-first-perspective-contribution.d.ts +6 -0
- package/lib/browser/ai-first-perspective-contribution.d.ts.map +1 -0
- package/lib/browser/ai-first-perspective-contribution.js +50 -0
- package/lib/browser/ai-first-perspective-contribution.js.map +1 -0
- package/lib/browser/ai-sessions-view-contribution.d.ts +13 -0
- package/lib/browser/ai-sessions-view-contribution.d.ts.map +1 -0
- package/lib/browser/ai-sessions-view-contribution.js +69 -0
- package/lib/browser/ai-sessions-view-contribution.js.map +1 -0
- package/lib/browser/ai-sessions-widget.d.ts +27 -0
- package/lib/browser/ai-sessions-widget.d.ts.map +1 -0
- package/lib/browser/ai-sessions-widget.js +131 -0
- package/lib/browser/ai-sessions-widget.js.map +1 -0
- package/lib/browser/chat-session-item-action-contribution.js +2 -2
- package/lib/browser/chat-session-item-action-contribution.js.map +1 -1
- package/lib/browser/chat-session-item.d.ts +15 -0
- package/lib/browser/chat-session-item.d.ts.map +1 -1
- package/lib/browser/chat-session-item.js +39 -18
- package/lib/browser/chat-session-item.js.map +1 -1
- package/lib/browser/chat-session-list-components.d.ts +38 -0
- package/lib/browser/chat-session-list-components.d.ts.map +1 -0
- package/lib/browser/chat-session-list-components.js +87 -0
- package/lib/browser/chat-session-list-components.js.map +1 -0
- package/lib/browser/chat-session-list-service.d.ts +34 -0
- package/lib/browser/chat-session-list-service.d.ts.map +1 -0
- package/lib/browser/chat-session-list-service.js +251 -0
- package/lib/browser/chat-session-list-service.js.map +1 -0
- package/lib/browser/chat-session-list-service.spec.d.ts +2 -0
- package/lib/browser/chat-session-list-service.spec.d.ts.map +1 -0
- package/lib/browser/{chat-sessions-welcome-message-provider.spec.js → chat-session-list-service.spec.js} +133 -39
- package/lib/browser/chat-session-list-service.spec.js.map +1 -0
- package/lib/browser/chat-session-tooltip.d.ts +2 -2
- package/lib/browser/chat-session-tooltip.d.ts.map +1 -1
- package/lib/browser/chat-session-tooltip.js +33 -10
- package/lib/browser/chat-session-tooltip.js.map +1 -1
- package/lib/browser/chat-sessions-welcome-message-provider.d.ts +9 -35
- package/lib/browser/chat-sessions-welcome-message-provider.d.ts.map +1 -1
- package/lib/browser/chat-sessions-welcome-message-provider.js +51 -205
- package/lib/browser/chat-sessions-welcome-message-provider.js.map +1 -1
- package/lib/browser/context-file-validation-service-impl.spec.js +2 -2
- package/lib/browser/context-file-validation-service-impl.spec.js.map +1 -1
- package/lib/browser/frontend-module.d.ts.map +1 -1
- package/lib/browser/frontend-module.js +20 -0
- package/lib/browser/frontend-module.js.map +1 -1
- package/lib/browser/workspace-functions.d.ts +5 -6
- package/lib/browser/workspace-functions.d.ts.map +1 -1
- package/lib/browser/workspace-functions.js +24 -9
- package/lib/browser/workspace-functions.js.map +1 -1
- package/lib/browser/workspace-functions.spec.js +29 -29
- package/lib/browser/workspace-functions.spec.js.map +1 -1
- package/lib/browser/workspace-launch-provider.spec.js +3 -3
- package/lib/browser/workspace-launch-provider.spec.js.map +1 -1
- package/lib/browser/workspace-search-provider.d.ts.map +1 -1
- package/lib/browser/workspace-search-provider.js +28 -20
- package/lib/browser/workspace-search-provider.js.map +1 -1
- package/lib/browser/workspace-search-provider.spec.js +68 -0
- package/lib/browser/workspace-search-provider.spec.js.map +1 -1
- package/lib/browser/workspace-task-provider.spec.js +4 -4
- package/lib/browser/workspace-task-provider.spec.js.map +1 -1
- package/package.json +25 -25
- package/src/browser/ai-allow-all-mode-chat-banner.spec.ts +205 -0
- package/src/browser/ai-allow-all-mode-chat-banner.tsx +217 -0
- package/src/browser/ai-configuration/tools-configuration-widget.tsx +20 -13
- package/src/browser/ai-first-perspective-contribution.ts +48 -0
- package/src/browser/ai-sessions-view-contribution.ts +67 -0
- package/src/browser/ai-sessions-widget.tsx +151 -0
- package/src/browser/chat-session-item-action-contribution.ts +2 -2
- package/src/browser/chat-session-item.tsx +82 -27
- package/src/browser/chat-session-list-components.tsx +136 -0
- package/src/browser/{chat-sessions-welcome-message-provider.spec.ts → chat-session-list-service.spec.ts} +136 -30
- package/src/browser/chat-session-list-service.ts +261 -0
- package/src/browser/chat-session-tooltip.ts +31 -11
- package/src/browser/chat-sessions-welcome-message-provider.tsx +78 -268
- package/src/browser/context-file-validation-service-impl.spec.ts +3 -3
- package/src/browser/frontend-module.ts +23 -0
- package/src/browser/style/index.css +150 -3
- package/src/browser/workspace-functions.spec.ts +36 -37
- package/src/browser/workspace-functions.ts +8 -9
- package/src/browser/workspace-launch-provider.spec.ts +5 -5
- package/src/browser/workspace-search-provider.spec.ts +101 -0
- package/src/browser/workspace-search-provider.ts +34 -23
- package/src/browser/workspace-task-provider.spec.ts +6 -7
- package/lib/browser/chat-sessions-welcome-message-provider.spec.d.ts +0 -2
- package/lib/browser/chat-sessions-welcome-message-provider.spec.d.ts.map +0 -1
- package/lib/browser/chat-sessions-welcome-message-provider.spec.js.map +0 -1
|
@@ -0,0 +1,261 @@
|
|
|
1
|
+
// *****************************************************************************
|
|
2
|
+
// Copyright (C) 2026 EclipseSource GmbH.
|
|
3
|
+
//
|
|
4
|
+
// This program and the accompanying materials are made available under the
|
|
5
|
+
// terms of the Eclipse Public License v. 2.0 which is available at
|
|
6
|
+
// http://www.eclipse.org/legal/epl-2.0.
|
|
7
|
+
//
|
|
8
|
+
// This Source Code may also be made available under the following Secondary
|
|
9
|
+
// Licenses when the conditions for such availability set forth in the Eclipse
|
|
10
|
+
// Public License v. 2.0 are satisfied: GNU General Public License, version 2
|
|
11
|
+
// with the GNU Classpath Exception which is available at
|
|
12
|
+
// https://www.gnu.org/software/classpath/license.html.
|
|
13
|
+
//
|
|
14
|
+
// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
|
|
15
|
+
// *****************************************************************************
|
|
16
|
+
|
|
17
|
+
import { ChatService, ChatSession, ChatSessionMetadata, ChatSessionStatus } from '@theia/ai-chat';
|
|
18
|
+
import { PERSISTED_SESSION_LIMIT_PREF, SESSION_STORAGE_PREF } from '@theia/ai-chat/lib/common/ai-chat-preferences';
|
|
19
|
+
import { ChatViewWidget } from '@theia/ai-chat-ui/lib/browser/chat-view-widget';
|
|
20
|
+
import { DisposableCollection, Emitter, Event, PreferenceService } from '@theia/core';
|
|
21
|
+
import { ApplicationShell } from '@theia/core/lib/browser';
|
|
22
|
+
import { inject, injectable, postConstruct } from '@theia/core/shared/inversify';
|
|
23
|
+
import { UnreadStateProvider } from './chat-session-item';
|
|
24
|
+
import { SectionedSessions, SessionRow, parentIdOf } from './chat-session-list-components';
|
|
25
|
+
|
|
26
|
+
@injectable()
|
|
27
|
+
export class ChatSessionListService implements UnreadStateProvider {
|
|
28
|
+
|
|
29
|
+
@inject(ChatService)
|
|
30
|
+
protected readonly chatService: ChatService;
|
|
31
|
+
|
|
32
|
+
@inject(PreferenceService)
|
|
33
|
+
protected readonly preferenceService: PreferenceService;
|
|
34
|
+
|
|
35
|
+
@inject(ApplicationShell)
|
|
36
|
+
protected readonly shell: ApplicationShell;
|
|
37
|
+
|
|
38
|
+
private readonly unreadSessions = new Map<string, {
|
|
39
|
+
unread: boolean;
|
|
40
|
+
requiresAction: boolean;
|
|
41
|
+
seenRequests: number;
|
|
42
|
+
seenCompleted: number;
|
|
43
|
+
listener: DisposableCollection;
|
|
44
|
+
}>();
|
|
45
|
+
private readonly onUnreadChangedEmitter = new Emitter<string>();
|
|
46
|
+
readonly onUnreadChanged: Event<string> = this.onUnreadChangedEmitter.event;
|
|
47
|
+
|
|
48
|
+
protected _persistedSessions: ChatSessionMetadata[] = [];
|
|
49
|
+
|
|
50
|
+
/** Expanded root session IDs (default collapsed). */
|
|
51
|
+
protected expandedRoots = new Set<string>();
|
|
52
|
+
|
|
53
|
+
protected readonly onStateChangedEmitter = new Emitter<void>();
|
|
54
|
+
readonly onStateChanged: Event<void> = this.onStateChangedEmitter.event;
|
|
55
|
+
|
|
56
|
+
@postConstruct()
|
|
57
|
+
protected init(): void {
|
|
58
|
+
for (const session of this.chatService.getSessions()) {
|
|
59
|
+
this.watchSession(session);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
this.chatService.onSessionEvent(event => {
|
|
63
|
+
if (event.type === 'created') {
|
|
64
|
+
const s = this.chatService.getSession(event.sessionId);
|
|
65
|
+
if (s) {
|
|
66
|
+
this.watchSession(s);
|
|
67
|
+
}
|
|
68
|
+
} else if (event.type === 'activeChange' && event.sessionId) {
|
|
69
|
+
this.markSessionRead(event.sessionId);
|
|
70
|
+
} else if (event.type === 'deleted') {
|
|
71
|
+
this.unwatchSession(event.sessionId);
|
|
72
|
+
}
|
|
73
|
+
this.loadSessions();
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
this.loadSessions();
|
|
77
|
+
this.preferenceService.onPreferenceChanged(e => {
|
|
78
|
+
if (e.preferenceName === PERSISTED_SESSION_LIMIT_PREF || e.preferenceName === SESSION_STORAGE_PREF) {
|
|
79
|
+
this.loadSessions();
|
|
80
|
+
}
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
isUnread(sessionId: string): boolean {
|
|
85
|
+
return this.unreadSessions.get(sessionId)?.unread === true;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
protected watchSession(session: ChatSession): void {
|
|
89
|
+
if (this.unreadSessions.has(session.id)) {
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
const reqs = session.model.getRequests();
|
|
93
|
+
const state = {
|
|
94
|
+
unread: false,
|
|
95
|
+
requiresAction: ChatSessionStatus.requiresUserAction(session.model.status),
|
|
96
|
+
seenRequests: reqs.length,
|
|
97
|
+
seenCompleted: this.countCompleted(reqs),
|
|
98
|
+
listener: new DisposableCollection()
|
|
99
|
+
};
|
|
100
|
+
this.unreadSessions.set(session.id, state);
|
|
101
|
+
|
|
102
|
+
session.model.onDidChange(() => {
|
|
103
|
+
const requiresAction = ChatSessionStatus.requiresUserAction(session.model.status);
|
|
104
|
+
if (requiresAction !== state.requiresAction) {
|
|
105
|
+
state.requiresAction = requiresAction;
|
|
106
|
+
if (requiresAction) {
|
|
107
|
+
this.expandAncestors(session);
|
|
108
|
+
}
|
|
109
|
+
this.onStateChangedEmitter.fire();
|
|
110
|
+
}
|
|
111
|
+
const current = session.model.getRequests();
|
|
112
|
+
if (current.length > state.seenRequests || this.countCompleted(current) > state.seenCompleted) {
|
|
113
|
+
const activeSession = this.chatService.getActiveSession();
|
|
114
|
+
const chatViewFocused = ChatViewWidget.findActive(this.shell) !== undefined;
|
|
115
|
+
if (chatViewFocused && activeSession && activeSession.id === session.id) {
|
|
116
|
+
state.seenRequests = current.length;
|
|
117
|
+
state.seenCompleted = this.countCompleted(current);
|
|
118
|
+
} else if (!state.unread) {
|
|
119
|
+
state.unread = true;
|
|
120
|
+
this.onUnreadChangedEmitter.fire(session.id);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}, undefined, state.listener);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
protected markSessionRead(sessionId: string): void {
|
|
127
|
+
const state = this.unreadSessions.get(sessionId);
|
|
128
|
+
if (!state) {
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
const session = this.chatService.getSession(sessionId);
|
|
132
|
+
const reqs = session?.model.getRequests() ?? [];
|
|
133
|
+
state.seenRequests = reqs.length;
|
|
134
|
+
state.seenCompleted = this.countCompleted(reqs);
|
|
135
|
+
if (state.unread) {
|
|
136
|
+
state.unread = false;
|
|
137
|
+
this.onUnreadChangedEmitter.fire(sessionId);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
protected unwatchSession(sessionId: string): void {
|
|
142
|
+
const state = this.unreadSessions.get(sessionId);
|
|
143
|
+
if (state) {
|
|
144
|
+
state.listener.dispose();
|
|
145
|
+
this.unreadSessions.delete(sessionId);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
private countCompleted(reqs: ReturnType<ChatSession['model']['getRequests']>): number {
|
|
150
|
+
return reqs.filter(r => r.response.isComplete).length;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
isPersistenceEnabled(): boolean {
|
|
154
|
+
const limit = this.preferenceService.get<number>(PERSISTED_SESSION_LIMIT_PREF, 25);
|
|
155
|
+
return limit !== 0;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
protected async loadSessions(): Promise<void> {
|
|
159
|
+
if (!this.isPersistenceEnabled()) {
|
|
160
|
+
this._persistedSessions = [];
|
|
161
|
+
this.onStateChangedEmitter.fire();
|
|
162
|
+
return;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
const hasSessions = await this.chatService.hasPersistedSessions();
|
|
166
|
+
if (!hasSessions) {
|
|
167
|
+
this._persistedSessions = [];
|
|
168
|
+
this.onStateChangedEmitter.fire();
|
|
169
|
+
return;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
try {
|
|
173
|
+
const index = await this.chatService.getPersistedSessions();
|
|
174
|
+
this._persistedSessions = Object.values(index)
|
|
175
|
+
.toSorted((a, b) => b.saveDate - a.saveDate);
|
|
176
|
+
} catch (error) {
|
|
177
|
+
console.error('Failed to load persisted sessions:', error);
|
|
178
|
+
this._persistedSessions = [];
|
|
179
|
+
} finally {
|
|
180
|
+
this.onStateChangedEmitter.fire();
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
getSections(): SectionedSessions {
|
|
185
|
+
const activeRaw = this.chatService.getSessions().filter(s => !!s.title);
|
|
186
|
+
const activeIds = new Set(activeRaw.map(s => s.id));
|
|
187
|
+
const active: ChatSessionMetadata[] = activeRaw
|
|
188
|
+
.toSorted((a, b) => (b.lastInteraction?.getTime() ?? 0) - (a.lastInteraction?.getTime() ?? 0))
|
|
189
|
+
.map(session => ({
|
|
190
|
+
sessionId: session.id,
|
|
191
|
+
title: session.title!,
|
|
192
|
+
saveDate: session.lastInteraction?.getTime() ?? Date.now(),
|
|
193
|
+
location: session.model.location,
|
|
194
|
+
pinnedAgentId: session.pinnedAgent?.id,
|
|
195
|
+
hasError: session.model.status === 'failed',
|
|
196
|
+
rootSessionId: session.rootSessionId,
|
|
197
|
+
parentSessionId: session.parentSessionId
|
|
198
|
+
}));
|
|
199
|
+
const restored = this._persistedSessions.filter(metadata => !activeIds.has(metadata.sessionId));
|
|
200
|
+
return { active, restored };
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
toggleExpand(sessionId: string): void {
|
|
204
|
+
this.expandedRoots = new Set(this.expandedRoots);
|
|
205
|
+
if (this.expandedRoots.has(sessionId)) {
|
|
206
|
+
this.expandedRoots.delete(sessionId);
|
|
207
|
+
} else {
|
|
208
|
+
this.expandedRoots.add(sessionId);
|
|
209
|
+
}
|
|
210
|
+
this.onStateChangedEmitter.fire();
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
isExpanded(sessionId: string): boolean {
|
|
214
|
+
return this.expandedRoots.has(sessionId);
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
sessionRequiresAction(sessionId: string): boolean {
|
|
218
|
+
const session = this.chatService.getSession(sessionId);
|
|
219
|
+
return session !== undefined && ChatSessionStatus.requiresUserAction(session.model.status);
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
descendantRequiresAction(row: SessionRow): boolean {
|
|
223
|
+
return row.childSessions.some(child =>
|
|
224
|
+
this.sessionRequiresAction(child.session.sessionId) || this.descendantRequiresAction(child));
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
protected expandAncestors(session: ChatSession): void {
|
|
228
|
+
const persistedById = new Map(this._persistedSessions.map(metadata => [metadata.sessionId, metadata]));
|
|
229
|
+
const parentIdFor = (id: string): string | undefined => {
|
|
230
|
+
const loaded = this.chatService.getSession(id);
|
|
231
|
+
if (loaded) {
|
|
232
|
+
return loaded.parentSessionId ?? loaded.rootSessionId;
|
|
233
|
+
}
|
|
234
|
+
const metadata = persistedById.get(id);
|
|
235
|
+
return metadata && parentIdOf(metadata);
|
|
236
|
+
};
|
|
237
|
+
const visited = new Set<string>();
|
|
238
|
+
let parentId = session.parentSessionId ?? session.rootSessionId;
|
|
239
|
+
while (parentId && !visited.has(parentId)) {
|
|
240
|
+
visited.add(parentId);
|
|
241
|
+
this.expandedRoots.add(parentId);
|
|
242
|
+
parentId = parentIdFor(parentId);
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
buildRows(sections: SectionedSessions): SessionRow[] {
|
|
247
|
+
const allSessions = [...sections.active, ...sections.restored];
|
|
248
|
+
const activeIds = new Set(sections.active.map(s => s.sessionId));
|
|
249
|
+
const rowsById = new Map<string, SessionRow>();
|
|
250
|
+
for (const session of allSessions) {
|
|
251
|
+
rowsById.set(session.sessionId, { session, isRestored: !activeIds.has(session.sessionId), childSessions: [] });
|
|
252
|
+
}
|
|
253
|
+
for (const session of allSessions) {
|
|
254
|
+
const pid = parentIdOf(session);
|
|
255
|
+
if (pid) {
|
|
256
|
+
rowsById.get(pid)?.childSessions.push(rowsById.get(session.sessionId)!);
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
return [...rowsById.values()];
|
|
260
|
+
}
|
|
261
|
+
}
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
|
|
17
17
|
import {
|
|
18
18
|
ChatAgentService, ChatRequestModel, ChatResponseContent, ChatSession, ChatSessionMetadata,
|
|
19
|
-
ErrorChatResponseContent, FormattedProviderError, formatProviderError, ThinkingChatResponseContent
|
|
19
|
+
ChatSessionStatus, ErrorChatResponseContent, FormattedProviderError, formatProviderError, ThinkingChatResponseContent
|
|
20
20
|
} from '@theia/ai-chat';
|
|
21
21
|
import { MarkdownRenderer } from '@theia/core/lib/browser/markdown-rendering/markdown-renderer';
|
|
22
22
|
import { nls } from '@theia/core/lib/common/nls';
|
|
@@ -89,27 +89,41 @@ function addDefinitionEntry(definitionList: HTMLDListElement, term: string, deta
|
|
|
89
89
|
export function buildSessionTooltip(
|
|
90
90
|
session: ChatSession, metadata: ChatSessionMetadata,
|
|
91
91
|
agentService: ChatAgentService, markdownRenderer: MarkdownRenderer,
|
|
92
|
-
isUnread: boolean,
|
|
92
|
+
isUnread: boolean, descendantNeedsAttention: boolean = false
|
|
93
93
|
): SessionTooltip {
|
|
94
94
|
const toDispose = new DisposableCollection();
|
|
95
95
|
const requests = session.model.getRequests();
|
|
96
96
|
const lastRequest = requests.at(-1);
|
|
97
|
+
const status = session.model.status;
|
|
97
98
|
|
|
98
99
|
const container = document.createElement('div');
|
|
99
100
|
container.className = 'theia-chat-session-tooltip';
|
|
100
101
|
|
|
101
|
-
//
|
|
102
|
-
|
|
102
|
+
// Waiting on the user takes precedence over the generic running badge: it signals that the
|
|
103
|
+
// user, not the agent, needs to act.
|
|
104
|
+
if (status === 'awaitingApproval') {
|
|
105
|
+
const badge = document.createElement('div');
|
|
106
|
+
badge.className = 'theia-chat-session-badge-attention-tooltip';
|
|
107
|
+
badge.textContent = nls.localize('theia/ai/ide/requiresApproval', 'Requires your approval');
|
|
108
|
+
container.appendChild(badge);
|
|
109
|
+
} else if (status === 'awaitingInput') {
|
|
103
110
|
const badge = document.createElement('div');
|
|
104
111
|
badge.className = 'theia-chat-session-badge-attention-tooltip';
|
|
105
112
|
badge.textContent = nls.localize('theia/ai/ide/waitingForInput', 'Waiting for your input');
|
|
106
113
|
container.appendChild(badge);
|
|
107
|
-
} else if (
|
|
114
|
+
} else if (descendantNeedsAttention) {
|
|
115
|
+
// The session itself is not awaiting the user, but one of its delegated children is;
|
|
116
|
+
// surface that instead of the generic running badge so the reason is clear.
|
|
117
|
+
const badge = document.createElement('div');
|
|
118
|
+
badge.className = 'theia-chat-session-badge-attention-tooltip';
|
|
119
|
+
badge.textContent = nls.localize('theia/ai/ide/childInteractionNeeded', 'A delegated session needs your attention');
|
|
120
|
+
container.appendChild(badge);
|
|
121
|
+
} else if (ChatSessionStatus.isInProgress(status)) {
|
|
108
122
|
const badge = document.createElement('div');
|
|
109
123
|
badge.className = 'theia-chat-session-badge-running-tooltip';
|
|
110
124
|
badge.textContent = nls.localizeByDefault('Running');
|
|
111
125
|
container.appendChild(badge);
|
|
112
|
-
} else if (
|
|
126
|
+
} else if (status === 'failed') {
|
|
113
127
|
const badge = document.createElement('div');
|
|
114
128
|
badge.className = 'theia-chat-session-badge-error-tooltip';
|
|
115
129
|
badge.textContent = nls.localizeByDefault('Error');
|
|
@@ -123,7 +137,7 @@ export function buildSessionTooltip(
|
|
|
123
137
|
|
|
124
138
|
if (lastRequest) {
|
|
125
139
|
const lastResponse = lastRequest.response;
|
|
126
|
-
const errorText =
|
|
140
|
+
const errorText = status === 'failed' ? getResponseErrorMessage(lastResponse) : undefined;
|
|
127
141
|
|
|
128
142
|
if (errorText) {
|
|
129
143
|
const label = document.createElement('div');
|
|
@@ -175,7 +189,7 @@ export function buildSessionTooltip(
|
|
|
175
189
|
const exchangeLabel = count === 1
|
|
176
190
|
? nls.localize('theia/ai/ide/tooltip/oneExchange', '1 exchange')
|
|
177
191
|
: nls.localize('theia/ai/ide/tooltip/multipleExchanges', '{0} exchanges', count);
|
|
178
|
-
addDefinitionEntry(definitionList, nls.
|
|
192
|
+
addDefinitionEntry(definitionList, nls.localizeByDefault('Messages'), exchangeLabel);
|
|
179
193
|
|
|
180
194
|
const date = session.lastInteraction ?? new Date(metadata.saveDate);
|
|
181
195
|
addDefinitionEntry(definitionList, nls.localize('theia/ai/ide/tooltip/lastActivity', 'Last activity'), date.toLocaleString());
|
|
@@ -189,14 +203,20 @@ export function buildSessionTooltip(
|
|
|
189
203
|
* shown on the home view). Avoids any restore I/O so hover does not promote the item to Active.
|
|
190
204
|
*/
|
|
191
205
|
export function buildRestoredSessionTooltip(
|
|
192
|
-
metadata: ChatSessionMetadata, agentService: ChatAgentService
|
|
206
|
+
metadata: ChatSessionMetadata, agentService: ChatAgentService, descendantNeedsAttention: boolean = false
|
|
193
207
|
): SessionTooltip {
|
|
194
208
|
const container = document.createElement('div');
|
|
195
209
|
container.className = 'theia-chat-session-tooltip';
|
|
196
210
|
|
|
197
211
|
const badge = document.createElement('div');
|
|
198
|
-
|
|
199
|
-
|
|
212
|
+
if (descendantNeedsAttention) {
|
|
213
|
+
// A persisted parent whose delegated child needs the user; show that rather than "Restored".
|
|
214
|
+
badge.className = 'theia-chat-session-badge-attention-tooltip';
|
|
215
|
+
badge.textContent = nls.localize('theia/ai/ide/childInteractionNeeded', 'A delegated session needs your attention');
|
|
216
|
+
} else {
|
|
217
|
+
badge.className = 'theia-chat-session-badge-restored-tooltip';
|
|
218
|
+
badge.textContent = nls.localize('theia/ai/ide/restoredSession', 'Restored session');
|
|
219
|
+
}
|
|
200
220
|
container.appendChild(badge);
|
|
201
221
|
|
|
202
222
|
const definitionList = document.createElement('dl');
|