agentHostChangesetFileMonitorCoordinator.ts ×27

Frontier kind: Code frontier

unlabeled · c_c24e3ddb497d

493 tests · 25098 LOC · 107 files · introduces 0 tests · 406 LOC · 3 files

Introduces — evidence that enters the hierarchy at this concept

Code
44 ranges406 lines · 3 files
Tests
0 tests

Contains — complete concept membership

All code (extent)
2079 ranges25098 lines · 107 files · Browse complete extent
All tests (intent)
493 testsBrowse complete intent

Neighbourhood graph

The orange circle is the focus. Violet and green circles are every ancestor and descendant, broader and narrower, at any distance; blue squares and pink diamonds are the introduced files and exact introduced tests of every visible concept, not only the focus's. Arrows point from broader to narrower concepts and bridge only concepts omitted from this view. Undirected links show source or test introduction. Concept and file size follows LOC; exact test nodes use test-count units.

Introduced files, introduced tests, and structurally relevant concept specialization

In the embedded map, ordinary wheel input scrolls the page; use the visible controls to zoom and drag to pan. Open the full-screen map for canvas navigation: wheel pans, Ctrl/Command plus wheel zooms, and arrow keys pan when this region is focused. On touch screens, open the full-screen map to pan or pinch. If JavaScript or WebGL is unavailable, use the native relationship evidence on this page.

Graph controls are ready.

Interactive rendering requires JavaScript and WebGL. Use the native relationship evidence on this page while the interactive map is unavailable.

Native relationship evidence

Every exact file and test below is linked only from the concept that introduces it.

Introduced tests

Every collected test enters the hierarchy at exactly one concept.

No tests are introduced at this concept. Its intent tests are introduced by other concepts.

Introduced code

Every collected source range enters the hierarchy at exactly one concept.

3 files ranked by introduced lines: 406 introduced LOC across 44 ranges. Expand a file to inspect source; the > gutter marks introduced lines.

