agentService.ts ×16

Frontier kind: Code frontier

unlabeled · c_e76928275e71

86 tests · 51273 LOC · 304 files · introduces 0 tests · 109 LOC · 4 files

Introduces — evidence that enters the hierarchy at this concept

Code
19 ranges109 lines · 4 files
Tests
0 tests

Contains — complete concept membership

All code (extent)
5295 ranges51273 lines · 304 files · Browse complete extent
All tests (intent)
86 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.

4 files ranked by introduced lines: 109 introduced LOC across 19 ranges. Expand a file to inspect source; the > gutter marks introduced lines.

src/vs/platform/agentHost/node/agentService.ts 103 introduced LOC · 16 ranges

Open complete file

962 throw new Error(`No agent provider registered for: ${providerId ?? '(none)'}`);
963 }
965 > // When forking, build the old→new turn ID mapping before creating the
966 > // session so the agent can use it to remap per-turn data. If the
967 > // source has no turns to copy (e.g. a still-provisional session), a
968 > // "fork" is indistinguishable from a fresh session, so we drop the
969 > // fork parameter and fall through to the regular create path.
970 if (config?.fork) {
971 const sourceState = this._stateManager.getSessionState(config.fork.session.toString());
990 }
991 }
993 > // When importing a conversation, assign fresh UUID turn ids up front so
994 > // the provider seeds an event log whose ids match the protocol turns we
995 > // seed below — keeping edit / fork / truncate addressable at the SDK
996 > // boundary.
997 if (config?.importConversation) {
998 const importedTurns = config.importConversation.turns.map(t => ({ ...t, id: generateUuid() }));
999 config = { ...config, importConversation: { ...config.importConversation, turns: importedTurns } };
1000 }
1002 > // Resolve host-owned isolation before provider creation. Providers such as
1003 > // Codex may schedule eager prewarming from createSession; marking a
1004 > // client-chosen worktree session pending first prevents that prewarm from
1005 > // materializing in the picked folder before the host creates the worktree.
1006 > const initializeSideEffects = this._sideEffects.initialize();
1007 > const sessionConfig = await this._resolveCreatedSessionConfig(provider, config);
1008 const deferWorktreeCreation = sessionConfig?.values?.[SessionConfigKey.Isolation] === 'worktree' && !config?.fork && !config?.importConversation;
1009
1013 this._createProviderSession(provider, config, deferWorktreeCreation),
1014 ]);
1015 > const session = created.session; agentService.ts
1016 > this._logService.trace(`[AgentService] createSession: initialization complete`);
1017 >
1018 > // Cancel any pending GC armed for this URI. A client may be
1019 > // re-issuing `createSession` for an existing URI mid-grace (e.g.
1020 > // during a reconnect that returned `missing`); without this, the
1021 > // timer would still fire and dispose the just-revived session
1022 > // before the follow-up `subscribe` arrives.
1023 > this._cancelPendingSessionGc(session);
1024 > this._cancelPendingSessionRelease(session);
1025 >
1026 this._logService.trace(`[AgentService] createSession: provider=${provider.id} model=${config?.model?.id ?? '(default)'}`);
1027 this._sessionToProvider.set(session.toString(), provider.id);
1039 sessions.add(session.toString());
1040 }
1041 > this._logService.trace(`[AgentService] createSession returned: ${session.toString()}`); agentService.ts
1042 >
1043 > // Resolve config and seed the initial customization set in parallel so
1044 > // both are available before we register the session in the state
1045 > // manager. Seeding `state.customizations` directly (instead of
1046 > // dispatching `SessionCustomizationsChanged` after the fact) means
1047 > // the very first snapshot a subscriber sees already contains
1048 > // host/global customizations and the custom agents they contribute,
1049 > // so the agent picker doesn't have to wait for a follow-up republish
1050 > // (`RootConfigChanged`, plugin reload, or the first message's
1051 > // `setClientCustomizations`). Subsequent updates flow through the
1052 > // existing `SessionCustomizationsChanged` / `SessionCustomizationUpdated`
1053 > // actions published by `PluginController`.
1054 > const initialCustomizations = await (provider.getSessionCustomizations
1055 ? provider.getSessionCustomizations(session).catch(err => {
1056 this._logService.error('[AgentService] createSession: failed to resolve initial customizations', err);
1057 return undefined;
1058 })
1059 > : Promise.resolve(undefined)); agentService.ts
1060 >
1061 > // When forking, populate the new session's protocol state with
1062 > // the source session's turns so the client sees the forked history.
1063 if (config?.fork) {
1064 const sourceState = this._stateManager.getSessionState(config.fork.session.toString());
1102 this._sideEffects.generateForkedTitle(summary.resource, undefined, sourceTurns, forkedTitle, sourceTitle);
1103 }
1104 > } else if (config?.importConversation) { agentService.ts
1105 // An imported conversation arrives with pre-existing turns (assigned
1106 // fresh UUID ids above). Seed them into the new session's protocol
1128 this._sideEffects.generateForkedTitle(summary.resource, undefined, importedTurns, importedTitle);
1129 }
1130 > } else { agentService.ts
1131 > // Provisional sessions defer the `sessionAdded` notification and
1132 > // the `SessionReady` lifecycle transition until the agent fires
1133 > // {@link IAgent.onDidMaterializeSession} (typically on first
1134 > // `sendMessage`). Until then, the state exists in memory so
1135 > // clients can subscribe and stream config / model changes that
1136 > // the agent will pick up at materialization time.
1137 > const summary = this._buildInitialSummary(provider, session, config, created, '');
1138 > const state = this._stateManager.createSession(summary, { emitNotification: !created.provisional });
1139 > state.config = sessionConfig;
1140 > state.activeClients = config?.activeClient ? [config.activeClient] : [];
1141 > if (initialCustomizations && initialCustomizations.length > 0) {
1142 state.customizations = [...initialCustomizations];
1143 }
1144 > } agentService.ts
1145 > // Persist initial config values so a subsequent `restoreSession` can
1146 > // re-hydrate them. We persist the full resolved values (not just the
1147 > // user's input) so clients can render them on restore without having
1148 > // to re-resolve. Mid-session changes are persisted by `AgentSideEffects`
1149 > // when handling `SessionConfigChanged`.
1150 if (sessionConfig?.values && Object.keys(sessionConfig.values).length > 0 && !created.provisional) {
1151 this._persistConfigValues(session, sessionConfig.values);
1152 }
1154 > this._changesetCoordinator.onSessionCreated(session.toString());
1155 >
1156 > if (!created.provisional) {
1157 // Persist the AH-owned workspace-less marker now that the session DB
1158 // exists, from the value `_buildInitialSummary` inferred. Provisional
1167 this._stateManager.dispatchServerAction(session.toString(), { type: ActionType.SessionReady });
1168 }
1170 > // Refresh the git state for the session.
1171 > const workingDirectory = created.workingDirectory ?? config?.workingDirectory;
1172 void this._gitStateService.refreshSessionGitState(session.toString(), workingDirectory);
1173
1379
1380 private async _createProviderSession(provider: IAgent, config: IAgentCreateSessionConfig | undefined, deferWorktreeCreation: boolean): Promise<IAgentCreateSessionResult> {
1381 > const requestedSessionId = deferWorktreeCreation && config?.session ? AgentSession.id(config.session) : undefined; agentService.ts
1382 > if (requestedSessionId) {
1383 this._worktree?.notePending(requestedSessionId);
1384 }
1386 > let created: IAgentCreateSessionResult | undefined;
1387 > try {
1388 > created = await provider.createSession(config ? this._toProviderConfig(config) : undefined);
1389 > if (deferWorktreeCreation && created.provisional) {
1390 this._worktree?.notePending(AgentSession.id(created.session));
1391 }
1392 > return created; agentService.ts
1393 > } finally {
1394 > const returnedPendingSessionId = created?.provisional ? AgentSession.id(created.session) : undefined;
1395 > if (requestedSessionId && requestedSessionId !== returnedPendingSessionId) {
1396 this._worktree?.clearPending(requestedSessionId);
1397 }
1398 > } agentService.ts
1399 > }
1400
1401 private async _disposeSession(provider: IAgent, session: URI): Promise<void> {
1529
1530 private _buildInitialSummary(provider: IAgent, session: URI, config: IAgentCreateSessionConfig | undefined, created: { project?: { uri: URI; displayName: string }; workingDirectory?: URI }, title: string): SessionSummary {
1531 > const now = new Date().toISOString(); agentService.ts
1532 > const primaryWorkingDir = (created.workingDirectory ?? config?.workingDirectory)?.toString();
1533 > return {
1534 > resource: session.toString(),
1535 > provider: provider.id,
1536 > title,
1537 > status: SessionStatus.Idle,
1538 > createdAt: now,
1539 > modifiedAt: now,
1540 > ...(created.project ? { project: { uri: created.project.uri.toString(), displayName: created.project.displayName } } : {}),
1541 > workingDirectories: primaryWorkingDir ? [primaryWorkingDir] : undefined,
1542 > // Workspace-less is inferred at create from an absent input
1543 > // `workingDirectory` (the host assigns a scratch cwd, so it can't be
1544 > // re-inferred later) and tagged on the generic `_meta` bag.
1545 > ...(config && !config.fork && !config.workingDirectory ? { _meta: withSessionWorkspaceless(undefined, true) } : {}),
1546 > };
1547 > }
1548
1549 /**
src/vs/platform/agentHost/node/agentHostChangesetCoordinator.ts 3 introduced LOC · 1 range

Open complete file

65 */
66 onSessionCreated(sessionStr: string): void {
67 > this._changesets.refreshChangesetCatalog(sessionStr); agentHostChangesetCoordinator.ts
68 > this._changesets.registerStaticChangesets(sessionStr);
69 > }
70
71 /**
src/vs/platform/agentHost/node/agentSideEffects.ts 2 introduced LOC · 1 range

Open complete file

498 */
499 initialize(): Promise<void> {
500 > return this._permissionManager.initialize(); agentSideEffects.ts
501 > }
502
503 // ---- Agent registration -------------------------------------------------
src/vs/platform/agentHost/common/changesetUri.ts 1 introduced LOC · 1 range

Open complete file

301 // New Session
302 if (state.lifecycle === SessionLifecycle.Creating) {
303 > if (readSessionWorkspaceless(state._meta)) { changesetUri.ts
304 // Quick chat
305 return [];