diff --git a/src/vs/workbench/contrib/chat/browser/agentSessions/agentHost/agentHostResponseFileChanges.ts b/src/vs/workbench/contrib/chat/browser/agentSessions/agentHost/agentHostResponseFileChanges.ts index 1da734f7c3f..7e9e3cf30b4 100644 --- a/src/vs/workbench/contrib/chat/browser/agentSessions/agentHost/agentHostResponseFileChanges.ts +++ b/src/vs/workbench/contrib/chat/browser/agentSessions/agentHost/agentHostResponseFileChanges.ts @@ -4,7 +4,8 @@ *--------------------------------------------------------------------------------------------*/ import { Disposable } from '../../../../../../base/common/lifecycle.js'; -import { constObservable, derived, IObservable, observableFromEvent } from '../../../../../../base/common/observable.js'; +import { constObservable, derived, derivedOpts, IObservable, observableFromEvent } from '../../../../../../base/common/observable.js'; +import { isEqual } from '../../../../../../base/common/resources.js'; import { isDefined } from '../../../../../../base/common/types.js'; import { URI } from '../../../../../../base/common/uri.js'; import { IAgentConnection } from '../../../../../../platform/agentHost/common/agentService.js'; @@ -64,7 +65,7 @@ export class AgentHostResponseFileChangesProvider extends Disposable implements // the summary stays empty (and self-hidden) for them. const sessionStateObs = this._subscribe(StateComponents.Session, constObservable(backendSession)); - const turnChangesetUriObs = derived(reader => { + const turnChangesetUriObs = derivedOpts({ equalsFn: isEqual }, reader => { const sessionState = sessionStateObs.read(reader).read(reader); if (!sessionState || sessionState instanceof Error) { return undefined; diff --git a/src/vs/workbench/contrib/chat/test/browser/agentHost/agentHostResponseFileChanges.test.ts b/src/vs/workbench/contrib/chat/test/browser/agentHost/agentHostResponseFileChanges.test.ts index 0777f22c9fb..7929a699f07 100644 --- a/src/vs/workbench/contrib/chat/test/browser/agentHost/agentHostResponseFileChanges.test.ts +++ b/src/vs/workbench/contrib/chat/test/browser/agentHost/agentHostResponseFileChanges.test.ts @@ -22,14 +22,20 @@ class FakeAgentConnection extends mock() { private readonly _emitters = new Map>(); private readonly _values = new Map(); + private readonly _subscriptionCounts = new Map(); setState(resource: string, value: unknown): void { this._values.set(resource, value); this._emitters.get(resource)?.fire(value); } + getSubscriptionCount(resource: string): number { + return this._subscriptionCounts.get(resource) ?? 0; + } + override getSubscription(_kind: T, resource: URI, _owner: string): IReference> { const key = resource.toString(); + this._subscriptionCounts.set(key, (this._subscriptionCounts.get(key) ?? 0) + 1); let emitter = this._emitters.get(key); if (!emitter) { emitter = new Emitter(); @@ -93,6 +99,24 @@ suite('AgentHostResponseFileChangesProvider', () => { ]); }); + test('keeps the changeset subscription when session state updates', () => { + const ds = store.add(new DisposableStore()); + const conn = new FakeAgentConnection(); + const provider = ds.add(new AgentHostResponseFileChangesProvider(conn, authority, () => backendSession)); + + conn.setState(backendSession.toString(), sessionStateWithTurnSupport()); + conn.setState(turnChangesetUri('t1'), { status: ChangesetStatus.Ready, files: [] } satisfies ChangesetState); + observe(provider, ds); + const subscriptionCountBeforeUpdate = conn.getSubscriptionCount(turnChangesetUri('t1')); + + conn.setState(backendSession.toString(), sessionStateWithTurnSupport()); + + assert.deepStrictEqual([ + subscriptionCountBeforeUpdate, + conn.getSubscriptionCount(turnChangesetUri('t1')), + ], [1, 1]); + }); + test('returns empty when the agent does not advertise a turn changeset', () => { const ds = store.add(new DisposableStore()); const conn = new FakeAgentConnection();