mirror of
https://github.com/microsoft/vscode.git
synced 2026-04-02 08:15:56 +01:00
Merge branch 'main' into dev/mjbvz/liable-firefly
This commit is contained in:
@@ -105,7 +105,7 @@ export class CopilotAgent extends Disposable implements IAgent {
|
||||
}
|
||||
}
|
||||
env['COPILOT_CLI_RUN_AS_NODE'] = '1';
|
||||
env['USE_BUILTIN_RIPGREP'] = '0';
|
||||
env['USE_BUILTIN_RIPGREP'] = 'false';
|
||||
|
||||
// Resolve the CLI entry point from node_modules. We can't use require.resolve()
|
||||
// because @github/copilot's exports map blocks direct subpath access.
|
||||
|
||||
@@ -644,8 +644,8 @@ export class ChangesViewPane extends ViewPane {
|
||||
const ciWidget = this.ciStatusWidget;
|
||||
const ciPane: IView = {
|
||||
element: ciElement,
|
||||
minimumSize: ciMinHeight,
|
||||
maximumSize: Number.POSITIVE_INFINITY,
|
||||
get minimumSize() { return ciWidget.collapsed ? CIStatusWidget.HEADER_HEIGHT : ciMinHeight; },
|
||||
get maximumSize() { return ciWidget.collapsed ? CIStatusWidget.HEADER_HEIGHT : Number.POSITIVE_INFINITY; },
|
||||
onDidChange: Event.map(this.ciStatusWidget.onDidChangeHeight, () => undefined),
|
||||
layout: (height) => {
|
||||
ciElement.style.height = `${height}px`;
|
||||
@@ -668,6 +668,25 @@ export class ChangesViewPane extends ViewPane {
|
||||
// Initially hide CI pane until checks arrive
|
||||
this.splitView.setViewVisible(1, false);
|
||||
|
||||
let savedCIPaneHeight = CIStatusWidget.HEADER_HEIGHT + CIStatusWidget.PREFERRED_BODY_HEIGHT;
|
||||
this._register(this.ciStatusWidget.onDidToggleCollapsed(collapsed => {
|
||||
if (!this.splitView || !this.ciStatusWidget) {
|
||||
return;
|
||||
}
|
||||
if (collapsed) {
|
||||
// Save current size before collapsing
|
||||
const currentSize = this.splitView.getViewSize(1);
|
||||
if (currentSize > CIStatusWidget.HEADER_HEIGHT) {
|
||||
savedCIPaneHeight = currentSize;
|
||||
}
|
||||
this.splitView.resizeView(1, CIStatusWidget.HEADER_HEIGHT);
|
||||
} else {
|
||||
// Restore saved size on expand
|
||||
this.splitView.resizeView(1, savedCIPaneHeight);
|
||||
}
|
||||
this.layoutSplitView();
|
||||
}));
|
||||
|
||||
this._register(this.ciStatusWidget.onDidChangeHeight(() => {
|
||||
if (!this.splitView || !this.ciStatusWidget) {
|
||||
return;
|
||||
@@ -1001,12 +1020,6 @@ export class ChangesViewPane extends ViewPane {
|
||||
? { args: [sessionResource, this.agentSessionsService.getSession(sessionResource)?.metadata] }
|
||||
: { shouldForwardArgs: true },
|
||||
buttonConfigProvider: (action) => {
|
||||
if (
|
||||
action.id === 'chatEditing.viewAllSessionChanges' ||
|
||||
action.id === 'github.copilot.chat.openPullRequestCopilotCLIAgentSession.openPR'
|
||||
) {
|
||||
return { showIcon: true, showLabel: false, isSecondary: true };
|
||||
}
|
||||
if (action.id === 'github.copilot.chat.createPullRequestCopilotCLIAgentSession.updatePR') {
|
||||
const customLabel = outgoingChanges > 0
|
||||
? `${action.label} ${outgoingChanges}↑`
|
||||
@@ -1022,9 +1035,20 @@ export class ChangesViewPane extends ViewPane {
|
||||
}
|
||||
return { showIcon: true, showLabel: false, isSecondary: true };
|
||||
}
|
||||
if (
|
||||
action.id === 'chatEditing.viewAllSessionChanges' ||
|
||||
action.id === 'github.copilot.chat.openPullRequestCopilotCLIAgentSession.openPR'
|
||||
) {
|
||||
return { showIcon: true, showLabel: false, isSecondary: true };
|
||||
}
|
||||
if (action.id === 'agentFeedbackEditor.action.submitActiveSession') {
|
||||
return { showIcon: false, showLabel: true, isSecondary: false };
|
||||
}
|
||||
if (
|
||||
action.id === 'github.copilot.chat.createPullRequestCopilotCLIAgentSession.createPR' ||
|
||||
action.id === 'github.copilot.chat.mergeCopilotCLIAgentSessionChanges.merge' ||
|
||||
action.id === 'github.copilot.chat.checkoutPullRequestReroute' ||
|
||||
action.id === 'pr.checkoutFromChat' ||
|
||||
action.id === 'github.copilot.sessions.initializeRepository' ||
|
||||
action.id === 'github.copilot.sessions.commitChanges' ||
|
||||
action.id === 'agentSession.markAsDone'
|
||||
@@ -1032,6 +1056,16 @@ export class ChangesViewPane extends ViewPane {
|
||||
return { showIcon: true, showLabel: true, isSecondary: false };
|
||||
}
|
||||
|
||||
// Unknown actions (e.g. extension-contributed): only hide the label when an icon is present.
|
||||
if (action instanceof MenuItemAction) {
|
||||
const icon = action.item.icon;
|
||||
if (icon) {
|
||||
// Icon-only button (no forced secondary state so primary/secondary can be inferred).
|
||||
return { showIcon: true, showLabel: false };
|
||||
}
|
||||
}
|
||||
|
||||
// Fall back to default button behavior for actions without an icon.
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -148,7 +148,7 @@ class CICheckListRenderer implements IListRenderer<ICICheckListItem, ICICheckTem
|
||||
*/
|
||||
export class CIStatusWidget extends Disposable {
|
||||
|
||||
static readonly HEADER_HEIGHT = 30;
|
||||
static readonly HEADER_HEIGHT = 38; // total header height in px
|
||||
static readonly MIN_BODY_HEIGHT = 84; // at least 3 checks (3 * 28)
|
||||
static readonly PREFERRED_BODY_HEIGHT = 112; // preferred 4 checks (4 * 28)
|
||||
static readonly MAX_BODY_HEIGHT = 240; // at most ~8 checks
|
||||
@@ -168,9 +168,14 @@ export class CIStatusWidget extends Disposable {
|
||||
private readonly _onDidChangeHeight = this._register(new Emitter<void>());
|
||||
readonly onDidChangeHeight = this._onDidChangeHeight.event;
|
||||
|
||||
private readonly _onDidToggleCollapsed = this._register(new Emitter<boolean>());
|
||||
readonly onDidToggleCollapsed = this._onDidToggleCollapsed.event;
|
||||
|
||||
private _checkCount = 0;
|
||||
private _collapsed = false;
|
||||
private _model: GitHubPullRequestCIModel | undefined;
|
||||
private _sessionResource: URI | undefined;
|
||||
private readonly _chevronNode: HTMLElement;
|
||||
|
||||
get element(): HTMLElement {
|
||||
return this._domNode;
|
||||
@@ -181,6 +186,9 @@ export class CIStatusWidget extends Disposable {
|
||||
if (this._checkCount === 0) {
|
||||
return 0;
|
||||
}
|
||||
if (this._collapsed) {
|
||||
return CIStatusWidget.HEADER_HEIGHT;
|
||||
}
|
||||
return CIStatusWidget.HEADER_HEIGHT + this._checkCount * CICheckListDelegate.ITEM_HEIGHT;
|
||||
}
|
||||
|
||||
@@ -189,6 +197,11 @@ export class CIStatusWidget extends Disposable {
|
||||
return this._checkCount > 0;
|
||||
}
|
||||
|
||||
/** Whether the body is collapsed (header-only). */
|
||||
get collapsed(): boolean {
|
||||
return this._collapsed;
|
||||
}
|
||||
|
||||
constructor(
|
||||
container: HTMLElement,
|
||||
@IOpenerService private readonly _openerService: IOpenerService,
|
||||
@@ -201,11 +214,11 @@ export class CIStatusWidget extends Disposable {
|
||||
this._domNode = dom.append(container, $('.ci-status-widget'));
|
||||
this._domNode.style.display = 'none';
|
||||
|
||||
// Header (always visible)
|
||||
// Header (always visible, click to collapse/expand)
|
||||
this._headerNode = dom.append(this._domNode, $('.ci-status-widget-header'));
|
||||
this._titleNode = dom.append(this._headerNode, $('.ci-status-widget-title'));
|
||||
this._titleLabelNode = dom.append(this._titleNode, $('.ci-status-widget-title-label'));
|
||||
this._titleLabelNode.textContent = localize('ci.checksLabel', "PR Checks");
|
||||
this._titleLabelNode.textContent = localize('ci.checksLabel', "Checks");
|
||||
this._countsNode = dom.append(this._titleNode, $('.ci-status-widget-counts'));
|
||||
this._headerActionBarContainer = dom.append(this._headerNode, $('.ci-status-widget-header-actions'));
|
||||
this._headerActionBar = this._register(new ActionBar(this._headerActionBarContainer));
|
||||
@@ -213,9 +226,33 @@ export class CIStatusWidget extends Disposable {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
}));
|
||||
this._chevronNode = dom.append(this._headerNode, $('.group-chevron'));
|
||||
this._chevronNode.classList.add(...ThemeIcon.asClassNameArray(Codicon.chevronDown));
|
||||
|
||||
this._headerNode.setAttribute('role', 'button');
|
||||
this._headerNode.setAttribute('aria-label', localize('ci.toggleChecks', "Toggle PR Checks"));
|
||||
this._headerNode.setAttribute('aria-expanded', 'true');
|
||||
this._headerNode.tabIndex = 0;
|
||||
|
||||
this._register(dom.addDisposableListener(this._headerNode, dom.EventType.CLICK, e => {
|
||||
// Don't toggle when clicking the action bar
|
||||
if (dom.isAncestor(e.target as HTMLElement, this._headerActionBarContainer)) {
|
||||
return;
|
||||
}
|
||||
this._toggleCollapsed();
|
||||
}));
|
||||
this._register(dom.addDisposableListener(this._headerNode, dom.EventType.KEY_DOWN, e => {
|
||||
if ((e.key === 'Enter' || e.key === ' ') && e.target === this._headerNode) {
|
||||
e.preventDefault();
|
||||
this._toggleCollapsed();
|
||||
}
|
||||
}));
|
||||
|
||||
// Body (list of checks)
|
||||
this._bodyNode = dom.append(this._domNode, $('.ci-status-widget-body'));
|
||||
const bodyId = 'ci-status-widget-body';
|
||||
this._bodyNode = dom.append(this._domNode, $(`.${bodyId}`));
|
||||
this._bodyNode.id = bodyId;
|
||||
this._headerNode.setAttribute('aria-controls', bodyId);
|
||||
|
||||
const listContainer = $('.ci-status-widget-list');
|
||||
this._list = this._register(this._instantiationService.createInstance(
|
||||
@@ -250,6 +287,7 @@ export class CIStatusWidget extends Disposable {
|
||||
this._model = model;
|
||||
if (!model) {
|
||||
this._checkCount = 0;
|
||||
this._setCollapsed(false);
|
||||
this._renderBody([]);
|
||||
this._renderHeaderActions([]);
|
||||
this._domNode.style.display = 'none';
|
||||
@@ -261,6 +299,7 @@ export class CIStatusWidget extends Disposable {
|
||||
|
||||
if (checks.length === 0) {
|
||||
this._checkCount = 0;
|
||||
this._setCollapsed(false);
|
||||
this._renderBody([]);
|
||||
this._renderHeaderActions([]);
|
||||
this._domNode.style.display = 'none';
|
||||
@@ -344,9 +383,36 @@ export class CIStatusWidget extends Disposable {
|
||||
* Called by the parent view after computing available space.
|
||||
*/
|
||||
layout(height: number): void {
|
||||
if (this._collapsed) {
|
||||
this._bodyNode.style.display = 'none';
|
||||
return;
|
||||
}
|
||||
this._bodyNode.style.display = '';
|
||||
this._list.layout(height);
|
||||
}
|
||||
|
||||
private _toggleCollapsed(): void {
|
||||
this._setCollapsed(!this._collapsed);
|
||||
this._onDidToggleCollapsed.fire(this._collapsed);
|
||||
// Also fires onDidChangeHeight so the SplitView pane updates its min/max constraints
|
||||
this._onDidChangeHeight.fire();
|
||||
}
|
||||
|
||||
private _setCollapsed(collapsed: boolean): void {
|
||||
this._collapsed = collapsed;
|
||||
this._updateChevron();
|
||||
this._headerNode.setAttribute('aria-expanded', String(!collapsed));
|
||||
}
|
||||
|
||||
private _updateChevron(): void {
|
||||
this._chevronNode.className = 'group-chevron';
|
||||
this._chevronNode.classList.add(
|
||||
...ThemeIcon.asClassNameArray(
|
||||
this._collapsed ? Codicon.chevronRight : Codicon.chevronDown
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
private _renderBody(checks: readonly ICICheckListItem[]): void {
|
||||
this._list.splice(0, this._list.length, checks);
|
||||
}
|
||||
@@ -421,7 +487,7 @@ function getCheckCounts(checks: readonly IGitHubCICheck[]): ICICheckCounts {
|
||||
function getCheckIcon(check: IGitHubCICheck): ThemeIcon {
|
||||
switch (check.status) {
|
||||
case GitHubCheckStatus.InProgress:
|
||||
return Codicon.clock;
|
||||
return Codicon.sync;
|
||||
case GitHubCheckStatus.Queued:
|
||||
return Codicon.circleFilled;
|
||||
case GitHubCheckStatus.Completed:
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
padding: 8px;
|
||||
padding: 4px 8px 8px 8px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
@@ -136,7 +136,7 @@
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
flex-wrap: nowrap;
|
||||
gap: 6px;
|
||||
gap: 4px;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
@@ -149,7 +149,7 @@
|
||||
/* Larger action buttons matching SCM ActionButton style */
|
||||
.changes-view-body .chat-editing-session-actions.outside-card .monaco-button {
|
||||
height: 26px;
|
||||
padding: 4px 14px;
|
||||
padding: 4px;
|
||||
font-size: 12px;
|
||||
line-height: 18px;
|
||||
}
|
||||
@@ -157,19 +157,34 @@
|
||||
/* Primary button grows to fill available space */
|
||||
.changes-view-body .chat-editing-session-actions.outside-card .monaco-button:not(.secondary) {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.changes-view-body .chat-editing-session-actions.outside-card .monaco-button:not(.secondary) > span:not(.codicon) {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
/* ButtonWithDropdown container grows to fill available space */
|
||||
.changes-view-body .chat-editing-session-actions.outside-card .monaco-button-dropdown {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.changes-view-body .chat-editing-session-actions.outside-card .monaco-button-dropdown > .monaco-button {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.changes-view-body .chat-editing-session-actions.outside-card .monaco-button-dropdown > .monaco-button > span:not(.codicon) {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.changes-view-body .chat-editing-session-actions.outside-card .monaco-button-dropdown > .monaco-button-dropdown-separator {
|
||||
flex: 0;
|
||||
}
|
||||
|
||||
@@ -18,10 +18,50 @@
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
padding: 2px 0;
|
||||
/* gap: 6px; */
|
||||
padding: 6px 4px;
|
||||
margin-top: 4px;
|
||||
border-radius: 8px;
|
||||
min-height: 22px;
|
||||
font-weight: 500;
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.ci-status-widget-header:hover {
|
||||
background-color: var(--vscode-list-hoverBackground);
|
||||
padding-right: 22px;
|
||||
}
|
||||
|
||||
.ci-status-widget-header:focus {
|
||||
padding-right: 22px;
|
||||
}
|
||||
|
||||
.ci-status-widget-header:focus-visible {
|
||||
outline: 1px solid var(--vscode-focusBorder);
|
||||
outline-offset: -1px;
|
||||
}
|
||||
|
||||
/* Chevron — right-aligned, visible on hover only */
|
||||
.ci-status-widget-header .group-chevron {
|
||||
position: absolute;
|
||||
right: 4px;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 12px;
|
||||
visibility: hidden;
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.ci-status-widget-header:hover .group-chevron,
|
||||
.ci-status-widget-header:focus-within .group-chevron {
|
||||
visibility: visible;
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
/* Title - single line, overflow ellipsis */
|
||||
@@ -50,8 +90,7 @@
|
||||
padding-right: 8px;
|
||||
}
|
||||
|
||||
.ci-status-widget.has-fix-actions:hover .ci-status-widget-counts,
|
||||
.ci-status-widget.has-fix-actions:focus-within .ci-status-widget-counts {
|
||||
.ci-status-widget-header:hover .ci-status-widget-counts, .ci-status-widget-header:focus .ci-status-widget-counts {
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
@@ -84,15 +123,9 @@
|
||||
}
|
||||
|
||||
.ci-status-widget-header-actions {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
display: none;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.ci-status-widget:hover .ci-status-widget-header-actions.has-actions,
|
||||
.ci-status-widget:focus-within .ci-status-widget-header-actions.has-actions {
|
||||
flex-shrink: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.ci-status-widget-header-actions .monaco-action-bar {
|
||||
@@ -151,6 +184,12 @@
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.ci-status-widget-check-label .monaco-icon-label::before {
|
||||
font-size: 14px;
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
}
|
||||
|
||||
.ci-status-widget-check-label .monaco-icon-label-container,
|
||||
.ci-status-widget-check-label .monaco-icon-name-container {
|
||||
display: block;
|
||||
|
||||
@@ -368,15 +368,16 @@ export class SetupAgent extends Disposable implements IChatAgentImplementation {
|
||||
const disposables = new DisposableStore();
|
||||
disposables.add(toDisposable(() => clearTimeout(timeoutHandle)));
|
||||
try {
|
||||
const allReady = Promise.allSettled([
|
||||
whenAgentActivated,
|
||||
whenAgentReady,
|
||||
whenLanguageModelReady,
|
||||
whenToolsModelReady
|
||||
]);
|
||||
const ready = await Promise.race([
|
||||
timeout(this.environmentService.remoteAuthority ? 60000 /* increase for remote scenarios */ : 20000).then(() => 'timedout'),
|
||||
this.whenPanelAgentHasGuidance(disposables).then(() => 'panelGuidance'),
|
||||
Promise.allSettled([
|
||||
whenAgentActivated,
|
||||
whenAgentReady,
|
||||
whenLanguageModelReady,
|
||||
whenToolsModelReady
|
||||
])
|
||||
allReady
|
||||
]);
|
||||
|
||||
if (ready === 'panelGuidance') {
|
||||
@@ -401,41 +402,9 @@ export class SetupAgent extends Disposable implements IChatAgentImplementation {
|
||||
warningMessage = localize('chatTookLongWarning', "Chat took too long to get ready. Please ensure you are signed in to {0} and that the extension `{1}` is installed and enabled. Click restart to try again if this issue persists.", defaultChat.provider.default.name, defaultChat.chatExtensionId);
|
||||
}
|
||||
|
||||
// Compute language model diagnostic info
|
||||
const languageModelIds = languageModelsService.getLanguageModelIds();
|
||||
let languageModelDefaultCount = 0;
|
||||
for (const id of languageModelIds) {
|
||||
const model = languageModelsService.lookupLanguageModel(id);
|
||||
if (model?.isDefaultForLocation[ChatAgentLocation.Chat]) {
|
||||
languageModelDefaultCount++;
|
||||
}
|
||||
}
|
||||
const diagnosticInfo = this.computeDiagnosticInfo(agentActivated, agentReady, languageModelReady, toolsModelReady, requestModel, languageModelsService, chatAgentService, modeInfo);
|
||||
|
||||
// Compute agent diagnostic info
|
||||
const defaultAgent = chatAgentService.getDefaultAgent(this.location, modeInfo?.kind);
|
||||
const agentHasDefault = !!defaultAgent;
|
||||
const agentDefaultIsCore = defaultAgent?.isCore ?? false;
|
||||
const contributedDefaultAgent = chatAgentService.getContributedDefaultAgent(this.location);
|
||||
const agentHasContributedDefault = !!contributedDefaultAgent;
|
||||
const agentContributedDefaultIsCore = contributedDefaultAgent?.isCore ?? false;
|
||||
const agentActivatedCount = chatAgentService.getActivatedAgents().length;
|
||||
|
||||
this.logService.warn(warningMessage, {
|
||||
agentActivated,
|
||||
agentReady,
|
||||
agentHasDefault,
|
||||
agentDefaultIsCore,
|
||||
agentHasContributedDefault,
|
||||
agentContributedDefaultIsCore,
|
||||
agentActivatedCount,
|
||||
agentLocation: this.location,
|
||||
agentModeKind: modeInfo?.kind,
|
||||
languageModelReady,
|
||||
languageModelCount: languageModelIds.length,
|
||||
languageModelDefaultCount,
|
||||
languageModelHasRequestedModel: !!requestModel.modelId,
|
||||
toolsModelReady
|
||||
});
|
||||
this.logService.warn(`[chat setup] ${warningMessage}`, diagnosticInfo);
|
||||
|
||||
type ChatSetupTimeoutClassification = {
|
||||
owner: 'chrmarti';
|
||||
@@ -477,28 +446,8 @@ export class SetupAgent extends Disposable implements IChatAgentImplementation {
|
||||
isAnonymous: boolean;
|
||||
matchingWelcomeViewWhen: string;
|
||||
};
|
||||
const chatViewPane = this.viewsService.getActiveViewWithId(ChatViewId) as ChatViewPane | undefined;
|
||||
const matchingWelcomeView = chatViewPane?.getMatchingWelcomeView();
|
||||
|
||||
this.telemetryService.publicLog2<ChatSetupTimeoutEvent, ChatSetupTimeoutClassification>('chatSetup.timeout', {
|
||||
agentActivated,
|
||||
agentReady,
|
||||
agentHasDefault,
|
||||
agentDefaultIsCore,
|
||||
agentHasContributedDefault,
|
||||
agentContributedDefaultIsCore,
|
||||
agentActivatedCount,
|
||||
agentLocation: this.location,
|
||||
agentModeKind: modeInfo?.kind ?? '',
|
||||
languageModelReady,
|
||||
languageModelCount: languageModelIds.length,
|
||||
languageModelDefaultCount,
|
||||
languageModelHasRequestedModel: !!requestModel.modelId,
|
||||
toolsModelReady,
|
||||
isRemote: !!this.environmentService.remoteAuthority,
|
||||
isAnonymous: this.chatEntitlementService.anonymous,
|
||||
matchingWelcomeViewWhen: matchingWelcomeView?.when.serialize() ?? (chatViewPane ? 'noWelcomeView' : 'noChatViewPane'),
|
||||
});
|
||||
this.telemetryService.publicLog2<ChatSetupTimeoutEvent, ChatSetupTimeoutClassification>('chatSetup.timeout', diagnosticInfo);
|
||||
|
||||
progress({
|
||||
kind: 'warning',
|
||||
@@ -527,10 +476,56 @@ export class SetupAgent extends Disposable implements IChatAgentImplementation {
|
||||
});
|
||||
}
|
||||
|
||||
// This means Chat is unhealthy and we cannot retry the
|
||||
// request. Signal this to the outside via an event.
|
||||
this._onUnresolvableError.fire();
|
||||
return;
|
||||
// Wait for all readiness signals and log/send
|
||||
// telemetry about recovery after the timeout.
|
||||
await allReady;
|
||||
|
||||
const recoveryDiagnosticInfo = this.computeDiagnosticInfo(agentActivated, agentReady, languageModelReady, toolsModelReady, requestModel, languageModelsService, chatAgentService, modeInfo);
|
||||
|
||||
this.logService.info('[chat setup] Chat setup timeout recovered', recoveryDiagnosticInfo);
|
||||
|
||||
type ChatSetupTimeoutRecoveryClassification = {
|
||||
owner: 'chrmarti';
|
||||
comment: 'Provides insight into chat setup timeout recovery.';
|
||||
agentActivated: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'Whether the agent was activated.' };
|
||||
agentReady: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'Whether the agent was ready.' };
|
||||
agentHasDefault: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'Whether a default agent exists for the location and mode.' };
|
||||
agentDefaultIsCore: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'Whether the default agent is a core agent.' };
|
||||
agentHasContributedDefault: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'Whether a contributed default agent exists for the location.' };
|
||||
agentContributedDefaultIsCore: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'Whether the contributed default agent is a core agent.' };
|
||||
agentActivatedCount: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; isMeasurement: true; comment: 'Number of activated agents at recovery time.' };
|
||||
agentLocation: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'The chat agent location.' };
|
||||
agentModeKind: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'The chat mode kind.' };
|
||||
languageModelReady: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'Whether the language model was ready.' };
|
||||
languageModelCount: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; isMeasurement: true; comment: 'Number of registered language models at recovery time.' };
|
||||
languageModelDefaultCount: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; isMeasurement: true; comment: 'Number of language models with isDefaultForLocation[Chat] set at recovery time.' };
|
||||
languageModelHasRequestedModel: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'Whether a specific model ID was requested.' };
|
||||
toolsModelReady: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'Whether the tools model was ready.' };
|
||||
isRemote: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'Whether this is a remote scenario.' };
|
||||
isAnonymous: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'Whether anonymous access is enabled.' };
|
||||
matchingWelcomeViewWhen: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'The when clause of the matching extension welcome view, if any.' };
|
||||
};
|
||||
type ChatSetupTimeoutRecoveryEvent = {
|
||||
agentActivated: boolean;
|
||||
agentReady: boolean;
|
||||
agentHasDefault: boolean;
|
||||
agentDefaultIsCore: boolean;
|
||||
agentHasContributedDefault: boolean;
|
||||
agentContributedDefaultIsCore: boolean;
|
||||
agentActivatedCount: number;
|
||||
agentLocation: string;
|
||||
agentModeKind: string;
|
||||
languageModelReady: boolean;
|
||||
languageModelCount: number;
|
||||
languageModelDefaultCount: number;
|
||||
languageModelHasRequestedModel: boolean;
|
||||
toolsModelReady: boolean;
|
||||
isRemote: boolean;
|
||||
isAnonymous: boolean;
|
||||
matchingWelcomeViewWhen: string;
|
||||
};
|
||||
|
||||
this.telemetryService.publicLog2<ChatSetupTimeoutRecoveryEvent, ChatSetupTimeoutRecoveryClassification>('chatSetup.timeoutRecovery', recoveryDiagnosticInfo);
|
||||
}
|
||||
} finally {
|
||||
disposables.dispose();
|
||||
@@ -647,6 +642,42 @@ export class SetupAgent extends Disposable implements IChatAgentImplementation {
|
||||
}
|
||||
}
|
||||
|
||||
private computeDiagnosticInfo(agentActivated: boolean, agentReady: boolean, languageModelReady: boolean, toolsModelReady: boolean, requestModel: IChatRequestModel, languageModelsService: ILanguageModelsService, chatAgentService: IChatAgentService, modeInfo: { kind?: ChatModeKind } | undefined) {
|
||||
const languageModelIds = languageModelsService.getLanguageModelIds();
|
||||
let languageModelDefaultCount = 0;
|
||||
for (const id of languageModelIds) {
|
||||
const model = languageModelsService.lookupLanguageModel(id);
|
||||
if (model?.isDefaultForLocation[ChatAgentLocation.Chat]) {
|
||||
languageModelDefaultCount++;
|
||||
}
|
||||
}
|
||||
|
||||
const defaultAgent = chatAgentService.getDefaultAgent(this.location, modeInfo?.kind);
|
||||
const contributedDefaultAgent = chatAgentService.getContributedDefaultAgent(this.location);
|
||||
const chatViewPane = this.viewsService.getActiveViewWithId(ChatViewId) as ChatViewPane | undefined;
|
||||
const matchingWelcomeView = chatViewPane?.getMatchingWelcomeView();
|
||||
|
||||
return {
|
||||
agentActivated,
|
||||
agentReady,
|
||||
agentHasDefault: !!defaultAgent,
|
||||
agentDefaultIsCore: defaultAgent?.isCore ?? false,
|
||||
agentHasContributedDefault: !!contributedDefaultAgent,
|
||||
agentContributedDefaultIsCore: contributedDefaultAgent?.isCore ?? false,
|
||||
agentActivatedCount: chatAgentService.getActivatedAgents().length,
|
||||
agentLocation: this.location,
|
||||
agentModeKind: modeInfo?.kind ?? '',
|
||||
languageModelReady,
|
||||
languageModelCount: languageModelIds.length,
|
||||
languageModelDefaultCount,
|
||||
languageModelHasRequestedModel: !!requestModel.modelId,
|
||||
toolsModelReady,
|
||||
isRemote: !!this.environmentService.remoteAuthority,
|
||||
isAnonymous: this.chatEntitlementService.anonymous,
|
||||
matchingWelcomeViewWhen: matchingWelcomeView?.when.serialize() ?? (chatViewPane ? 'noWelcomeView' : 'noChatViewPane'),
|
||||
};
|
||||
}
|
||||
|
||||
private async doInvokeWithSetup(request: IChatAgentRequest, progress: (part: IChatProgress) => void, chatService: IChatService, languageModelsService: ILanguageModelsService, chatWidgetService: IChatWidgetService, chatAgentService: IChatAgentService, languageModelToolsService: ILanguageModelToolsService, defaultAccountService: IDefaultAccountService): Promise<IChatAgentResult> {
|
||||
this.telemetryService.publicLog2<WorkbenchActionExecutedEvent, WorkbenchActionExecutedClassification>('workbenchActionExecuted', { id: CHAT_SETUP_ACTION_ID, from: 'chat' });
|
||||
|
||||
|
||||
@@ -122,9 +122,6 @@ export class DelegationSessionPickerActionItem extends SessionTypePickerActionIt
|
||||
}
|
||||
|
||||
protected override _getSessionDescription(sessionTypeItem: ISessionTypeItem): string | undefined {
|
||||
if (this._isSessionsWindow && sessionTypeItem.type === AgentSessionProviders.Cloud && !this._hasGitRepository()) {
|
||||
return localize('chat.cloudRequiresGit', "Requires a Git repository");
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user