* NES cursor jump: respect first model with tag CursorJump from /models
* extract cursor-jump model name selection into a method with hardcoded default
* comment cursor-jump model name selection priority
* Fix open router prompt caching with messages API
* Send anthropic-beta headers for OpenRouter Messages API
Extract getAnthropicBetaHeader() on ChatEndpoint so subclasses that
override getExtraHeaders() (like OpenAIEndpoint) can opt in. Override
it on OpenRouterEndpoint so anthropic-beta (interleaved-thinking,
advanced-tool-use, context-management) is sent on the /messages path.
* Use TokenizerType.O200K instead of 'as any' in OpenRouter test
---------
Co-authored-by: bhavyaus <bhavyau@microsoft.com>
- Refactor IThemeImporterService to return IThemePreviewResult with
apply() and reset() methods from previewVSCodeTheme(), removing
the separate importVSCodeTheme() API
- Preview result is cached so repeated calls reuse the same install
- apply() permanently imports the theme without re-doing the preview
- reset() uninstalls the temporarily installed extension
- Call reset() when user switches away from VS Code theme to a
built-in theme card
- Remove 'Happy Agentic Coding!' tagline during sign-in progress
by passing a direct element reference instead of using querySelector
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Add the github.copilot.chat.agent.backgroundTodoAgent.enabled setting
to the advanced configuration section, fixing the CI failure in
configurations.spec.ts that requires all experiment-backed config
keys to have a corresponding package.json entry.
- Add hideTodoPromptInstructions to ReminderInstructionsProps and
AgentUserMessageProps; thread it through getUserMessagePropsFromAgentProps
and the reminderProps construction in AgentUserMessage
- Gate todo tool guidance and markdown fallback paragraphs in
gpt5Prompt, gpt51Prompt, gpt52Prompt, gpt53CodexPrompt: suppress
the full planning section when the background agent manages todos
- Restore the missing fallback paragraph for gpt51 and gpt52 that
was accidentally removed (empty branch body)
- Gate the CRITICAL todo checklist and plan-tracking bullets in
AlternateGPTPrompt (defaultAgentInstructions) using hasTodoTool
- Gate todo-specific reminder instructions in Gpt5ReminderInstructions
- Render each assistant context snippet as its own UserMessage with
descending priority so prompt-tsx can prune oldest snippets first
when the prompt exceeds the token budget
- Tighten prompt guidelines: require 3+ steps for list creation,
forbid single-item lists, exclude operational sub-tasks, add
concrete good/bad examples for granularity
- Add extractSessionResourceString() helper that properly serializes
Uri objects to strings, fixing lookups in getCurrentTodoContext()
- Use delta.newRounds directly instead of collectAllRounds() to avoid
duplicating rounds already extracted from history by peekDelta()
- Make failed passes retryable: don't call markProcessed() on error
so the delta's rounds can be picked up by a subsequent pass
- Throw on non-success model responses instead of silently treating
them as no-op, allowing retry on transient endpoint failures
- Fix waitForCompletion() to loop until no new promise is created by
_checkPending(), ensuring coalesced passes are fully drained
- Update test to reflect retryable failure semantics
Cover executeFinalReview no-ops (no context, no todos created).
Verify coalesced pending delta runs its own queued work callback.
Add extractTarget tests for multi-edit single/few/many file paths.
Test that explanation/description notes attach to latestRound summaries.
Add executeFinalReview() that fires after the agent loop ends so the
last round's completions are not stuck as in-progress. Cache the most
recent execution context and guard against duplicate finalize passes.
Store the pending work callback alongside the pending delta so
coalesced finalize passes retain their isFinalReview closure.
Extract tool notes (explanation/description/goal) and surface them
in the latest-round detail. Support multi_replace_string_in_file
targets by reading paths from replacements[]. Pick the last
manage_todo_list call when the model emits multiple in one response.
Extract verbose inline JSX prompt text into reusable constants.
Add granularity rules to prefer 2-4 high-level phase items over
file-level implementation details. Strengthen sequential state and
ordering rules so items complete in order with skip-reordering.
Add new-task deduplication rules. Gate tool calls on an explicit
diff check to avoid redundant no-change updates.
* agents: render Copilot CLI 'rg' search tool
The Copilot CLI emits both 'grep' and 'rg' search tool calls, but the
agent-host only recognized 'grep'. 'rg' calls fell through to generic
'Using "rg"' / 'Used "rg"' messages.
Add 'rg' as a first-class tool in copilotToolDisplay.ts with its own
typed args interface (matching the CLI parity reference), display name
'Search', and 'Searching for {pattern}' / 'Searched for {pattern}'
messages mirroring the existing 'grep' wording. Also broaden
ICopilotGrepToolArgs to match the current CLI schema.
(Written by Copilot)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* address Copilot review: fix doc comment and reuse grep l10n keys for rg
- Remove bare filename reference from ICopilotGrepToolArgs doc comment
- rg cases reuse toolInvoke.grepPattern/grep and toolComplete.grepPattern/grep
instead of introducing duplicate localization keys with identical strings
(Written by Copilot)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Add search icon for grep/rg tools via 'search' toolKind
- Add SEARCH_TOOL_NAMES set (grep, rg) in copilotToolDisplay.ts; getToolKind() returns 'search' for these tools
- Widen toolKind type to include 'search' in mapSessionEvents.ts, sessionReducers.ts, and IHistoryToolStartRecord
- Add IChatSearchToolInvocationData { kind: 'search' } to chatService.ts and all toolSpecificData union types
- stateToProgressAdapter: set toolSpecificData = { kind: 'search' } for completed and running search tools
- chatToolInvocationSubPart.ts: getIcon() returns Codicon.search (spinning while running) for search tools
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Simplify search icon: use getToolInvocationIcon instead of toolSpecificData
Revert the 'search' toolKind / IChatSearchToolInvocationData approach from the
previous commit. Instead, use the existing getToolInvocationIcon() function
(chatThinkingContentPart.ts) which already returns Codicon.search for tool IDs
containing 'grep', 'search', the same mechanism used by EH CLI sessions.etc.
Changes:
- Add 'rg' exact-match to getToolInvocationIcon()'s search condition
- getIcon() in BaseChatToolInvocationSubPart now calls getToolInvocationIcon(toolId)
instead of hardcoding Codicon.check / Codicon.loading, so all tools show their
semantic icon (search, pencil, terminal, etc.) rather than a generic check/spinner
- Revert all IChatSearchToolInvocationData / toolSpecificData / getToolKind changes
(Written by Copilot)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Revert "Simplify search icon: use getToolInvocationIcon instead of toolSpecificData"
This reverts commit 1ad9ff21cd.
* Search icon: don't spin while running
(Written by Copilot)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Fix double-icon: move search icon to outer thinking-part layer
The rg/grep tools were showing two icons per row:
- Outer icon: generic tools (Codicon.tools) from chatThinkingContentPart
- Inner icon: Codicon.search from getIcon() in BaseChatToolInvocationSubPart
This is inconsistent with how terminal tools work (outer=terminal, inner=check/spinner).
Fix:
- chatThinkingContentPart: detect toolSpecificData.kind === 'search' and show
Codicon.search as the outer type-icon (same pattern as the isTerminalTool check)
- chatToolInvocationSubPart: remove the search-specific case from getIcon() so the
inner icon reverts to normal check/spinner status icons
Result: outer=search (type), inner=check/spinner (status) - consistent with terminal.
(Written by Copilot)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
---------
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Agents - scaffold the model reference collections
* Add test coverage for the new methods
* Migrated existing tests to use the new code path
* Switch over to using the new references
* More cleanup
* Centralized polling of the models
* More fixes
* agents: show per-turn model in agent-host session history
Surface the model used for each response in agent-host chat sessions, like
extension-host Copilot CLI sessions do.
AHP already reports the model on each turn via Turn.usage?.model. The
workbench-side adapter (turnsToHistory) was ignoring this and stamping a
single session-level modelId on every request, with no per-response model
detail.
Refactor turnsToHistory to take a TurnModelLookup (id + display name
resolution) instead of a single modelId. AgentHostSessionHandler builds
the lookup using ILanguageModelsService and the session-type prefix, with
fallback to the session-level summary model when a turn has no usage.
Response history items now carry the display name in 'details', which
flows through to the per-response footer in the chat UI.
(Written by Copilot)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Add integrated history-loading test for per-turn model display
Covers the active-turn restoration path through provideChatSessionContent
with a mix of completed turns (with/without usage.model) and an in-flight
activeTurn falling back to the session-level model.
(Written by Copilot)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
---------
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
agent-host: respect chat.permissions.default for new sessions
New agent-host sessions now seed their initial `autoApprove` config from
the user-facing `chat.permissions.default` setting, matching the behavior
of CopilotChatSessionsProvider. Agents that advertise the well-known
auto-approve enum (`default | autoApprove | autopilot`) pick up the
configured value on their first `resolveSessionConfig` round-trip; agents
that do not advertise `autoApprove` ignore the unknown key.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* sessions: count remote plugin customizations
Remote agent-host customization providers can contribute plugin rows and plugin-sourced items without a local plugin URI. Preserve provider-declared storage while normalizing items and include remote provider plugin rows in sidebar plugin counts, excluding locally synced remote-client rows.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* sessions: dedupe locally installed plugins from harness rows
When the AHP harness reports plugins that are already known to IAgentPluginService (e.g. the local Copilot CLI surfaces its installed plugins as remote-host customizations), do not double-count or double-display them. Match by display name and fold harness-provided rows into the locally installed plugin.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* sessions: add data-source mirror tests for customizations counts
Adds a new 'data sources' suite to AICustomizationItemsModel tests that:
- Validates getCount() for each prompts-based section (Agents, Skills, Instructions, Prompts, Hooks) reflects provider items filtered by the section's prompt type.
- Validates getCount() refetches and updates when the provider fires onDidChange.
- Validates getPluginCount() in three scenarios: only local plugins, only harness plugin rows (with type='plugin' / 'plugins' and remote-client filtered out), and a mix that exercises name-based dedup.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Preserve groupKey/isBuiltin in URI-inference fallback
The Agents app customization provider declares its built-in items
only via groupKey: BUILTIN_STORAGE — without an explicit storage,
extensionId, pluginUri, or workspace-anchored URI. The final fallback
in inferStorageAndGroup dropped groupKey/isBuiltin and returned
PromptsStorage.user, so those items rendered under 'User' instead of
'Built-in'.
Carry groupKey and isBuiltin through the fallback so the list widget
preserves the provider's intent. Adds a regression test.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Align getPluginCount dedup with PluginListWidget basename fallback
When a locally installed plugin has a falsy IAgentPlugin.label, the
editor PluginListWidget renders it under basename(plugin.uri) (see
installedPluginToItem), but the items-model getPluginCount derived its
dedup key from (label ?? '').toLowerCase(). Result: a remote provider
row whose name matched the URI basename was hidden by the editor list
but still added to the sidebar plugin count, recreating the very count
drift this PR is trying to eliminate.
Use the same (label || basename(uri)) fallback as the widget. Adds a
regression test that fails without the fix.
Council-review: addresses 3/3 consensus finding.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* Address council review: align dedup, widen storage type
Two fixes from a multi-model code review of this PR:
1. PluginListWidget.installedPluginToItem used `plugin.label ?? basename`, but the items model's getPluginCount uses `plugin.label || basename`. For plugins with an empty-string label, the dedup keys diverged, so a remote provider row matching the URI basename was hidden by the editor list yet still added to the sidebar plugin count — recreating the very count drift earlier commits in this PR aim to eliminate. Switch the widget to `||` so empty labels also fall back to the URI basename.
2. ICustomizationItem.storage was typed as PromptsStorage, but inferStorageAndGroup compared `item.storage === (BUILTIN_STORAGE as unknown as PromptsStorage)`, which is type-laundering. Widen the field to AICustomizationPromptsStorage so providers can declare `storage: BUILTIN_STORAGE` without a cast, and drop the cast in inferStorageAndGroup. Coerce back to PromptsStorage at the IChatPromptSlashCommand boundary in getPromptSlashCommands.
* Recognize User/globalStorage extension paths as extension-owned
Extensions like Copilot Chat materialize prompt files under their own `globalStorageUri` and register them via the prompt-file provider API (e.g. ~/<userdata>/User/globalStorage/github.copilot-chat/ask-agent/Ask.agent.md). When such items reach inferStorageAndGroup with no extensionId/pluginUri/storage and the URI doesn't fall under a workspace or plugin folder, they would previously land in "User" instead of the chat extension's "Built-in" group.
Extend extractExtensionIdFromPath to also recognize User/globalStorage/<extensionId>/... paths. Use a strict publisher.name regex to avoid matching unrelated entries like state.vscdb. Add three regression tests.
---------
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Ubuntu <josh@ahp.4mywozgnka0etnlo23z031udwc.xx.internal.cloudapp.net>
* fix(nes-datagen): discard xtab-275 oracle edits outside edit window
The NES model can only edit lines inside the prompt's <|code_to_edit|>
window [K, N). formatAsEditWindowOnly was expanding the window to cover
stray oracle edits, so the assistant text spilled out of the window and
duplicated surrounding context when applied.
Instead, keep edits in order and drop the first edit that isn't fully
contained in [K, N) along with every later edit (later offsets assume
earlier edits were applied). Add unit tests covering both the spillover
repro and the all-in-window control case.
* refactor(nes-datagen): extract filterEditsInsideEditWindow helper
Pull the line-containment filter out of formatAsEditWindowOnly into a
reusable helper, plus a small getEditLineRange utility to dedupe the
offset->line conversion. No behavior change.
* refactor(nes-datagen): route xtab-275 dropped-edit warnings through pipeline logger
Replace the console.warn in formatAsEditWindowOnly with a structured
return shape ({ assistant, droppedCount }) and a ResponseLogger threaded
through generateResponse / generateAllResponses. The pipeline now logs
dropped edits via its existing log callback (so warnings are visible to
dataset curators and captured by the e2e test logs), and surfaces the
count on IGeneratedResponse.droppedEditCount.
* fix(nes-datagen): correct netLineChange math for full-line deletions
splitLines("L6\n") returns ['L6', ''] (length 2), so deleting a single
terminated line was counted as -2 lines instead of -1. Compute the delta
by counting newlines in the old segment vs. the new text, which gives
the correct line-count delta for any combination of insertions,
deletions, or replacements without the trailing-empty quirk.
* test(nes-datagen): cover line-changing and boundary-straddling oracle edits
Add cases for in-window inserts that grow the slice, in-window deletes
that shrink it, edits that straddle the window boundary (must be
discarded), and the all-edits-outside fallback (assistant equals the
original window slice with droppedCount equal to all edits).
* fix(nes-datagen): filter oracle edits independently against edit window
Oracle edits come from StringEdit.compose().replacements upstream
(processor.ts) and applyEditsToContent applies them all to the original
doc by sorting offset-descending — so each edit's offsets are
independent. The earlier drop-and-truncate rule (which discarded every
edit after the first out-of-window one) was over-conservative and threw
away in-window edits that were perfectly applicable.
Filter each edit on its own merits and update the test that relied on
the truncate behavior to assert independent filtering instead.
* feat(nes-datagen): surface dropped-edit count in pipeline summary
Sum droppedEditCount across all generated responses and append it to
the [4/5] log line when nonzero, so dataset curators running the
pipeline can see at a glance how many oracle edits were discarded as
out-of-window. Silent omission was the original bug; making it visible
in the summary closes the feedback loop.
The AfterLine placement was never the desired default. Remove the
NextCursorLinePredictionCursorPlacement enum and the associated
experiment-based config key, and simplify getNextCursorColumn to
always place the cursor at the first non-whitespace character of
the predicted line (BeforeLine behaviour).
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Previously compressHistory truncated assistant responses to 400/1500 chars and only kept 3 hand-picked snippets (latest + first + longest middle). This dropped useful planning context before prompt-tsx ever saw it.
- Remove MAX_RESPONSE_LENGTH, MAX_LATEST_RESPONSE_LENGTH and the truncateResponse helper.
- extractAssistantContext now returns every non-empty assistant response in chronological order, untruncated.
- BackgroundTodoPrompt renders each snippet as its own UserMessage with priority Math.max(700, 850 - age * 30) so the prompt-tsx renderer prunes the oldest snippets first when the budget is tight.
- Update history specs for the new no-truncation / chronological behaviour.
- Default to silence: enumerate the only conditions under which the bg agent should call manage_todo_list (creation, completion, advancing to next, genuinely new work). Forbid re-affirming, re-wording or re-marking 'in-progress'.
- Sequential execution: enforce exactly one 'in-progress' item; require the previous 'in-progress' to be marked 'completed' in the same update before promoting the next item.
- Status transitions: codify the allowed moves and forbid regression from 'completed'.
Reduces churn from speculative or duplicate updates and keeps execution strictly serial.
Rewrites the background todo system prompt to bias toward complete, forward-looking plans rather than per-file mirroring of recent activity.
- Adds an explicit ABORT CONDITIONS block so research-only prompts ("read the following files", "do NOT write any code"), pure read-only activity, and one-item-per-file temptations short-circuit to silence.
- Adds a PLAN COMPLETENESS section requiring the list to cover the full user request and to be derived from the user request + agent's stated plan, with subagent findings and grouped progress as supporting evidence only.
- Renders the new subagentDigests block at lower priority with a label that explicitly tells the model not to mirror its structure as the todo list, so prompt-tsx prunes it before higher-signal context.
Policy: drop the context-only firing branch so research-only requests (e.g. "read these files and summarise") with many read_file/list_dir calls but no mutating actions never trigger the bg agent. The initial-request case now also waits for activity instead of guessing a plan from the user message alone.
History compression: pass toolCallResults into compressHistory and extract subagent (search/explore/execution/run) outputs as ISubagentDigest entries so the bg agent sees what exploration discovered, not just that it happened. Raise the latest-round assistant response cap to 1500 chars (older rounds stay at 400) and additionally surface the longest mid-trajectory assistant message — the typical place the agent states its plan.
Logging: add structured debug logs for every policy decision, pass start/completion, coalesced delta, and history-compression summary so background agent behaviour is traceable end-to-end.
Previously executePass was invoked for both Run and Wait decisions, relying on the processor to coalesce. That coupling made it harder to reason about when the bg agent fires. Tighten the call site to only fire on Run, and pass an ILogService into the processor so its policy/coalescing decisions show up in logs.
Adds `github.copilot.chat.otel.maxAttributeSizeChars` setting and
`COPILOT_OTEL_MAX_ATTRIBUTE_SIZE_CHARS` env var to control truncation
of free-form OTel content attributes (prompts, responses, tool
arguments/results, hook input/output).
Default is `0` (unlimited), matching the OTel spec's
`AttributeValueLengthLimit` default of `Infinity`. Users on backends
with per-attribute size limits can set a positive value to keep OTLP
batches under the backend cap.
Plumbs the resolved limit through every call site that previously hit a
hardcoded 64KB fallback. Drops the `DEFAULT_MAX_OTEL_ATTRIBUTE_LENGTH`
constant; `truncateForOTel`'s default arg is now `0` (unlimited).
Refs #299952