Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/vs/platform/agentHost/common/openSessionLink.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ function getAgentSessionLinkPresentationStatus(status: AgentSessionLinkStatus):
switch (status) {
case 'untitled': return { kind: 'neutral', label: localize('agentSessionLink.notStarted', "Not started") };
case 'inProgress': return { kind: 'pending', label: localize('agentSessionLink.working', "Working") };
case 'needsInput': return { kind: 'warning', label: localize('agentSessionLink.needsInput', "Needs input") };
case 'needsInput': return { kind: 'warning', label: localize('agentSessionLink.needsInput', "Needs attention") };
case 'completed': return { kind: 'success', label: localize('agentSessionLink.completed', "Completed") };
case 'error': return { kind: 'error', label: localize('agentSessionLink.error', "Error") };
}
Expand Down
6 changes: 3 additions & 3 deletions src/vs/platform/agentHost/test/common/openSessionLink.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -199,9 +199,9 @@ suite('openSessionLink', () => {
kind: 'session',
title: 'Implement rich links',
detail: 'Updating core',
status: { kind: 'warning', label: 'Needs input' },
tooltip: 'Implement rich links · Needs input',
ariaLabel: 'Agent session Implement rich links, Needs input',
status: { kind: 'warning', label: 'Needs attention' },
tooltip: 'Implement rich links · Needs attention',
ariaLabel: 'Agent session Implement rich links, Needs attention',
},
chat: {
kind: 'chat',
Expand Down
2 changes: 1 addition & 1 deletion src/vs/sessions/browser/sessionConversationGroups.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ export function getSessionConversationStatusLabel(status: SessionStatus): string
case SessionStatus.InProgress:
return localize('sessionConversationStatus.inProgress', "In Progress");
case SessionStatus.NeedsInput:
return localize('sessionConversationStatus.needsInput', "Input Needed");
return localize('sessionConversationStatus.needsInput', "Attention Needed");
case SessionStatus.Completed:
return localize('sessionConversationStatus.completed', "Completed");
case SessionStatus.Error:
Expand Down
27 changes: 11 additions & 16 deletions src/vs/sessions/browser/sessionStatusIcon.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import { URI } from '../../base/common/uri.js';
import { createPixelSpinner } from '../../base/browser/ui/pixelSpinner/pixelSpinner.js';
import { asCssVariable } from '../../platform/theme/common/colorUtils.js';
import { IAccessibilityService } from '../../platform/accessibility/common/accessibility.js';
import { isActiveSessionStatus, SessionStatus } from '../services/sessions/common/session.js';
import { SessionStatus } from '../services/sessions/common/session.js';
import { ISessionsListModelService } from '../services/sessions/browser/sessionsListModelService.js';

const $ = DOM.$;
Expand All @@ -23,12 +23,10 @@ const ICON_SWAP_FADE_MS = 180;
// follow-up swap (before the previous fade finishes) skip re-processing it.
const ICON_FADING_OUT_ATTR = 'iconFadingOut';

// Sentinel cache keys used when the icon container holds an animated pixel
// spinner (vs. a codicon). Distinct per variant so transitions between variants
// rebuild the DOM, while same-variant re-renders only update color and avoid
// restarting the CSS animation.
// Sentinel cache key used when the icon container holds an animated pixel
// spinner (vs. a codicon), so re-renders only update color and avoid restarting
// the CSS animation.
const PIXEL_SPINNER_GRID_KEY = '__pixel_spinner_grid__';
const PIXEL_SPINNER_RING_KEY = '__pixel_spinner_ring__';

interface ISessionStatusInputs {
readonly status: SessionStatus;
Expand All @@ -39,9 +37,9 @@ interface ISessionStatusInputs {

/**
* Renders a session's status indicator into a host-provided container and keeps it
* up to date. In-progress / needs-input sessions get the animated pixel spinner
* (grid variant for in-progress, ring for needs-input) when motion is allowed;
* other states render the codicon from {@link ISessionsListModelService.getStatusIcon}.
* up to date. In-progress sessions get the animated pixel spinner when motion is
* allowed; other states render the codicon from
* {@link ISessionsListModelService.getStatusIcon}.
*
* The widget owns all rendering concerns so every surface (sessions list, session
* header, …) stays in sync by simply hosting it:
Expand Down Expand Up @@ -113,18 +111,16 @@ export class SessionStatusIcon extends Disposable {

private _render(inputs: ISessionStatusInputs): void {
const { status, isRead, isArchived, completedStateIcon } = inputs;
const isSpinner = isActiveSessionStatus(status) && !this._accessibilityService.isMotionReduced();
const isSpinner = status === SessionStatus.InProgress && !this._accessibilityService.isMotionReduced();

let cacheKey: string;
let color: string;
let createIcon: () => { element: HTMLElement; disposable?: IDisposable };
if (isSpinner) {
const isNeedsInput = status === SessionStatus.NeedsInput;
const variant: 'grid' | 'ring' = isNeedsInput ? 'ring' : 'grid';
cacheKey = isNeedsInput ? PIXEL_SPINNER_RING_KEY : PIXEL_SPINNER_GRID_KEY;
color = isNeedsInput ? asCssVariable('list.warningForeground') : asCssVariable('textLink.foreground');
cacheKey = PIXEL_SPINNER_GRID_KEY;
color = asCssVariable('textLink.foreground');
createIcon = () => {
const spinner = createPixelSpinner(undefined, { variant });
const spinner = createPixelSpinner(undefined, { variant: 'grid' });
return { element: spinner.element, disposable: spinner };
};
} else {
Expand All @@ -134,7 +130,6 @@ export class SessionStatusIcon extends Disposable {
createIcon = () => ({ element: $(`span${cacheKey}`) });
}

// Reduced-motion fallback for needs-input pulses the codicon; harmless when a spinner is shown.
this._container.classList.toggle('session-icon-pulse', status === SessionStatus.NeedsInput);

if (this._currentCacheKey === cacheKey) {
Expand Down
2 changes: 1 addition & 1 deletion src/vs/sessions/common/contextkeys.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ export const SessionFocusedChatIsRenameTargetContext = new RawContextKey<boolean
export const SessionActiveChatIsDeletableContext = new RawContextKey<boolean>('sessionActiveChatIsDeletable', false, localize('sessionActiveChatIsDeletable', "Whether the session's active chat can be permanently deleted from the tab strip, i.e. it is a real, user-created non-main chat (not the main chat and not a tool-spawned subagent chat, which are transient children). Used to scope the delete-chat keybinding"));
export const SessionIsReadContext = new RawContextKey<boolean>('sessionIsRead', true, localize('sessionIsRead', "Whether the session's main chat has been marked as read"));
export const SessionIsArchivedContext = new RawContextKey<boolean>('sessionIsArchived', false, localize('sessionIsArchived', "Whether the session in scope is archived/marked as done (the active session globally, or a specific session within an isolated component such as the session view or a context menu overlay)"));
export const SessionIsActiveContext = new RawContextKey<boolean>('sessionIsActive', false, localize('sessionIsActive', "Whether the session in scope is in progress or needs input"));
export const SessionIsActiveContext = new RawContextKey<boolean>('sessionIsActive', false, localize('sessionIsActive', "Whether the session in scope is in progress or needs attention"));
export const SessionHasChangesContext = new RawContextKey<boolean>('sessionHasChanges', false, localize('sessionHasChanges', "Whether the session view's session has pending changes (insertions or deletions)"));
export const SessionHasCachedChangesContext = new RawContextKey<boolean>('sessionHasCachedChanges', false, localize('sessionHasCachedChanges', "Whether the session view's session has remembered changes from the last time its changes pill was shown, while it has not reported its own changes yet. Used to render the changes pill optimistically when a session opens"));
export const SessionHasPullRequestContext = new RawContextKey<boolean>('sessionHasPullRequest', false, localize('sessionHasPullRequest', "Whether the session view's session is associated with a GitHub pull request"));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ export class SessionsChatAccessibilityHelp implements IAccessibleViewImplementat
content.push(localize('sessionsChat.compareAgentsInstructions', "The initial Judge and synthesis messages are collapsed under Judge Instructions or Synthesis Instructions. Use Tab to reach the summary, then press Enter or Space to expand or collapse the full instructions. The complete instructions remain in the conversation and in its Accessible View."));
content.push(localize('sessionsChat.compareAgentsOpenInGrid', "Attempts open in a grid when they start by default. Turn off sessions.chat.compareAgents.openInGrid to start them without automatic navigation. You can still select the comparison group to open its grid."));
content.push(localize('sessionsChat.compareAgentsRationaleOrder', "Judge results identify candidates as Attempt N with agent, model, and effort, then present Why it won in this order: Comparison, Validation, Code quality, Solution."));
content.push(localize('sessionsChat.compareAgentsInactivePaneNotification', "When a question tool needs input in an inactive visible pane, the confirmation notification setting can show an operating system notification even while another pane in the Agents window is active."));
content.push(localize('sessionsChat.compareAgentsInactivePaneNotification', "When a question tool needs attention in an inactive visible pane, the confirmation notification setting can show an operating system notification even while another pane in the Agents window is active."));
}
content.push(localize('sessionsChat.migrations', "When agent customizations need an update, a notice below the new-session input shows how many need attention. Use Tab to reach Review Migrations and open the Migrations page. Dismiss Migration Notice for This Workspace hides the notice for that workspace, including after restarting, and returns focus to the input."));
if (configurationService.getValue<boolean>(UNIFIED_WORKSPACE_PICKER_SETTING)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -382,9 +382,9 @@ suite('OpenSessionLinkOpenerContribution', () => {
kind: 'chat',
title: 'Peer chat',
detail: 'Session details',
status: { kind: 'warning', label: 'Needs input' },
tooltip: 'Peer chat · Needs input',
ariaLabel: 'Agent chat Peer chat, Needs input',
status: { kind: 'warning', label: 'Needs attention' },
tooltip: 'Peer chat · Needs attention',
ariaLabel: 'Agent chat Peer chat, Needs attention',
},
]);
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -690,7 +690,7 @@ suite('SessionsChatAccessibilityHelp', () => {
instructions: enabledProvider.provideContent().includes('press Enter or Space to expand or collapse the full instructions'),
gridOptOut: enabledProvider.provideContent().includes('Turn off sessions.chat.compareAgents.openInGrid to start them without automatic navigation.'),
deleteGroup: enabledProvider.provideContent().includes('Delete Group remains available from the comparison header context menu'),
inactivePaneNotification: enabledProvider.provideContent().includes('question tool needs input in an inactive visible pane'),
inactivePaneNotification: enabledProvider.provideContent().includes('question tool needs attention in an inactive visible pane'),
rationaleOrder: enabledProvider.provideContent().includes('Comparison, Validation, Code quality, Solution'),
attemptLinks: enabledProvider.provideContent().includes('activate its link to reveal that session'),
accessibleView: enabledProvider.provideContent().includes('use Open Accessible View<keybinding:editor.action.accessibleView>'),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ export function registerBlockedSessionsItemActions(): IDisposable {
MenuRegistry.appendMenuItem(Menus.BlockedSessionsItem, {
command: {
id: IGNORE_INPUT_NEEDED_COMMAND_ID,
title: localize('ignoreInputNeeded', "Ignore Input Needed"),
title: localize('ignoreInputNeeded', "Ignore Needs Attention Alert"),
icon: Codicon.bellSlash,
},
group: 'navigation',
Expand Down Expand Up @@ -119,7 +119,7 @@ export class BlockedSessionsList extends Disposable {
// right (e.g. the action that opens the full sessions picker).
const header = append(element, $('.agent-sessions-blocked-list-header'));
const title = append(header, $('.agent-sessions-blocked-list-title'));
title.textContent = localize('sessionsRequiringInput', "Sessions requiring input");
title.textContent = localize('sessionsRequiringInput', "Sessions needing attention");
const headerActions = append(header, $('.agent-sessions-blocked-list-header-actions'));
this._register(instantiationService.createInstance(MenuWorkbenchToolBar, headerActions, Menus.BlockedSessionsHeader, {
hiddenItemStrategy: HiddenItemStrategy.NoHide,
Expand Down Expand Up @@ -153,7 +153,7 @@ export class BlockedSessionsList extends Disposable {
}
options.onIgnoreSession(session);
status(action.id === IGNORE_INPUT_NEEDED_COMMAND_ID
? localize('inputNeededIgnored', "Input needed ignored until this session needs input again.")
? localize('inputNeededIgnored', "Needs attention alert ignored until this session needs attention again.")
: localize('ciFailureIgnored', "CI failure ignored until this session has another CI failure."));
return true;
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ Registry.as<IConfigurationRegistry>(ConfigurationExtensions.Configuration).regis
[SESSIONS_LIST_SHOW_UNREAD_IN_COLLAPSED_SECTIONS_SETTING]: {
type: 'boolean',
tags: ['preview'],
description: localize('sessions.list.showUnreadInCollapsedSections', "Controls whether collapsed sections in the sessions list show needs-input, CI-failure, or unread indicators for the unarchived sessions they contain."),
description: localize('sessions.list.showUnreadInCollapsedSections', "Controls whether collapsed sections in the sessions list show needs-attention, CI-failure, or unread indicators for the unarchived sessions they contain."),
default: false,
experiment: { mode: 'auto' }
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,7 @@ registerAction2(class ShowSessionsPickerAction extends Action2 {
}
};

appendSessions(localize('sessionsPickerNeedsInput', "needs input"), sessionGroups.needsInput);
appendSessions(localize('sessionsPickerNeedsInput', "needs attention"), sessionGroups.needsInput);
appendSessions(localize('sessionsPickerUnread', "unread"), sessionGroups.unread);
appendSessions(localize('recentlyOpened', "recently opened"), sessionGroups.recent);
appendSessions(localize('otherSessions', "other sessions"), sessionGroups.other);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ export function registerBlockedSessionsHeaderActions(): IDisposable {
MenuRegistry.appendMenuItem(Menus.BlockedSessionsHeader, {
command: {
id: IGNORE_ALL_INPUT_NEEDED_COMMAND_ID,
title: localize('ignoreAllInputNeeded', "Ignore All Input Needed"),
title: localize('ignoreAllInputNeeded', "Ignore All Needs Attention Alerts"),
icon: Codicon.bellSlash,
},
group: 'navigation',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -178,8 +178,8 @@ export class SessionsWindowNotifier extends Disposable implements IWorkbenchCont
switch (status) {
case SessionStatus.NeedsInput:
return workspaceLabel
? localize('sessions.notification.needsInputWithWorkspace', "Input needed in {0}.", workspaceLabel)
: localize('sessions.notification.needsInput', "Input needed.");
? localize('sessions.notification.needsInputWithWorkspace', "Attention needed in {0}.", workspaceLabel)
: localize('sessions.notification.needsInput', "Attention needed.");
case SessionStatus.Completed:
return workspaceLabel
? localize('sessions.notification.completedWithWorkspace', "Completed in {0}.", workspaceLabel)
Expand Down
12 changes: 6 additions & 6 deletions src/vs/sessions/contrib/sessions/browser/views/sessionsList.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1620,7 +1620,7 @@ class SessionItemRenderer implements ITreeRenderer<SessionListItem, FuzzyScore,
template.comparisonAttemptStatusLabel.textContent = '';
break;
case SessionStatus.NeedsInput:
template.comparisonAttemptStatusLabel.textContent = localize('comparisonAttemptNeedsInput', "Input needed");
template.comparisonAttemptStatusLabel.textContent = localize('comparisonAttemptNeedsInput', "Attention needed");
break;
case SessionStatus.Completed:
template.comparisonAttemptStatusLabel.textContent = '';
Expand Down Expand Up @@ -2825,7 +2825,7 @@ class SessionsAccessibilityProvider {
let label = element.label;
switch (this.automationStatus?.read(reader)) {
case SessionStatus.NeedsInput:
label = localize('automationsNeedsInputAria', "{0}, run needs input", element.label);
label = localize('automationsNeedsInputAria', "{0}, run needs attention", element.label);
break;
case SessionStatus.InProgress:
label = localize('automationsActiveAria', "{0}, run in progress", element.label);
Expand Down Expand Up @@ -2927,7 +2927,7 @@ class SessionsAccessibilityProvider {
const status = this.options?.showUnreadInCollapsedSections?.read(reader) ? getSessionHeaderStatus(sessions, reader, this.options.sessionsWithFailingCI?.read(reader)) : undefined;
switch (status) {
case SessionHeaderStatus.NeedsInput:
return localize('sessionSectionNeedsInputAria', "{0}, {1}, session needs input", label, sessions.length);
return localize('sessionSectionNeedsInputAria', "{0}, {1}, session needs attention", label, sessions.length);
case SessionHeaderStatus.FailingCI:
return localize('sessionSectionFailingCIAria', "{0}, {1}, session has failing CI checks", label, sessions.length);
case SessionHeaderStatus.Unread:
Expand Down Expand Up @@ -6097,8 +6097,8 @@ function getComparisonGroupSummary(comparison: ISessionComparison, sessions: rea
const finished = statuses.filter(status => status === SessionStatus.Completed || status === SessionStatus.Error).length;
if (needsInput > 0) {
return needsInput === 1
? localize('comparisonGroup.oneAttemptNeedsInput', "Comparison · 1 attempt needs input")
: localize('comparisonGroup.attemptsNeedInput', "Comparison · {0} attempts need input", needsInput);
? localize('comparisonGroup.oneAttemptNeedsInput', "Comparison · 1 attempt needs attention")
: localize('comparisonGroup.attemptsNeedInput', "Comparison · {0} attempts need attention", needsInput);
}
if (working > 0) {
return working === 1
Expand All @@ -6113,7 +6113,7 @@ function getComparisonGroupSummary(comparison: ISessionComparison, sessions: rea
? sessions.find(session => isEqual(session.resource, judge.sessionResource))?.status.read(reader)
: sessions.find(session => isEqual(session.resource, judge.sessionResource))?.status.get();
if (judgeStatus === SessionStatus.NeedsInput) {
return localize('comparisonGroup.judgeNeedsInput', "Comparison · Judge needs input");
return localize('comparisonGroup.judgeNeedsInput', "Comparison · Judge needs attention");
}
if (judgeStatus === SessionStatus.Untitled || judgeStatus === SessionStatus.InProgress) {
return localize('comparisonGroup.reviewing', "Comparison · Reviewing attempts");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ Registry.as<IConfigurationRegistry>(ConfigurationExtensions.Configuration).regis
properties: {
inputNeeded: {
type: 'boolean',
description: localize('sessions.applicationBadge.inputNeeded', "Count sessions that need input or approval."),
description: localize('sessions.applicationBadge.inputNeeded', "Count sessions that need attention or approval."),
Comment thread
eli-w-king marked this conversation as resolved.
default: SESSIONS_APPLICATION_BADGE_OPTIONS_DEFAULT.inputNeeded,
},
unread: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -235,7 +235,7 @@ export default defineThemedFixtureGroup({ path: 'sessions/' }, {
}, {
labels: { kind: 'screenshot' },
additionalThemes: ['darkHighContrast', 'lightHighContrast'],
expectedVisualDescriptions: ['All sections are collapsed. Orange ring pixel spinners replace the icons for Release work and vscode-docs, taking priority over unread indicators. The vscode section retains its folder icon because its needs-input session appears only in Release work.'],
expectedVisualDescriptions: ['All sections are collapsed. Orange report icons replace the icons for Release work and vscode-docs, taking priority over unread indicators. The vscode section retains its folder icon because its needs-attention session appears only in Release work.'],
}),
SessionsList_CollapsedNeedsInputSections_Disabled: defineSessionsListFixture({
sessions: COLLAPSED_NEEDS_INPUT_SESSIONS,
Expand Down
Loading
Loading