Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

import { localize } from '../../../nls.js';
import { createSchema, schemaProperty } from './agentHostSchema.js';
import { CustomizationType, type Customization } from './state/protocol/state.js';
import { CustomizationType, type Customization, type PluginCustomization } from './state/protocol/state.js';
import { customizationId } from './state/sessionState.js';

/**
Expand Down Expand Up @@ -105,7 +105,7 @@ export function getAgentHostConfiguredCustomizations(values: Record<string, unkn
* Lifts a persisted plugin config entry into the new
* {@link Customization} container shape.
*/
export function toContainerCustomization(entry: IPersistedCustomizationConfigEntry): Customization {
export function toContainerCustomization(entry: IPersistedCustomizationConfigEntry): PluginCustomization {
return {
type: CustomizationType.Plugin,
id: customizationId(entry.uri),
Expand Down
6 changes: 3 additions & 3 deletions src/vs/platform/agentHost/common/agentPluginManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

import { URI } from '../../../base/common/uri.js';
import { createDecorator } from '../../instantiation/common/instantiation.js';
import type { ClientPluginCustomization, Customization } from './state/sessionState.js';
import type { ClientPluginCustomization, PluginCustomization } from './state/sessionState.js';

export const IAgentPluginManager = createDecorator<IAgentPluginManager>('agentPluginManager');

Expand All @@ -14,7 +14,7 @@ export const IAgentPluginManager = createDecorator<IAgentPluginManager>('agentPl
*/
export interface ISyncedCustomization {
/** The session customization with loading/error status. */
readonly customization: Customization;
readonly customization: PluginCustomization;
/** Local plugin directory URI, defined when the sync was successful. */
readonly pluginDir?: URI;
}
Expand Down Expand Up @@ -51,5 +51,5 @@ export interface IAgentPluginManager {
* @returns Final status for every customization, with `pluginDir`
* defined when the sync was successful.
*/
syncCustomizations(clientId: string, customizations: ClientPluginCustomization[], progress?: (status: Customization) => void): Promise<ISyncedCustomization[]>;
syncCustomizations(clientId: string, customizations: ClientPluginCustomization[], progress?: (status: PluginCustomization) => void): Promise<ISyncedCustomization[]>;
}
3 changes: 3 additions & 0 deletions src/vs/platform/agentHost/common/customAgents.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@ export function getEffectiveAgents(
const seen = new Map<string, AgentCustomization>();
if (sessionCustomizations) {
for (const container of sessionCustomizations) {
if (container.type === CustomizationType.McpServer) {
continue;
}
if (container.enabled === false || !container.children) {
continue;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1 +1 @@
eafb1d7
740f6cf
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
// Generated from types/actions.ts — do not edit
// Run `npm run generate` to regenerate.

import { ActionType, type StateAction, type RootAgentsChangedAction, type RootActiveSessionsChangedAction, type RootTerminalsChangedAction, type RootConfigChangedAction, type SessionReadyAction, type SessionCreationFailedAction, type SessionTurnStartedAction, type SessionDeltaAction, type SessionResponsePartAction, type SessionToolCallStartAction, type SessionToolCallDeltaAction, type SessionToolCallReadyAction, type SessionToolCallConfirmedAction, type SessionToolCallCompleteAction, type SessionToolCallResultConfirmedAction, type SessionToolCallContentChangedAction, type SessionTurnCompleteAction, type SessionTurnCancelledAction, type SessionErrorAction, type SessionTitleChangedAction, type SessionUsageAction, type SessionReasoningAction, type SessionModelChangedAction, type SessionAgentChangedAction, type SessionServerToolsChangedAction, type SessionActiveClientChangedAction, type SessionActiveClientToolsChangedAction, type SessionPendingMessageSetAction, type SessionPendingMessageRemovedAction, type SessionQueuedMessagesReorderedAction, type SessionInputRequestedAction, type SessionInputAnswerChangedAction, type SessionInputCompletedAction, type SessionCustomizationsChangedAction, type SessionCustomizationToggledAction, type SessionCustomizationUpdatedAction, type SessionCustomizationRemovedAction, type SessionTruncatedAction, type SessionIsReadChangedAction, type SessionIsArchivedChangedAction, type SessionActivityChangedAction, type SessionChangesetsChangedAction, type SessionConfigChangedAction, type SessionMetaChangedAction, type ChangesetStatusChangedAction, type ChangesetFileSetAction, type ChangesetFileRemovedAction, type ChangesetOperationsChangedAction, type ChangesetClearedAction, type TerminalDataAction, type TerminalInputAction, type TerminalResizedAction, type TerminalClaimedAction, type TerminalTitleChangedAction, type TerminalCwdChangedAction, type TerminalExitedAction, type TerminalClearedAction, type TerminalCommandDetectionAvailableAction, type TerminalCommandExecutedAction, type TerminalCommandFinishedAction, type ResourceWatchChangedAction } from './actions.js';
import { ActionType, type StateAction, type RootAgentsChangedAction, type RootActiveSessionsChangedAction, type RootTerminalsChangedAction, type RootConfigChangedAction, type SessionReadyAction, type SessionCreationFailedAction, type SessionTurnStartedAction, type SessionDeltaAction, type SessionResponsePartAction, type SessionToolCallStartAction, type SessionToolCallDeltaAction, type SessionToolCallReadyAction, type SessionToolCallConfirmedAction, type SessionToolCallCompleteAction, type SessionToolCallResultConfirmedAction, type SessionToolCallContentChangedAction, type SessionTurnCompleteAction, type SessionTurnCancelledAction, type SessionErrorAction, type SessionTitleChangedAction, type SessionUsageAction, type SessionReasoningAction, type SessionModelChangedAction, type SessionAgentChangedAction, type SessionServerToolsChangedAction, type SessionActiveClientChangedAction, type SessionActiveClientToolsChangedAction, type SessionPendingMessageSetAction, type SessionPendingMessageRemovedAction, type SessionQueuedMessagesReorderedAction, type SessionInputRequestedAction, type SessionInputAnswerChangedAction, type SessionInputCompletedAction, type SessionCustomizationsChangedAction, type SessionCustomizationToggledAction, type SessionCustomizationUpdatedAction, type SessionCustomizationRemovedAction, type SessionMcpServerStateChangedAction, type SessionTruncatedAction, type SessionIsReadChangedAction, type SessionIsArchivedChangedAction, type SessionActivityChangedAction, type SessionChangesetsChangedAction, type SessionConfigChangedAction, type SessionMetaChangedAction, type ChangesetStatusChangedAction, type ChangesetFileSetAction, type ChangesetFileRemovedAction, type ChangesetOperationsChangedAction, type ChangesetOperationStatusChangedAction, type ChangesetClearedAction, type TerminalDataAction, type TerminalInputAction, type TerminalResizedAction, type TerminalClaimedAction, type TerminalTitleChangedAction, type TerminalCwdChangedAction, type TerminalExitedAction, type TerminalClearedAction, type TerminalCommandDetectionAvailableAction, type TerminalCommandExecutedAction, type TerminalCommandFinishedAction, type ResourceWatchChangedAction } from './actions.js';


// ─── Root vs Session vs Terminal vs Changeset Action Unions ─────────────────
Expand Down Expand Up @@ -69,6 +69,7 @@ export type SessionAction =
| SessionCustomizationToggledAction
| SessionCustomizationUpdatedAction
| SessionCustomizationRemovedAction
| SessionMcpServerStateChangedAction
| SessionTruncatedAction
| SessionIsReadChangedAction
| SessionIsArchivedChangedAction
Expand Down Expand Up @@ -121,6 +122,7 @@ export type ServerSessionAction =
| SessionCustomizationsChangedAction
| SessionCustomizationUpdatedAction
| SessionCustomizationRemovedAction
| SessionMcpServerStateChangedAction
| SessionActivityChangedAction
| SessionChangesetsChangedAction
| SessionMetaChangedAction
Expand Down Expand Up @@ -166,6 +168,7 @@ export type ChangesetAction =
| ChangesetFileSetAction
| ChangesetFileRemovedAction
| ChangesetOperationsChangedAction
| ChangesetOperationStatusChangedAction
| ChangesetClearedAction
;

Expand All @@ -180,6 +183,7 @@ export type ServerChangesetAction =
| ChangesetFileSetAction
| ChangesetFileRemovedAction
| ChangesetOperationsChangedAction
| ChangesetOperationStatusChangedAction
| ChangesetClearedAction
;

Expand Down Expand Up @@ -242,6 +246,7 @@ export const IS_CLIENT_DISPATCHABLE: { readonly [K in StateAction['type']]: bool
[ActionType.SessionCustomizationToggled]: true,
[ActionType.SessionCustomizationUpdated]: false,
[ActionType.SessionCustomizationRemoved]: false,
[ActionType.SessionMcpServerStateChanged]: false,
[ActionType.SessionTruncated]: true,
[ActionType.SessionIsReadChanged]: true,
[ActionType.SessionIsArchivedChanged]: true,
Expand All @@ -253,6 +258,7 @@ export const IS_CLIENT_DISPATCHABLE: { readonly [K in StateAction['type']]: bool
[ActionType.ChangesetFileSet]: false,
[ActionType.ChangesetFileRemoved]: false,
[ActionType.ChangesetOperationsChanged]: false,
[ActionType.ChangesetOperationStatusChanged]: false,
[ActionType.ChangesetCleared]: false,
[ActionType.TerminalData]: false,
[ActionType.TerminalInput]: true,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

import { ActionType } from '../common/actions.js';
import type { ErrorInfo } from '../common/state.js';
import { ChangesetStatus, type ChangesetFile, type ChangesetOperation } from './state.js';
import { ChangesetStatus, type ChangesetFile, type ChangesetOperation, type ChangesetOperationStatus } from './state.js';

// ─── Changeset Actions ───────────────────────────────────────────────────────

Expand Down Expand Up @@ -70,6 +70,31 @@ export interface ChangesetOperationsChangedAction {
operations: ChangesetOperation[] | undefined;
}

/**
* The {@link ChangesetOperation.status} for a single operation transitioned
* (e.g. `idle → running → idle`, or `running → error`). The error payload
* is set together with `status` whenever it transitions to
* {@link ChangesetOperationStatus.Error | Error}, and cleared on any other
* transition.
*
* Targets one operation by its {@link ChangesetOperation.id}. If no
* operation with that id is currently present in the changeset, the action
* is a no-op. Use {@link ChangesetOperationsChangedAction} to add, remove,
* or otherwise replace the operation list itself.
*
* @category Changeset Actions
* @version 3
*/
export interface ChangesetOperationStatusChangedAction {
type: ActionType.ChangesetOperationStatusChanged;
/** The {@link ChangesetOperation.id} whose status changed. */
operationId: string;
/** New execution status. */
status: ChangesetOperationStatus;
/** Cause when `status === ChangesetOperationStatus.Error`; otherwise omitted. */
error?: ErrorInfo;
}

/**
* Drop every file from the changeset.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
// DO NOT EDIT -- auto-generated by scripts/sync-agent-host-protocol.ts

import { ActionType } from '../common/actions.js';
import { ChangesetStatus, type ChangesetState, type ChangesetFile } from './state.js';
import { ChangesetStatus, ChangesetOperationStatus, type ChangesetState, type ChangesetFile, type ChangesetOperation } from './state.js';
import type { ChangesetAction } from '../action-origin.generated.js';
import { softAssertNever } from '../common/reducer-helpers.js';

Expand Down Expand Up @@ -60,6 +60,29 @@ export function changesetReducer(state: ChangesetState, action: ChangesetAction,
return { ...state, operations: action.operations };
}

case ActionType.ChangesetOperationStatusChanged: {
if (state.operations === undefined) {
return state;
}
const idx = state.operations.findIndex(o => o.id === action.operationId);
if (idx < 0) {
return state;
}
const current = state.operations[idx];
// Carry `error` only when the new status is `Error` so we don't leave
// a stale error on an operation that recovered or started running.
let nextOp: ChangesetOperation;
if (action.status === ChangesetOperationStatus.Error) {
nextOp = { ...current, status: action.status, error: action.error };
} else {
const { error: _ignored, ...rest } = current;
nextOp = { ...rest, status: action.status };
}
const next: ChangesetOperation[] = [...state.operations];
next[idx] = nextOp;
return { ...state, operations: next };
}

case ActionType.ChangesetCleared:
if (state.files.length === 0) {
return state;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,31 @@ export interface ChangesetFile {
_meta?: Record<string, unknown>;
}

/**
* Execution lifecycle of a {@link ChangesetOperation}.
*
* An operation is invoked imperatively via `invokeChangesetOperation`, but
* its progress and outcome are reflected back into changeset state so that
* every subscriber observes a consistent view (e.g. a spinner on a "Create
* Pull Request" button, or an inline error after a failed "revert").
*
* @category Changesets
*/
export const enum ChangesetOperationStatus {
/**
* The operation is ready to be invoked. This is the default when
* {@link ChangesetOperation.status} is omitted.
*/
Idle = 'idle',
/** An invocation of this operation is currently in flight. */
Running = 'running',
/**
* The most recent invocation failed. The cause is described by
* {@link ChangesetOperation.error}.
*/
Error = 'error',
}

/**
* Where a {@link ChangesetOperation} can be invoked.
*
Expand Down Expand Up @@ -161,4 +186,21 @@ export interface ChangesetOperation {
confirmation?: StringOrMarkdown;
/** Optional generic icon hint, e.g. `"check"`, `"trash"`. */
icon?: string;
/**
* Current execution status. The server sets
* {@link ChangesetOperationStatus.Running | Running} while an invocation
* is in flight, {@link ChangesetOperationStatus.Error | Error} when the
* most recent invocation failed, and
* {@link ChangesetOperationStatus.Idle | Idle} otherwise.
*
* Clients SHOULD reflect this state in the UI — e.g. disabling the
* control or showing a spinner while `Running`, and surfacing
* {@link error} while `Error`.
*/
status: ChangesetOperationStatus;
/**
* Cause of failure. Present iff
* `status === ChangesetOperationStatus.Error`; otherwise omitted.
*/
error?: ErrorInfo;
}
Original file line number Diff line number Diff line change
Expand Up @@ -66,13 +66,15 @@ export interface AgentInfo {
/**
* Customizations associated with this agent.
*
* Always container customizations —
* Either container customizations —
* {@link PluginCustomization | `PluginCustomization`} entries the agent
* bundles, plus {@link DirectoryCustomization | `DirectoryCustomization`}
* entries it watches in any workspace it's used with. When a session is
* created with this agent, these entries are augmented (e.g. directory
* URIs are resolved against the workspace, children are parsed) and
* propagated into the session's `customizations` list.
* entries it watches in any workspace it's used with — or top-level
* {@link McpServerCustomization | `McpServerCustomization`} entries
* the agent host declares directly. When a session is created with
* this agent, these entries are augmented (e.g. directory URIs are
* resolved against the workspace, children are parsed) and propagated
* into the session's `customizations` list.
*/
customizations?: Customization[];
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@
// DO NOT EDIT -- auto-generated by scripts/sync-agent-host-protocol.ts

import { ActionType } from '../common/actions.js';
import type { StringOrMarkdown, ErrorInfo, FileEdit, UsageInfo } from '../common/state.js';
import { ToolCallConfirmationReason, ToolCallCancellationReason, PendingMessageKind, type Message, type ResponsePart, type ToolCallResult, type ToolResultContent, type ToolDefinition, type SessionActiveClient, type Customization, type SessionInputAnswer, type SessionInputRequest, type SessionInputResponseKind, type ConfirmationOption, type AgentSelection } from './state.js';
import type { StringOrMarkdown, ErrorInfo, FileEdit, UsageInfo, URI } from '../common/state.js';
import { ToolCallConfirmationReason, ToolCallCancellationReason, PendingMessageKind, type Message, type ResponsePart, type ToolCallResult, type ToolResultContent, type ToolDefinition, type SessionActiveClient, type Customization, type McpServerState, type SessionInputAnswer, type SessionInputRequest, type SessionInputResponseKind, type ConfirmationOption, type AgentSelection, type ToolCallContributor } from './state.js';
import type { ModelSelection } from '../channels-root/state.js';
import type { ChangesetSummary } from '../channels-changeset/state.js';

Expand Down Expand Up @@ -116,9 +116,11 @@ export interface SessionResponsePartAction {
/**
* A tool call begins — parameters are streaming from the LM.
*
* For client-provided tools, the server sets `toolClientId` to identify the
* owning client. That client is responsible for executing the tool once it
* reaches the `running` state and dispatching `session/toolCallComplete`.
* The server sets {@link ToolCallContributor | `contributor`} to identify
* the origin of the tool. For client-provided tools, the named client is
* responsible for executing the tool once it reaches the `running` state
* and dispatching `session/toolCallComplete`. For MCP-served tools, the
* server executes the call against the named `McpServerCustomization`.
*
* @category Session Actions
* @version 1
Expand All @@ -130,10 +132,10 @@ export interface SessionToolCallStartAction extends ToolCallActionBase {
/** Human-readable tool name */
displayName: string;
/**
* If this tool is provided by a client, the `clientId` of the owning client.
* Absent for server-side tools.
* Reference to the contributor of the tool being called. Absent for
* server-side tools that are not contributed by a client or MCP server.
*/
toolClientId?: string;
contributor?: ToolCallContributor;
}

/**
Expand Down Expand Up @@ -617,6 +619,45 @@ export interface SessionCustomizationRemovedAction {
id: string;
}

/**
* Updates the runtime fields of an existing
* {@link McpServerCustomization} — narrow alternative to
* {@link SessionCustomizationUpdatedAction} for the high-frequency
* `starting` ↔ `ready` ↔ `authRequired` transitions.
*
* Locates the target entry by `id`, searching both the top-level
* customization list and the `children` array of every container.
* Replaces the entry's {@link McpServerCustomization.state | `state`}
* and {@link McpServerCustomization.channel | `channel`}
* (full-replacement semantics: omit `channel` to clear an existing
* channel URI). Other fields of the customization are preserved.
*
* Is a no-op when no matching `McpServerCustomization` is found. To
* update any other field (name, icons, `mcpApp` capabilities, etc.) use
* {@link SessionCustomizationUpdatedAction} instead.
*
* When the transition is to {@link McpServerStatus.AuthRequired}
* because of a request issued mid-turn, the host SHOULD also raise
* {@link SessionStatus.InputNeeded} on the session — see
* {@link McpServerAuthRequiredState} for the rationale.
*
* @category Session Actions
* @version 1
*/
export interface SessionMcpServerStateChangedAction {
type: ActionType.SessionMcpServerStateChanged;
/** The id of the {@link McpServerCustomization} to update. */
id: string;
/** The new lifecycle state. */
state: McpServerState;
/**
* Updated `mcp://` side-channel URI. Full-replacement: omit to clear
* an existing channel (typical when leaving
* {@link McpServerStatus.Ready | `Ready`}).
*/
channel?: URI;
}

// ─── Config Actions ──────────────────────────────────────────────────────────

/**
Expand Down
Loading
Loading