Skip to content

Agents - generate titles for untitled external sessions - #331985

Merged
Benjamin Christopher Simmonds (benibenj) merged 8 commits into
mainfrom
benibenj/agents/external-session-rename-logic
Aug 22, 2026
Merged

Agents - generate titles for untitled external sessions#331985
Benjamin Christopher Simmonds (benibenj) merged 8 commits into
mainfrom
benibenj/agents/external-session-rename-logic

Conversation

@benibenj

Copy link
Copy Markdown
Contributor

External sessions whose provider surfaces them without a title showed up unnamed in the sessions list. This generates one from the user's first prompt, reusing the existing rename infrastructure.

Deferred work

Rather than an ad-hoc timer, AgentService gets a single lane for maintenance that is fine to run late:

  • markStartupComplete() is the explicit signal, called by the process mains — agentHostMain once every ingress is wired, agentHostServerMain when it reports READY.
  • Work queued through _runWhenStartupSettled(name, work) runs once that signal and the first served session listing have both happened, so background work never competes with startup. Jobs are serialized, failures are logged per job, and a disposal guard keeps work off a torn-down service.
  • The stale external-session prune moves onto the same lane, replacing its 60s timer. The service now owns no ambient timer, which also keeps tests deterministic — they opt in via markStartupComplete().

Titling external sessions

  • Discovery and legacy migration queue newly registered external sessions that carry no provider title.
  • The job keeps the 2 most recently updated candidates, reads the first user prompt from the default chat, and hands it to AgentHostSessionTitleController.generateExternalSessionTitle — the same utility-model prompt, cleaning, persistence and cancellation used for first-message titling.
  • A session that already carries a persisted title is left alone, and a rename during generation cancels it.
  • These sessions are surfaced but not live, so AgentHostStateManager.updateSurfacedSessionTitle pushes the title onto the surfaced summary and clients update in place; live sessions still go through the reducer.

Validation

  • npm run typecheck-client clean.
  • 665 tests pass across the agentService, agentHostSessionTitleController, agentHostStateManager and agentSideEffects suites, including four new tests: deferred titling of the 2 most recent candidates, surfaced-summary retitling, not clobbering a concurrent rename, and keeping an existing persisted title.
  • The 7 sessionPermissions failures visible in a wider run reproduce on a clean checkout of this branch, so they are pre-existing and unrelated.

External sessions whose provider surfaces them without a title showed up
unnamed in the sessions list. Generate one from the user's first prompt,
reusing the existing rename infrastructure.

Adds a deferred-work lane on AgentService: `markStartupComplete()` is
signalled by the process mains, and work queued through
`_runWhenStartupSettled` runs once that and the first served session
listing have both happened. Background maintenance therefore never
competes with startup, and the service owns no ambient timer. The stale
external-session prune moves onto the same lane, replacing its 60s timer.

Discovery and legacy migration queue newly registered external sessions
that have no provider title. The job keeps the 2 most recently updated
candidates, reads the first user prompt from the default chat, and hands
it to `AgentHostSessionTitleController.generateExternalSessionTitle`,
which reuses the same utility-model prompt, cleaning, persistence, and
cancellation as first-message titling. Sessions that already carry a
persisted title are left alone, and a rename during generation cancels
it. Since these sessions are surfaced but not live,
`updateSurfacedSessionTitle` pushes the title onto the surfaced summary
so clients update in place.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds deferred title generation for untitled external agent sessions after startup settles.

Changes:

  • Introduces serialized post-startup maintenance for pruning and title generation.
  • Generates and persists titles for the two newest untitled external sessions.
  • Updates surfaced session summaries and protects concurrent user renames.
Show a summary per file
File Description
agentService.ts Schedules maintenance and external-session titling.
agentHostSessionTitleController.ts Generates, applies, and persists external titles.
agentHostStateManager.ts Updates surfaced session titles.
agentSideEffects.ts Exposes external title generation.
agentHostMain.ts Signals desktop host startup completion.
agentHostServerMain.ts Signals server startup completion.
agentService.test.ts Tests deferred candidate selection.
agentHostSessionTitleController.test.ts Tests persistence, updates, and rename protection.

Review details

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

  • Files reviewed: 8/8 changed files
  • Comments generated: 3
  • Review effort level: Balanced

Comment thread src/vs/platform/agentHost/node/agentHostSessionTitleController.ts Outdated
Comment thread src/vs/platform/agentHost/node/agentService.ts Outdated
Comment thread src/vs/platform/agentHost/node/agentHostMain.ts Outdated
Copilot AI and others added 2 commits August 21, 2026 16:04
Co-authored-by: benibenj <44439583+benibenj@users.noreply.github.com>
Co-authored-by: benibenj <44439583+benibenj@users.noreply.github.com>
roblourens
roblourens previously approved these changes Aug 21, 2026
- `generateExternalSessionTitle` returned a promise that resolved before
  generation had run, because `_generateTitleSoon` starts the work
  fire-and-forget. The awaited loop in `_titleUntitledExternalSessions`
  therefore launched both model calls concurrently and
  `whenDeferredWorkSettled()` reported completion while generation and
  persistence were still in flight, breaking the lane's serialization
  contract. Split out `_startTitleGeneration`, which returns the tracked
  promise, and await it on the external-session path.

- `listSessions` marked the first listing as served from its rejection
  handler too, so deferred maintenance could start after a failed listing
  and compete with the retry the gate exists to protect. Keep in-flight
  cleanup on both paths but only set the flag on fulfillment.

- `agentHostMain` marked startup complete while the configured WebSocket
  server was still being created and wired, so deferred work could begin
  concurrently with that remaining startup. Mark completion once the
  optional startup promise settles, keeping its non-fatal error handling.

Adds a regression test for the failed-listing gate, and tightens the
existing titling tests to assert the awaited-generation contract without
polling.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
roblourens
roblourens previously approved these changes Aug 21, 2026
Resolves a conflict in `_registerDiscoveredChats`: main replaced the
`existing` provenance map with a `registeredKeys` set, so take main's
`registeredKeys.add(...)` while keeping the collection of untitled
external sessions for deferred titling.

Also repairs `new AgentService(...)` in agentService.test.ts, which main
left calling the pre-`IAgentServiceCore` 5-argument signature; it now
uses the file's `createTestAgentService` helper like every other case.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM! Code changes are clean and well-structured.

Resolves a conflict in `agentService.ts` against "Simplify AgentService
composition (#332035)", which moved the `agents` / `onDidStartTurn`
getters out of `AgentService` into the composition/runtime layer and left
the region overlapping the deferred-work lane this branch added.

Takes main's removal of both getters, drops main's timer-based
`_scheduleExternalSessionPrune`, and keeps the startup-settled lane that
replaced it — the prune now runs through `_runWhenStartupSettled` like
the external-session titling job.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…nal-session-rename-logic

# Conflicts:
#	src/vs/platform/agentHost/node/agentService.ts
@benibenj
Benjamin Christopher Simmonds (benibenj) merged commit 4fae990 into main Aug 22, 2026
27 checks passed
@benibenj
Benjamin Christopher Simmonds (benibenj) deleted the benibenj/agents/external-session-rename-logic branch August 22, 2026 09:55
@vs-code-engineering vs-code-engineering Bot added this to the 1.135.0 milestone Aug 22, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

7 participants