src/vs/platform/agentHost/node/agentHostChangesetCoordinator.ts 160 introduced LOC · 16 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- agentHostChangesetCoordinator.ts
2 > * Copyright (c) Microsoft Corporation. All rights reserved.
3 > * Licensed under the MIT License. See License.txt in the project root for license information.
4 > *--------------------------------------------------------------------------------------------*/
5 >
6 > import { Disposable } from '../../../base/common/lifecycle.js';
7 > import { URI } from '../../../base/common/uri.js';
8 > import { IAgentSessionMetadata } from '../common/agentService.js';
9 > import { buildBranchChangesetUri, ChangesetKind, parseChangesetUri } from '../common/changesetUri.js';
10 > import { ChangesetFileMonitorCoordinator } from './agentHostChangesetFileMonitorCoordinator.js';
11 > import { AgentHostStateManager, IAgentHostStateManager } from './agentHostStateManager.js';
12 > import { IAgentHostChangesetService, META_CHANGESET_BRANCH, META_CHANGESET_SESSION, META_LEGACY_DIFFS } from '../common/agentHostChangesetService.js';
13 > import { IAgentHostChangesetSubscriptionService } from '../common/agentHostChangesetSubscriptionService.js';
14 > import { IAgentHostChangesetOperationService } from '../common/agentHostChangesetOperationService.js';
15 > import { IAgentHostGitStateService } from '../common/agentHostGitStateService.js';
16 > import { IInstantiationService } from '../../instantiation/common/instantiation.js';
17 > import { isAhpChatChannel } from '../common/state/sessionState.js';
18 >
19 > /**
20 > * Raw metadata blob values for the session DB, batch-read by the caller.
21 > * Keys are the changeset-specific metadata keys ({@link META_CHANGESET_BRANCH}
22 > * etc.); values are the raw `string | undefined` payloads as returned by
23 > * `ISessionDatabase.getMetadataObject`.
24 > */
25 > export type IChangesetSessionMetadata = Record<string, string | undefined>;
26 >
27 > /**
28 > * Coordinator that encapsulates all `AgentService`-side orchestration of
29 > * the changeset feature. Sits between `AgentService` (which owns session
30 > * lifecycle / subscription refcounting / batched DB reads) and
31 > * {@link IAgentHostChangesetService} (which owns compute / publish /
32 > * persist primitives).
33 > *
34 > * Owns only URI routing and forwards lifecycle signals. Subscription state is
35 > * recorded in the shared changeset subscription service. All computation,
36 > * working-directory gating, and the deferred-refresh state machine live in
37 > * {@link IAgentHostChangesetService}.
38 > *
39 > * No per-session controllers — the cross-cutting concerns (listSessions
40 > * overlay, subscribe URI routing) inherently span sessions, so a single
41 > * coordinator with internal maps is simpler than per-session RAII.
42 > */
43 > export class AgentHostChangesetCoordinator extends Disposable {
44 > private readonly _changesetFileMonitor: ChangesetFileMonitorCoordinator;
45 >
46 > constructor(
47 @IAgentHostStateManager private readonly _stateManager: AgentHostStateManager,
48 @IAgentHostChangesetOperationService private readonly _changesetOperationService: IAgentHostChangesetOperationService,
57 this._register(gitStateService.onDidRefreshSessionGitState(sessionStr => this.onDidRunSessionGitStateRefresh(sessionStr)));
58 }
60 > // ---- Lifecycle hooks ----------------------------------------------------
61 >
62 > /**
63 > * Seeds the create-time catalogue and registers its backing changeset state
64 > * before `SessionReady` is dispatched.
65 > */
66 > onSessionCreated(sessionStr: string): void {
67 this._changesets.refreshChangesetCatalog(sessionStr);
68 this._changesets.registerStaticChangesets(sessionStr);
69 }
71 > /**
72 > * Called at session restore time. Registers the static changeset URIs
73 > * and reseeds them from any persisted blobs already read from the DB.
74 > * `metadata` must come from the same batched `getMetadataObject` call
75 > * `AgentService` already issues for title / read / archive / config
76 > * keys.
77 > */
78 > onSessionRestored(sessionStr: string, metadata: IChangesetSessionMetadata): void {
79 this._changesets.refreshChangesetCatalog(sessionStr);
80 this._changesets.registerStaticChangesets(sessionStr);
90 this._changesetFileMonitor.onSessionRestored(sessionStr);
91 }
93 > /**
94 > * Called when a provisional session is materialized (working directory
95 > * becomes known). Drains any static changeset refresh that was deferred
96 > * because the working directory was not yet known.
97 > */
98 > onSessionMaterialized(sessionStr: string): void {
99 this._changesets.refreshChangesetCatalog(sessionStr);
100 this._changesets.onWorkingDirectoryAvailable(sessionStr);
102 this._changesetFileMonitor.onSessionMaterialized(sessionStr);
103 }
105 > /**
106 > * Called when a session is disposed. Forgets any pending refresh
107 > * queued for that session.
108 > */
109 > onSessionDisposed(sessionStr: string): void {
110 this._changesets.onSessionDisposed(sessionStr);
111 this._changesetFileMonitor.onSessionDisposed(sessionStr);
113 this._changesetSubscriptions.clearSessionSubscriptions(sessionStr);
114 }
116 > onSessionTurnActiveChanged(sessionStr: string, active: boolean): void {
117 this._changesetFileMonitor.onSessionTurnActiveChanged(sessionStr, active);
118
122 this._changesetOperationService.updateOperations(sessionStr);
123 }
125 > // ---- Subscription hooks -------------------------------------------------
126 >
127 > /**
128 > * Called on every `addSubscriber` 0→1 transition. When `resource` is a
129 > * static changeset URI, triggers the first git-diff refresh (the
130 > * changeset service self-defers it when the working directory is not yet
131 > * known).
132 > *
133 > * Both {@link AgentService.subscribe} and the handshake fast-path
134 > * (`ProtocolServerHandler.initialSubscriptions`) call into
135 > * `addSubscriber`, so this single hook covers both paths.
136 > */
137 > onFirstSubscriber(resource: URI): void {
138 const resourceStr = resource.toString();
139 const parsed = parseChangesetUri(resourceStr);
182 }
183 }
185 > /**
186 > * Called when a resource's last subscriber drops. Removes the
187 > * changeset from the session's subscription set so a later
188 > * materialization / git-state recompute (driven by
189 > * {@link IAgentHostChangesetService.recomputeSubscribedChangesets})
190 > * naturally skips it — no explicit cancellation needed.
191 > */
192 > onLastSubscriber(resource: URI): void {
193 const resourceStr = resource.toString();
194 const parsed = parseChangesetUri(resourceStr);
217 }
218 }
220 > /**
221 > * Restores the parent session when `resource` is a changeset URI and the
222 > * parent session is not already live. Non-changeset URIs are ignored.
223 > *
224 > * This is intentionally narrower than {@link tryHandleSubscribe}: it does
225 > * not compute per-turn / compare changesets and does not register static
226 > * changesets. It exists for the AgentService subscribe path where
227 > * `addSubscriber` may have already created a placeholder changeset snapshot
228 > * before the parent session restore had a chance to apply persisted diffs.
229 > */
230 > async restoreSessionIfChangesetSubscription(resource: URI, restoreSession: (session: URI) => Promise<void>): Promise<void> {
231 const resourceStr = resource.toString();
232 const parsed = parseChangesetUri(resourceStr);
241 }
242 }
244 > /**
245 > * If `resource` is a known changeset URI (uncommitted / session /
246 > * turn), seeds its state on the state manager and returns `true`.
247 > * Returns `false` for non-changeset URIs so callers fall through to
248 > * their default routing (session / subagent / terminal).
249 > *
250 > * The parent session is restored via the provided `restoreSession`
251 > * callback when no live state exists yet — this matches the previous
252 > * inline behaviour in `AgentService.subscribe`.
253 > *
254 > * Throws when the URI matches the changeset shape but the id is not
255 > * a well-known kind ({@link ChangesetKind.Unknown}). The unknown-id
256 > * rejection MUST fire before any parent-session restore so subscribing
257 > * to a bogus child URI cannot materialize the parent as a side effect.
258 > */
259 > async tryHandleSubscribe(resource: URI, restoreSession: (session: URI) => Promise<void>): Promise<boolean> {
260 const resourceStr = resource.toString();
261 const parsed = parseChangesetUri(resourceStr);
285 return true;
286 }
288 > private _addSubscription(sessionStr: string, changesetStr: string) {
289 this._changesetSubscriptions.addSubscription(sessionStr, changesetStr);
290 }
292 > private _removeSubscription(sessionStr: string, changesetStr: string) {
293 this._changesetSubscriptions.removeSubscription(sessionStr, changesetStr);
294 }
296 > // ---- listSessions overlay ----------------------------------------------
297 >
298 > /**
299 > * Returns the session-DB metadata keys to merge into a batched read
300 > * for `sessionStr`, OR `undefined` when live state already answers
301 > * the aggregate-counts question. Delegates to the changeset service,
302 > * which owns the live-vs-persisted decision.
303 > */
304 > getListMetadataKeys(sessionStr: string): Record<string, true> | undefined {
305 return this._changesets.getListMetadataKeys(sessionStr);
306 }
308 > /**
309 > * Decorates a single listSessions entry with the `changes` aggregate
310 > * (additions / deletions / files for the session-wide changeset). The
311 > * aggregate computation lives in the changeset service; the coordinator
312 > * only projects the result onto the entry.
313 > */
314 > decorateListEntry(entry: IAgentSessionMetadata, metadata: IChangesetSessionMetadata): IAgentSessionMetadata {
315 const changes = this._changesets.computeListEntryChanges(entry.session.toString(), metadata);
316 return changes ? { ...entry, changes } : entry;
317 }
319 > // ---- Git state events -------------------------------------------------
320 >
321 > /**
322 > * Called when a session's Git state is refreshed.
323 > */
324 > private onDidRunSessionGitStateRefresh(sessionStr: string): void {
325 // Refresh the list of changesets for the session.
326 this._changesets.refreshChangesetCatalog(sessionStr);
src/vs/platform/agentHost/common/agentHostChangesetOperationService.ts 130 introduced LOC · 1 range

Open complete file

1 > /*--------------------------------------------------------------------------------------------- agentHostChangesetOperationService.ts
2 > * Copyright (c) Microsoft Corporation. All rights reserved.
3 > * Licensed under the MIT License. See License.txt in the project root for license information.
4 > *--------------------------------------------------------------------------------------------*/
5 >
6 > import { CancellationToken } from '../../../base/common/cancellation.js';
7 > import type { IDisposable } from '../../../base/common/lifecycle.js';
8 > import { createDecorator } from '../../instantiation/common/instantiation.js';
9 > import type { ChangesetKind } from './changesetUri.js';
10 > import type { InvokeChangesetOperationParams, InvokeChangesetOperationResult } from './state/protocol/channels-changeset/commands.js';
11 > import type { ChangesetOperation, ISessionGitHubState, ISessionGitState, URI } from './state/sessionState.js';
12 >
13 > export const IAgentHostChangesetOperationService = createDecorator<IAgentHostChangesetOperationService>('agentHostChangesetOperationService');
14 >
15 > /**
16 > * Server-side handler for a changeset operation advertised via
17 > * `changeset/operationsChanged`.
18 > *
19 > * The agent service validates the request shape (changeset exists, operation id
20 > * known, target scope matches) before invoking the handler; the handler is only
21 > * responsible for executing the operation.
22 > */
23 > export interface IChangesetOperationHandler {
24 > /**
25 > * Executes a previously advertised changeset operation.
26 > *
27 > * The handler receives the original protocol params so it can inspect the
28 > * changeset channel and optional target. Validation that the operation exists
29 > * on the changeset and supports the requested target scope happens before this
30 > * method is called.
31 > */
32 > invoke(params: InvokeChangesetOperationParams, token: CancellationToken): Promise<InvokeChangesetOperationResult>;
33 > }
34 >
35 > /**
36 > * Context used by changeset operation contributions to decide which operations
37 > * to advertise for a session changeset.
38 > *
39 > * Keep this interface intentionally small. Add new fields here only when a
40 > * contribution genuinely needs them to compute operation availability. Likely
41 > * future additions include the concrete changeset URI, the session state, the
42 > * changeset state, or the working directory URI.
43 > */
44 > export interface IChangesetOperationContext {
45 > /** String form of the session URI that owns the changeset. */
46 > readonly sessionKey: string;
47 > /** Expanded changeset URI whose operations are being computed. */
48 > readonly changesetUri: URI;
49 > /** Well-known changeset kind for {@link changesetUri}. */
50 > readonly changesetKind: ChangesetKind;
51 > /** Current git metadata for the session used to compute operation availability. */
52 > readonly gitState?: ISessionGitState;
53 > /** Current GitHub metadata for the session used to compute operation availability. */
54 > readonly gitHubState?: ISessionGitHubState;
55 > }
56 >
57 > /**
58 > * Registration surface handed to changeset operation contributions.
59 > *
60 > * Contributions use this object to install operation handlers and request a
61 > * refresh when external state changes which operations should be advertised.
62 > */
63 > export interface IChangesetOperationRegistry {
64 > /**
65 > * Registers the server-side handler for one {@link ChangesetOperation.id}.
66 > * The returned disposable removes only this registration.
67 > */
68 > registerChangesetOperationHandler(operationId: string, handler: IChangesetOperationHandler): IDisposable;
69 > /**
70 > * Notifies the contribution service that advertised operations for all static
71 > * changesets in `sessionKey` should be recomputed from current session state.
72 > */
73 > onDidChangeOperations(sessionKey: string): void;
74 > /**
75 > * Recomputes the session's git metadata and then refreshes advertised
76 > * operations if that metadata can be resolved.
77 > */
78 > refreshSessionGitState(sessionKey: string): Promise<void>;
79 > }
80 >
81 > /**
82 > * Provider of changeset operations for one feature area.
83 > *
84 > * A contribution owns the decision about which operations are available for a
85 > * changeset and registers the handlers that execute those operations.
86 > */
87 > export interface IChangesetOperationContribution extends IDisposable {
88 > /**
89 > * Registers every operation handler owned by this contribution. Called once
90 > * when the contribution is added to the service.
91 > */
92 > registerHandlers(registry: IChangesetOperationRegistry): IDisposable;
93 > /**
94 > * Returns operations that should be advertised for the given changeset, or
95 > * `undefined` when this contribution has nothing to offer in the context.
96 > */
97 > getOperations(context: IChangesetOperationContext): readonly ChangesetOperation[] | undefined;
98 > }
99 >
100 > /**
101 > * Coordinates changeset operation contributions, advertised operation state,
102 > * and client-triggered invocation.
103 > */
104 > export interface IAgentHostChangesetOperationService extends IDisposable {
105 > readonly _serviceBrand: undefined;
106 >
107 > /**
108 > * Adds a contribution and registers its handlers. Disposing the returned value
109 > * unregisters the handlers and disposes the contribution.
110 > */
111 > registerContribution(contribution: IChangesetOperationContribution): IDisposable;
112 > /**
113 > * Recomputes and publishes operations for the changesets for a given
114 > * session. If `gitState` is not provided, the current git state will
115 > * be used.
116 > */
117 > updateOperations(sessionKey: string, changeset?: string, gitState?: ISessionGitState, gitHubState?: ISessionGitHubState): void;
118 >
119 > /**
120 > * Returns the operations that should be advertised for the given changeset, or
121 > * `undefined` when no operations are available.
122 > */
123 > getOperations(sessionKey: string, changeset?: string, gitState?: ISessionGitState, gitHubState?: ISessionGitHubState): readonly ChangesetOperation[] | undefined;
124 >
125 > /**
126 > * Invokes an advertised operation after validating the changeset, operation id,
127 > * and requested target scope.
128 > */
129 > invokeChangesetOperation(params: InvokeChangesetOperationParams): Promise<InvokeChangesetOperationResult>;
130 > }
src/vs/platform/agentHost/node/agentHostChangesetFileMonitorCoordinator.ts 116 introduced LOC · 27 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- agentHostChangesetFileMonitorCoordinator.ts
2 > * Copyright (c) Microsoft Corporation. All rights reserved.
3 > * Licensed under the MIT License. See License.txt in the project root for license information.
4 > *--------------------------------------------------------------------------------------------*/
5 >
6 > import { SequencerByKey } from '../../../base/common/async.js';
7 > import { Disposable, DisposableMap, IReference, ReferenceCollection } from '../../../base/common/lifecycle.js';
8 > import { URI } from '../../../base/common/uri.js';
9 > import { buildBranchChangesetUri, buildSessionChangesetUri, buildUncommittedChangesetUri } from '../common/changesetUri.js';
10 > import { parseSubagentSessionUri } from '../common/state/sessionState.js';
11 > import { IAgentConfigurationService } from './agentConfigurationService.js';
12 > import { DEFAULT_AGENT_HOST_WATCH_EXCLUDES, IAgentHostFileMonitorService } from './agentHostFileMonitorService.js';
13 > import { IAgentHostGitService } from '../common/agentHostGitService.js';
14 > import { AgentHostStateManager, IAgentHostStateManager } from './agentHostStateManager.js';
15 > import { ILogService } from '../../log/common/log.js';
16 > import { IAgentHostGitStateService } from '../common/agentHostGitStateService.js';
17 >
18 > class WatchInterestReferenceCollection extends ReferenceCollection<string> {
19 > constructor(
20 private readonly _create: (sessionStr: string) => void,
21 private readonly _destroy: (sessionStr: string) => void,
23 super();
24 }
26 > protected createReferencedObject(sessionStr: string): string {
27 this._create(sessionStr);
28 return sessionStr;
29 }
31 > protected destroyReferencedObject(sessionStr: string): void {
32 this._destroy(sessionStr);
33 }
35 >
36 > /**
37 > * Keeps static changeset catalogue entries fresh while a client is observing a
38 > * session or one of its static changeset resources.
39 > *
40 > * The generic {@link IAgentHostFileMonitorService} owns folder watching and
41 > * debounce mechanics; this coordinator owns the changeset-specific lifecycle:
42 > * subscription interest, session materialization, repository-root resolution,
43 > * root-level watcher sharing, and refresh fanout.
44 > *
45 > * We only monitor roots while at least one client is subscribed to a session or
46 > * static changeset that needs fresh changeset counts. We do not monitor while a
47 > * session on that root is actively running a turn: agent/tool edits made during
48 > * the turn are captured by the turn lifecycle, and the static changesets are
49 > * recomputed once when the turn completes. Watching during the turn would add
50 > * duplicate file-system noise without improving correctness.
51 > */
52 > export class ChangesetFileMonitorCoordinator extends Disposable {
53 >
54 > /** Per-subscription references into the per-session watch-interest collection. */
55 > private readonly _watchInterestReferences = this._register(new DisposableMap<string, IReference<string>>());
56 > private readonly _watchInterestCollection = new WatchInterestReferenceCollection(
57 > sessionStr => this._attachWatcherIfPossible(sessionStr),
58 > sessionStr => this._destroyWatchInterest(sessionStr),
59 > );
60 > /** Sessions waiting for materialization before a root watcher can attach. */
61 > private readonly _pendingWatchInterest = new Set<string>();
62 > /** Session URI string to the working directory that produced the current root attachment. */
63 > private readonly _sessionWorkingDirectory = new Map<string, string>();
64 > /** Session URI string to repository-root URI string. */
65 > private readonly _sessionRoot = new Map<string, string>();
66 > /** Repository-root URI string to sessions currently fanned out from that root. */
67 > private readonly _rootSessions = new Map<string, Set<string>>();
68 > /** Repository-root URI string to the shared monitor acquisition. */
69 > private readonly _rootWatchAcquisitions = this._register(new DisposableMap<string>());
70 > /** Repository-root URI string to the canonical repository root URI. */
71 > private readonly _rootUris = new Map<string, URI>();
72 > /** Active session URI string to repository-root URI string. */
73 > private readonly _activeSessionRoots = new Map<string, string>();
74 > /** Repository-root URI string to sessions currently active against that root. */
75 > private readonly _rootActiveSessions = new Map<string, Set<string>>();
76 > /** Active sessions whose repository root cannot yet be resolved. */
77 > private readonly _unresolvedActiveSessions = new Set<string>();
78 > private readonly _watchAttachmentSequencer = new SequencerByKey<string>();
79 > private readonly _activeTurnSequencer = new SequencerByKey<string>();
80 >
81 > constructor(
82 @IAgentHostStateManager private readonly _stateManager: AgentHostStateManager,
83 @IAgentConfigurationService private readonly _configurationService: IAgentConfigurationService,
89 super();
90 }
92 > trackSessionChanges(subscriptionKey: string, sessionStr: string): void {
93 if (!this._watchInterestReferences.has(subscriptionKey)) {
94 this._watchInterestReferences.set(subscriptionKey, this._watchInterestCollection.acquire(sessionStr));
95 }
96 }
98 > untrackSessionChanges(subscriptionKey: string): void {
99 this._watchInterestReferences.deleteAndDispose(subscriptionKey);
100 }
102 > onSessionRestored(sessionStr: string): void {
103 this._retryWatchAttachment(sessionStr);
104 }
106 > onSessionMaterialized(sessionStr: string): void {
107 this._retryWatchAttachment(sessionStr);
108 }
110 > onSessionDisposed(sessionStr: string): void {
111 this.untrackSessionChanges(buildUncommittedChangesetUri(sessionStr));
112 this.untrackSessionChanges(buildSessionChangesetUri(sessionStr));
115 this._destroyWatchInterest(sessionStr);
116 }
118 > onSessionTurnActiveChanged(sessionStr: string, active: boolean): void {
119 this._activeTurnSequencer.queue(sessionStr, async () => {
120 if (active) {
125 });
126 }
128 > private _destroyWatchInterest(sessionStr: string): void {
129 this._pendingWatchInterest.delete(sessionStr);
130 this._releaseSessionRoot(sessionStr);
131 }
133 > private _retryWatchAttachment(sessionStr: string): void {
134 if (this._shouldAttachSession(sessionStr) || this._pendingWatchInterest.has(sessionStr)) {
135 this._attachWatcherIfPossible(sessionStr);
136 }
137 }
139 > private _hasWatchInterest(sessionStr: string): boolean {
140 return this._watchInterestReferences.has(sessionStr)
141 || this._watchInterestReferences.has(buildBranchChangesetUri(sessionStr))
143 || this._watchInterestReferences.has(buildSessionChangesetUri(sessionStr));
144 }
146 > private _attachWatcherIfPossible(sessionStr: string): void {
147 this._watchAttachmentSequencer.queue(sessionStr, async () => {
148 if (!this._shouldAttachSession(sessionStr)) {
181 });
182 }
184 > private _attachSessionToRoot(sessionStr: string, repositoryRoot: URI, workingDirectory: string): void {
185 const rootStr = repositoryRoot.toString();
186 if (this._sessionRoot.get(sessionStr) === rootStr) {
201 this._ensureRootWatcher(rootStr, repositoryRoot);
202 }
204 > private _releaseSessionRoot(sessionStr: string): void {
205 const rootStr = this._sessionRoot.get(sessionStr);
206 if (!rootStr) {
250 }
251 }
253 > private _shouldAttachSession(sessionStr: string): boolean {
254 return this._hasWatchInterest(sessionStr)
255 && !this._activeSessionRoots.has(sessionStr)
256 && !this._unresolvedActiveSessions.has(sessionStr);
257 }
259 > private _isRootActive(rootStr: string): boolean {
260 return (this._rootActiveSessions.get(rootStr)?.size ?? 0) > 0;
261 }
263 > private _ensureRootWatcher(rootStr: string, repositoryRoot: URI): void {
264 if (this._isRootActive(rootStr) || this._rootWatchAcquisitions.has(rootStr)) {
265 return;
281 this._rootWatchAcquisitions.set(rootStr, rootWatchAcquisition);
282 }
284 > private _suspendRootWatcher(rootStr: string): void {
285 this._rootWatchAcquisitions.deleteAndDispose(rootStr);
286 }
288 > private async _markSessionActive(sessionStr: string): Promise<void> {
289 this._removeActiveSession(sessionStr);
290 this._pendingWatchInterest.delete(sessionStr);
322 }
323 }
325 > private _removeActiveSession(sessionStr: string): string | undefined {
326 this._unresolvedActiveSessions.delete(sessionStr);
327 const rootStr = this._activeSessionRoots.get(sessionStr);
339 return rootStr;
340 }
342 > private async _resolveActivityRepositoryRoot(sessionStr: string): Promise<URI | undefined> {
343 const workingDirectory = this._getActivityWorkingDirectory(sessionStr);
344 if (!workingDirectory) {
354 return this._gitService.getRepositoryRoot(workingDirectoryUri);
355 }
357 > private _getActivityWorkingDirectory(sessionStr: string): string | undefined {
358 const workingDirectory = this._configurationService.getEffectiveWorkingDirectory(sessionStr);
359 if (workingDirectory) {