Merge pull request #305633 from microsoft/benibenj/brief-hawk

Add approval timestamp to session approval info
This commit is contained in:
Benjamin Christopher Simmonds
2026-03-27 14:03:39 +01:00
committed by GitHub
3 changed files with 31 additions and 5 deletions

View File

@@ -14,7 +14,7 @@ import { Emitter, Event } from '../../../../../base/common/event.js';
import { FuzzyScore } from '../../../../../base/common/filters.js';
import { Disposable, DisposableStore, MutableDisposable, toDisposable } from '../../../../../base/common/lifecycle.js';
import { MarkdownString } from '../../../../../base/common/htmlContent.js';
import { autorun } from '../../../../../base/common/observable.js';
import { IReader, autorun } from '../../../../../base/common/observable.js';
import { ThemeIcon } from '../../../../../base/common/themables.js';
import { URI } from '../../../../../base/common/uri.js';
import { fromNow } from '../../../../../base/common/date.js';
@@ -32,7 +32,7 @@ import { asCssVariable } from '../../../../../platform/theme/common/colorUtils.j
import { IStorageService, StorageScope, StorageTarget } from '../../../../../platform/storage/common/storage.js';
import { GITHUB_REMOTE_FILE_SCHEME, ISessionData, ISessionWorkspace, SessionStatus } from '../../common/sessionData.js';
import { ISessionsManagementService } from '../sessionsManagementService.js';
import { AgentSessionApprovalModel } from '../../../../../workbench/contrib/chat/browser/agentSessions/agentSessionApprovalModel.js';
import { AgentSessionApprovalModel, IAgentSessionApprovalInfo } from '../../../../../workbench/contrib/chat/browser/agentSessions/agentSessionApprovalModel.js';
import { Button } from '../../../../../base/browser/ui/button/button.js';
import { IMarkdownRendererService } from '../../../../../platform/markdown/browser/markdownRenderer.js';
import { Separator } from '../../../../../base/common/actions.js';
@@ -103,7 +103,7 @@ class SessionsTreeDelegate implements IListVirtualDelegate<SessionListItem> {
let height = SessionsTreeDelegate.ITEM_HEIGHT;
if (this._approvalModel) {
const approval = this._approvalModel.getApproval(element.resource).get();
const approval = getFirstApprovalAcrossChats(this._approvalModel, element as ISessionData, undefined);
if (approval) {
height += SessionItemRenderer.getApprovalRowHeight(approval.label);
}
@@ -369,7 +369,7 @@ class SessionItemRenderer implements ITreeRenderer<SessionListItem, FuzzyScore,
}
const approvalModel = this.approvalModel;
const initialInfo = approvalModel.getApproval(element.resource).get();
const initialInfo = getFirstApprovalAcrossChats(approvalModel, element, undefined);
let wasVisible = !!initialInfo;
template.approvalRow.classList.toggle('visible', wasVisible);
@@ -378,7 +378,7 @@ class SessionItemRenderer implements ITreeRenderer<SessionListItem, FuzzyScore,
template.elementDisposables.add(autorun(reader => {
buttonStore.clear();
const info = approvalModel.getApproval(element.resource).read(reader);
const info = getFirstApprovalAcrossChats(approvalModel, element, reader);
const visible = !!info;
template.approvalRow.classList.toggle('visible', visible);
@@ -1175,6 +1175,21 @@ export class SessionsList extends Disposable implements ISessionsList {
//#endregion
//#region Approval Helpers
function getFirstApprovalAcrossChats(approvalModel: AgentSessionApprovalModel, session: ISessionData, reader: IReader | undefined,): IAgentSessionApprovalInfo | undefined {
let oldest: IAgentSessionApprovalInfo | undefined;
for (const chat of session.chats.read(reader)) {
const approval = approvalModel.getApproval(chat.resource).read(reader);
if (approval && (!oldest || approval.since.getTime() < oldest.since.getTime())) {
oldest = approval;
}
}
return oldest;
}
//#endregion
//#region Sorting & Grouping Helpers
export function sortSessions(sessions: ISessionData[], sorting: SessionsSorting): ISessionData[] {

View File

@@ -15,6 +15,7 @@ import { ILanguageService } from '../../../../../editor/common/languages/languag
export interface IAgentSessionApprovalInfo {
readonly label: string;
readonly languageId: string | undefined;
readonly since: Date;
confirm(): void;
}
@@ -112,6 +113,7 @@ export class AgentSessionApprovalModel extends Disposable {
setIfChanged({
label,
languageId,
since: new Date(),
confirm: () => confirmState.confirm({ type: ToolConfirmKind.UserAction }),
});
return;

View File

@@ -513,6 +513,7 @@ export default defineThemedFixtureGroup({
const approvalModel = createMockApprovalModel(resource, {
label: '{ "action": "deleteFile", "path": "/src/old-module.ts" }',
languageId: 'json',
since: new Date(),
confirm: () => { },
});
renderSessionItem(ctx, createMockSession({
@@ -535,6 +536,7 @@ export default defineThemedFixtureGroup({
const approvalModel = createMockApprovalModel(resource, {
label: 'npm install --save express@latest',
languageId: 'sh',
since: new Date(),
confirm: () => { },
});
renderSessionItem(ctx, createMockSession({
@@ -557,6 +559,7 @@ export default defineThemedFixtureGroup({
const approvalModel = createMockApprovalModel(resource, {
label: 'Start-Job -ScriptBlock { Set-Location \'c:\\some\\path\'; npm install } | Out-Null',
languageId: 'pwsh',
since: new Date(),
confirm: () => { },
});
renderSessionItem(ctx, createMockSession({
@@ -579,6 +582,7 @@ export default defineThemedFixtureGroup({
const approvalModel = createMockApprovalModel(resource, {
label: 'rm -rf node_modules && npm cache clean --force && npm install --legacy-peer-deps --ignore-scripts',
languageId: 'sh',
since: new Date(),
confirm: () => { },
});
renderSessionItem(ctx, createMockSession({
@@ -603,6 +607,7 @@ export default defineThemedFixtureGroup({
const approvalModel = createMockApprovalModel(resource, {
label: 'npm install --save express@latest',
languageId: 'sh',
since: new Date(),
confirm: () => { },
});
renderSessionItem(ctx, createMockSession({
@@ -625,6 +630,7 @@ export default defineThemedFixtureGroup({
const approvalModel = createMockApprovalModel(resource, {
label: 'cd /workspace/project\nnpm install',
languageId: 'sh',
since: new Date(),
confirm: () => { },
});
renderSessionItem(ctx, createMockSession({
@@ -647,6 +653,7 @@ export default defineThemedFixtureGroup({
const approvalModel = createMockApprovalModel(resource, {
label: 'cd /workspace/project\nnpm install\nnpm run build',
languageId: 'sh',
since: new Date(),
confirm: () => { },
});
renderSessionItem(ctx, createMockSession({
@@ -669,6 +676,7 @@ export default defineThemedFixtureGroup({
const approvalModel = createMockApprovalModel(resource, {
label: 'cd /workspace/project\nnpm install\nnpm run build\nnpm run test -- --coverage',
languageId: 'sh',
since: new Date(),
confirm: () => { },
});
renderSessionItem(ctx, createMockSession({
@@ -691,6 +699,7 @@ export default defineThemedFixtureGroup({
const approvalModel = createMockApprovalModel(resource, {
label: 'RUSTFLAGS="-C target-cpu=native -C opt-level=3" cargo build --release --target x86_64-unknown-linux-gnu\nfind ./target/release -name "*.so" -exec strip --strip-unneeded {} \\; && tar czf release-bundle.tar.gz -C target/release .\ncurl -X POST https://deploy.internal.example.com/api/v2/artifacts/upload --header "Authorization: Bearer $DEPLOY_TOKEN" --form "bundle=@release-bundle.tar.gz"',
languageId: 'sh',
since: new Date(),
confirm: () => { },
});
renderSessionItem(ctx, createMockSession({